summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 03:34:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 03:34:56 +0000
commit671f456761fc66649260e831ceee36ec3d4ba9ca (patch)
tree033c56bded071f681e1304311ba0bff449d477cc
parentAdding debian version 3.3a-5. (diff)
downloadtmux-671f456761fc66649260e831ceee36ec3d4ba9ca.tar.xz
tmux-671f456761fc66649260e831ceee36ec3d4ba9ca.zip
Merging upstream version 3.4.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--CHANGES115
-rw-r--r--Makefile.am10
-rw-r--r--Makefile.in266
-rw-r--r--arguments.c343
-rw-r--r--cfg.c47
-rw-r--r--client.c29
-rw-r--r--cmd-attach-session.c3
-rw-r--r--cmd-break-pane.c1
-rw-r--r--cmd-capture-pane.c32
-rw-r--r--cmd-choose-tree.c2
-rw-r--r--cmd-command-prompt.c7
-rw-r--r--cmd-confirm-before.c42
-rw-r--r--cmd-display-menu.c55
-rw-r--r--cmd-display-message.c26
-rw-r--r--cmd-display-panes.c6
-rw-r--r--cmd-find-window.c35
-rw-r--r--cmd-find.c12
-rw-r--r--cmd-join-pane.c37
-rw-r--r--cmd-list-clients.c24
-rw-r--r--cmd-list-keys.c11
-rw-r--r--cmd-load-buffer.c2
-rw-r--r--cmd-new-session.c10
-rw-r--r--cmd-new-window.c7
-rw-r--r--cmd-parse.c24
-rw-r--r--cmd-parse.y20
-rw-r--r--cmd-paste-buffer.c5
-rw-r--r--cmd-pipe-pane.c2
-rw-r--r--cmd-queue.c51
-rw-r--r--cmd-resize-window.c3
-rw-r--r--cmd-run-shell.c14
-rw-r--r--cmd-save-buffer.c13
-rw-r--r--cmd-select-pane.c6
-rw-r--r--cmd-send-keys.c40
-rw-r--r--cmd-set-buffer.c16
-rw-r--r--cmd-source-file.c9
-rw-r--r--cmd-split-window.c79
-rw-r--r--cmd-swap-pane.c8
-rw-r--r--cmd.c12
-rw-r--r--colour.c42
-rw-r--r--compat.h14
-rw-r--r--compat/getpeereid.c5
-rw-r--r--compat/htonll.c30
-rw-r--r--compat/imsg-buffer.c507
-rw-r--r--compat/imsg.c284
-rw-r--r--compat/imsg.h75
-rw-r--r--compat/ntohll.c30
-rw-r--r--compat/systemd.c159
-rwxr-xr-xconfigure317
-rw-r--r--configure.ac69
-rw-r--r--control-notify.c26
-rw-r--r--control.c12
-rw-r--r--environ.c15
-rw-r--r--file.c54
-rw-r--r--format-draw.c54
-rw-r--r--format.c261
-rw-r--r--grid-view.c2
-rw-r--r--grid.c160
-rw-r--r--hyperlinks.c227
-rw-r--r--image-sixel.c600
-rw-r--r--image.c186
-rw-r--r--input-keys.c26
-rw-r--r--input.c329
-rw-r--r--key-bindings.c12
-rw-r--r--key-string.c4
-rw-r--r--layout-custom.c4
-rw-r--r--menu.c177
-rw-r--r--mode-tree.c6
-rw-r--r--notify.c41
-rw-r--r--options-table.c76
-rw-r--r--options.c15
-rw-r--r--paste.c26
-rw-r--r--popup.c9
-rw-r--r--proc.c16
-rw-r--r--regsub.c4
-rw-r--r--screen-redraw.c6
-rw-r--r--screen-write.c496
-rw-r--r--screen.c40
-rw-r--r--server-client.c156
-rw-r--r--server-fn.c77
-rw-r--r--server.c5
-rw-r--r--session.c24
-rw-r--r--spawn.c19
-rw-r--r--status.c72
-rw-r--r--style.c83
-rw-r--r--tmux-protocol.h7
-rw-r--r--tmux.1858
-rw-r--r--tmux.c4
-rw-r--r--tmux.h312
-rw-r--r--tty-acs.c4
-rw-r--r--tty-features.c168
-rw-r--r--tty-keys.c341
-rw-r--r--tty-term.c100
-rw-r--r--tty.c482
-rw-r--r--utf8-combined.c100
-rw-r--r--utf8.c239
-rw-r--r--window-buffer.c7
-rw-r--r--window-client.c2
-rw-r--r--window-copy.c229
-rw-r--r--window-tree.c22
-rw-r--r--window.c101
100 files changed, 7471 insertions, 1781 deletions
diff --git a/CHANGES b/CHANGES
index b72d1ec..9456b63 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,116 @@
+CHANGES FROM 3.3a to 3.4
+
+* Add options keep-last and keep-group to destroy-unattached to keep the last
+ session whether in a group.
+
+* Don't allow paste-buffer into dead panes.
+
+* Add -t to source-file.
+
+* Rewrite combined character handling to be more consistent and to support
+ newer Unicode combined characters.
+
+* Add basic support for SIXEL if built with --enable-sixel.
+
+* Add a session, pane and user mouse range types for the status line and add
+ format variables for mouse_status_line and mouse_status_range so they can be
+ associated with different commands in the key bindings.
+
+* Add flag (-o) to next-prompt/previous-prompt to go to OSC 133 command output.
+
+* Add options and flags for menu styles (menu-style, menu-border-style) similar
+ to those existing for popups.
+
+* Add support for marking lines with a shell prompt based on the OSC 133 extension.
+
+* Check for libterminfo for NetBSD.
+
+* Add "us" to styles for underscore colour.
+
+* Add flags (-c and -y) to change the confirm key and default behaviour of
+ confirm-before.
+
+* Use ncurses' new tparm_s function (added in 6.4-20230424) instead of tparm so
+ it does not object to string arguments in c apabilities it doesn't already
+ know. Also ignore errors from tparm if using previous ncurses versions.
+
+* Set default lock command to vlock on Linux if present at build time.
+
+* Discard mouse sequences that have the right form but actually are invalid.
+
+* Add support for spawning panes in separate cgroups with systemd and a
+ configure flag (--disable-cgroups) to turn off.
+
+* Add a format (pane_unseen_changes) to show if there are unseen changes while
+ in a mode.
+
+* Remove old buffer when renaming rather than complaining.
+
+* Add an L modifier like P, W, S to loop over clients.
+
+* Add -f to list-clients like the other list commands.
+
+* Extend display-message to work for control clients.
+
+* Add a flag to display-menu to select the manu item selected when the menu is
+ open.
+
+* Have tmux recognise pasted text wrapped in bracket paste sequences, rather
+ than only forwarding them to the program inside.
+
+* Have client return 1 if process is interrupted to an input pane.
+
+* Query the client terminal for foreground and background colours and if OSC 10
+ or 11 is received but no colour has been set inside tmux, return the colour
+ from the first attached client.
+
+* Add send-keys -K to handle keys directly as if typed (so look up in key
+ table).
+
+* Process escape sequences in show-buffer.
+
+* Add a -l flag to display-message to disable format expansion.
+
+* Add paste-buffer-deleted notification and fix name of paste-buffer-changed.
+
+* Do not attempt to connect to the socket as a client if systemd is active.
+
+* Add scroll-top and scroll-bottom commands to scroll so cursor is at top or
+ bottom.
+
+* Add a -T flag to capture-pane to stop at the last used cell instead of the
+ full width. Restore the previous behaviour by making it default to off unless
+ -J is used.
+
+* Add message-line option to control where message and prompt go.
+
+* Notification when a when a paste buffer is deleted.
+
+* Add a Nobr terminfo(5) capability to tell tmux the terminal does not use bright
+ colours for bold.
+
+* Change g and G to go to top and bottom in menus.
+
+* Add a third state "all" to allow-passthrough to work even in invisible panes.
+
+* Add support for OSC 8 hyperlinks.
+
+* Store the time lines are scrolled into history and display in copy mode.
+
+* Add a %config-error reply to control mode for configuration file errors since
+ reporting them in view mode is useless.
+
+* A new feature flag (ignorefkeys) to ignore terminfo(5) function key
+ definitions for rxvt.
+
+* Pass through first argument to OSC 52 (which clipboards to set) if the
+ application provides it.
+
+* Expand arguments to send-keys, capture-pane, split-window, join-pane where it
+ makes sense to do so.
+
+* Ignore named buffers when choosing a buffer if one is not specified by the user.
+
CHANGES FROM 3.3 TO 3.3a
* Do not crash when run-shell produces output from a config file.
@@ -1302,7 +1415,7 @@ Incompatible Changes
bind -Tcopy-mode C-r command-prompt -i -p'search up' "send -X search-backward-incremental '%%'"
- 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
diff --git a/Makefile.am b/Makefile.am
index fb94e82..8e5f72b 100644
--- a/Makefile.am
+++ b/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)
diff --git a/Makefile.in b/Makefile.in
index 979172b..b2854d9 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -14,8 +14,6 @@
@SET_MAKE@
-# Makefile.am
-
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
@@ -133,6 +131,9 @@ bin_PROGRAMS = tmux$(EXEEXT)
# Add compat file for utf8proc.
@HAVE_UTF8PROC_TRUE@am__append_14 = compat/utf8proc.c
+
+# Enable sixel support.
+@ENABLE_SIXEL_TRUE@am__append_15 = image.c image-sixel.c
@NEED_FUZZING_TRUE@check_PROGRAMS = fuzz/input-fuzzer$(EXEEXT)
subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@@ -155,6 +156,42 @@ fuzz_input_fuzzer_OBJECTS = fuzz/input-fuzzer.$(OBJEXT)
@NEED_FUZZING_TRUE@fuzz_input_fuzzer_DEPENDENCIES = $(LDADD)
fuzz_input_fuzzer_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(fuzz_input_fuzzer_LDFLAGS) $(LDFLAGS) -o $@
+am__dist_tmux_SOURCES_DIST = alerts.c arguments.c attributes.c cfg.c \
+ client.c cmd-attach-session.c cmd-bind-key.c cmd-break-pane.c \
+ cmd-capture-pane.c cmd-choose-tree.c cmd-command-prompt.c \
+ cmd-confirm-before.c cmd-copy-mode.c cmd-detach-client.c \
+ cmd-display-menu.c cmd-display-message.c cmd-display-panes.c \
+ cmd-find-window.c cmd-find.c cmd-if-shell.c cmd-join-pane.c \
+ cmd-kill-pane.c cmd-kill-server.c cmd-kill-session.c \
+ cmd-kill-window.c cmd-list-buffers.c cmd-list-clients.c \
+ cmd-list-keys.c cmd-list-panes.c cmd-list-sessions.c \
+ cmd-list-windows.c cmd-load-buffer.c cmd-lock-server.c \
+ cmd-move-window.c cmd-new-session.c cmd-new-window.c \
+ cmd-parse.y cmd-paste-buffer.c cmd-pipe-pane.c cmd-queue.c \
+ cmd-refresh-client.c cmd-rename-session.c cmd-rename-window.c \
+ cmd-resize-pane.c cmd-resize-window.c cmd-respawn-pane.c \
+ cmd-respawn-window.c cmd-rotate-window.c cmd-run-shell.c \
+ cmd-save-buffer.c cmd-select-layout.c cmd-select-pane.c \
+ cmd-select-window.c cmd-send-keys.c cmd-server-access.c \
+ cmd-set-buffer.c cmd-set-environment.c cmd-set-option.c \
+ cmd-show-environment.c cmd-show-messages.c cmd-show-options.c \
+ cmd-show-prompt-history.c cmd-source-file.c cmd-split-window.c \
+ cmd-swap-pane.c cmd-swap-window.c cmd-switch-client.c \
+ cmd-unbind-key.c cmd-wait-for.c cmd.c colour.c compat.h \
+ control-notify.c control.c environ.c file.c format.c \
+ format-draw.c grid-reader.c grid-view.c grid.c hyperlinks.c \
+ input-keys.c input.c job.c key-bindings.c key-string.c \
+ layout-custom.c layout-set.c layout.c log.c menu.c mode-tree.c \
+ names.c notify.c options-table.c options.c paste.c popup.c \
+ proc.c regsub.c resize.c screen-redraw.c screen-write.c \
+ screen.c server-acl.c server-client.c server-fn.c server.c \
+ session.c spawn.c status.c style.c tmux.c tmux.h \
+ tmux-protocol.h tty-acs.c tty-features.c tty-keys.c tty-term.c \
+ tty.c utf8-combined.c utf8.c window-buffer.c window-client.c \
+ window-clock.c window-copy.c window-customize.c window-tree.c \
+ window.c xmalloc.c xmalloc.h image.c image-sixel.c
+@ENABLE_SIXEL_TRUE@am__objects_1 = image.$(OBJEXT) \
+@ENABLE_SIXEL_TRUE@ image-sixel.$(OBJEXT)
dist_tmux_OBJECTS = alerts.$(OBJEXT) arguments.$(OBJEXT) \
attributes.$(OBJEXT) cfg.$(OBJEXT) client.$(OBJEXT) \
cmd-attach-session.$(OBJEXT) cmd-bind-key.$(OBJEXT) \
@@ -192,29 +229,31 @@ dist_tmux_OBJECTS = alerts.$(OBJEXT) arguments.$(OBJEXT) \
colour.$(OBJEXT) control-notify.$(OBJEXT) control.$(OBJEXT) \
environ.$(OBJEXT) file.$(OBJEXT) format.$(OBJEXT) \
format-draw.$(OBJEXT) grid-reader.$(OBJEXT) \
- grid-view.$(OBJEXT) grid.$(OBJEXT) input-keys.$(OBJEXT) \
- input.$(OBJEXT) job.$(OBJEXT) key-bindings.$(OBJEXT) \
- key-string.$(OBJEXT) layout-custom.$(OBJEXT) \
- layout-set.$(OBJEXT) layout.$(OBJEXT) log.$(OBJEXT) \
- menu.$(OBJEXT) mode-tree.$(OBJEXT) names.$(OBJEXT) \
- notify.$(OBJEXT) options-table.$(OBJEXT) options.$(OBJEXT) \
- paste.$(OBJEXT) popup.$(OBJEXT) proc.$(OBJEXT) \
- regsub.$(OBJEXT) resize.$(OBJEXT) screen-redraw.$(OBJEXT) \
- screen-write.$(OBJEXT) screen.$(OBJEXT) server-acl.$(OBJEXT) \
- server-client.$(OBJEXT) server-fn.$(OBJEXT) server.$(OBJEXT) \
- session.$(OBJEXT) spawn.$(OBJEXT) status.$(OBJEXT) \
- style.$(OBJEXT) tmux.$(OBJEXT) tty-acs.$(OBJEXT) \
- tty-features.$(OBJEXT) tty-keys.$(OBJEXT) tty-term.$(OBJEXT) \
- tty.$(OBJEXT) utf8.$(OBJEXT) window-buffer.$(OBJEXT) \
+ grid-view.$(OBJEXT) grid.$(OBJEXT) hyperlinks.$(OBJEXT) \
+ input-keys.$(OBJEXT) input.$(OBJEXT) job.$(OBJEXT) \
+ key-bindings.$(OBJEXT) key-string.$(OBJEXT) \
+ layout-custom.$(OBJEXT) layout-set.$(OBJEXT) layout.$(OBJEXT) \
+ log.$(OBJEXT) menu.$(OBJEXT) mode-tree.$(OBJEXT) \
+ names.$(OBJEXT) notify.$(OBJEXT) options-table.$(OBJEXT) \
+ options.$(OBJEXT) paste.$(OBJEXT) popup.$(OBJEXT) \
+ proc.$(OBJEXT) regsub.$(OBJEXT) resize.$(OBJEXT) \
+ screen-redraw.$(OBJEXT) screen-write.$(OBJEXT) \
+ screen.$(OBJEXT) server-acl.$(OBJEXT) server-client.$(OBJEXT) \
+ server-fn.$(OBJEXT) server.$(OBJEXT) session.$(OBJEXT) \
+ spawn.$(OBJEXT) status.$(OBJEXT) style.$(OBJEXT) \
+ tmux.$(OBJEXT) tty-acs.$(OBJEXT) tty-features.$(OBJEXT) \
+ tty-keys.$(OBJEXT) tty-term.$(OBJEXT) tty.$(OBJEXT) \
+ utf8-combined.$(OBJEXT) utf8.$(OBJEXT) window-buffer.$(OBJEXT) \
window-client.$(OBJEXT) window-clock.$(OBJEXT) \
window-copy.$(OBJEXT) window-customize.$(OBJEXT) \
- window-tree.$(OBJEXT) window.$(OBJEXT) xmalloc.$(OBJEXT)
-@NEED_FORKPTY_TRUE@am__objects_1 = \
+ window-tree.$(OBJEXT) window.$(OBJEXT) xmalloc.$(OBJEXT) \
+ $(am__objects_1)
+@NEED_FORKPTY_TRUE@am__objects_2 = \
@NEED_FORKPTY_TRUE@ compat/forkpty-@PLATFORM@.$(OBJEXT)
-@HAVE_SYSTEMD_TRUE@am__objects_2 = compat/systemd.$(OBJEXT)
-@HAVE_UTF8PROC_TRUE@am__objects_3 = compat/utf8proc.$(OBJEXT)
-nodist_tmux_OBJECTS = osdep-@PLATFORM@.$(OBJEXT) $(am__objects_1) \
- $(am__objects_2) $(am__objects_3)
+@HAVE_SYSTEMD_TRUE@am__objects_3 = compat/systemd.$(OBJEXT)
+@HAVE_UTF8PROC_TRUE@am__objects_4 = compat/utf8proc.$(OBJEXT)
+nodist_tmux_OBJECTS = osdep-@PLATFORM@.$(OBJEXT) $(am__objects_2) \
+ $(am__objects_3) $(am__objects_4)
tmux_OBJECTS = $(dist_tmux_OBJECTS) $(nodist_tmux_OBJECTS)
tmux_LDADD = $(LDADD)
tmux_DEPENDENCIES = $(LIBOBJS)
@@ -256,7 +295,7 @@ am__v_YACC_1 =
YLWRAP = $(top_srcdir)/etc/ylwrap
SOURCES = fuzz/input-fuzzer.c $(dist_tmux_SOURCES) \
$(nodist_tmux_SOURCES) $(dist_EXTRA_tmux_SOURCES)
-DIST_SOURCES = fuzz/input-fuzzer.c $(dist_tmux_SOURCES) \
+DIST_SOURCES = fuzz/input-fuzzer.c $(am__dist_tmux_SOURCES_DIST) \
$(dist_EXTRA_tmux_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
@@ -299,8 +338,9 @@ am__DIST_COMMON = $(srcdir)/Makefile.in \
$(top_srcdir)/compat/getline.c $(top_srcdir)/compat/getopt.c \
$(top_srcdir)/compat/getpeereid.c \
$(top_srcdir)/compat/getprogname.c \
+ $(top_srcdir)/compat/htonll.c \
$(top_srcdir)/compat/imsg-buffer.c $(top_srcdir)/compat/imsg.c \
- $(top_srcdir)/compat/memmem.c \
+ $(top_srcdir)/compat/memmem.c $(top_srcdir)/compat/ntohll.c \
$(top_srcdir)/compat/reallocarray.c \
$(top_srcdir)/compat/recallocarray.c \
$(top_srcdir)/compat/setenv.c \
@@ -342,6 +382,7 @@ AM_CFLAGS = @AM_CFLAGS@ $(am__append_1) $(am__append_2) \
AM_CPPFLAGS = @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@"' $(am__append_4) $(am__append_5) \
$(am__append_6) $(am__append_7) $(am__append_9) \
$(am__append_10) $(am__append_11)
@@ -357,6 +398,7 @@ CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DEFAULT_LOCK_CMD = @DEFAULT_LOCK_CMD@
DEFAULT_TERM = @DEFAULT_TERM@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
@@ -385,6 +427,8 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTINFO_CFLAGS = @LIBTINFO_CFLAGS@
LIBTINFO_LIBS = @LIBTINFO_LIBS@
+LIBUTF8PROC_CFLAGS = @LIBUTF8PROC_CFLAGS@
+LIBUTF8PROC_LIBS = @LIBUTF8PROC_LIBS@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MANFORMAT = @MANFORMAT@
@@ -433,6 +477,8 @@ datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
+found_vlock = @found_vlock@
+found_yacc = @found_yacc@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
@@ -475,138 +521,40 @@ dist_EXTRA_tmux_SOURCES = compat/*.[ch]
LDADD = $(LIBOBJS)
# List of sources.
-dist_tmux_SOURCES = \
- alerts.c \
- arguments.c \
- attributes.c \
- cfg.c \
- client.c \
- cmd-attach-session.c \
- cmd-bind-key.c \
- cmd-break-pane.c \
- cmd-capture-pane.c \
- cmd-choose-tree.c \
- cmd-command-prompt.c \
- cmd-confirm-before.c \
- cmd-copy-mode.c \
- cmd-detach-client.c \
- cmd-display-menu.c \
- cmd-display-message.c \
- cmd-display-panes.c \
- cmd-find-window.c \
- cmd-find.c \
- cmd-if-shell.c \
- cmd-join-pane.c \
- cmd-kill-pane.c \
- cmd-kill-server.c \
- cmd-kill-session.c \
- cmd-kill-window.c \
- cmd-list-buffers.c \
- cmd-list-clients.c \
- cmd-list-keys.c \
- cmd-list-panes.c \
- cmd-list-sessions.c \
- cmd-list-windows.c \
- cmd-load-buffer.c \
- cmd-lock-server.c \
- cmd-move-window.c \
- cmd-new-session.c \
- cmd-new-window.c \
- cmd-parse.y \
- cmd-paste-buffer.c \
- cmd-pipe-pane.c \
- cmd-queue.c \
- cmd-refresh-client.c \
- cmd-rename-session.c \
- cmd-rename-window.c \
- cmd-resize-pane.c \
- cmd-resize-window.c \
- cmd-respawn-pane.c \
- cmd-respawn-window.c \
- cmd-rotate-window.c \
- cmd-run-shell.c \
- cmd-save-buffer.c \
- cmd-select-layout.c \
- cmd-select-pane.c \
- cmd-select-window.c \
- cmd-send-keys.c \
- cmd-server-access.c \
- cmd-set-buffer.c \
- cmd-set-environment.c \
- cmd-set-option.c \
- cmd-show-environment.c \
- cmd-show-messages.c \
- cmd-show-options.c \
- cmd-show-prompt-history.c \
- cmd-source-file.c \
- cmd-split-window.c \
- cmd-swap-pane.c \
- cmd-swap-window.c \
- cmd-switch-client.c \
- cmd-unbind-key.c \
- cmd-wait-for.c \
- cmd.c \
- colour.c \
- compat.h \
- control-notify.c \
- control.c \
- environ.c \
- file.c \
- format.c \
- format-draw.c \
- grid-reader.c \
- grid-view.c \
- grid.c \
- input-keys.c \
- input.c \
- job.c \
- key-bindings.c \
- key-string.c \
- layout-custom.c \
- layout-set.c \
- layout.c \
- log.c \
- menu.c \
- mode-tree.c \
- names.c \
- notify.c \
- options-table.c \
- options.c \
- paste.c \
- popup.c \
- proc.c \
- regsub.c \
- resize.c \
- screen-redraw.c \
- screen-write.c \
- screen.c \
- server-acl.c \
- server-client.c \
- server-fn.c \
- server.c \
- session.c \
- spawn.c \
- status.c \
- style.c \
- tmux.c \
- tmux.h \
- tmux-protocol.h \
- tty-acs.c \
- tty-features.c \
- tty-keys.c \
- tty-term.c \
- tty.c \
- utf8.c \
- window-buffer.c \
- window-client.c \
- window-clock.c \
- window-copy.c \
- window-customize.c \
- window-tree.c \
- window.c \
- xmalloc.c \
- xmalloc.h
-
+dist_tmux_SOURCES = alerts.c arguments.c attributes.c cfg.c client.c \
+ cmd-attach-session.c cmd-bind-key.c cmd-break-pane.c \
+ cmd-capture-pane.c cmd-choose-tree.c cmd-command-prompt.c \
+ cmd-confirm-before.c cmd-copy-mode.c cmd-detach-client.c \
+ cmd-display-menu.c cmd-display-message.c cmd-display-panes.c \
+ cmd-find-window.c cmd-find.c cmd-if-shell.c cmd-join-pane.c \
+ cmd-kill-pane.c cmd-kill-server.c cmd-kill-session.c \
+ cmd-kill-window.c cmd-list-buffers.c cmd-list-clients.c \
+ cmd-list-keys.c cmd-list-panes.c cmd-list-sessions.c \
+ cmd-list-windows.c cmd-load-buffer.c cmd-lock-server.c \
+ cmd-move-window.c cmd-new-session.c cmd-new-window.c \
+ cmd-parse.y cmd-paste-buffer.c cmd-pipe-pane.c cmd-queue.c \
+ cmd-refresh-client.c cmd-rename-session.c cmd-rename-window.c \
+ cmd-resize-pane.c cmd-resize-window.c cmd-respawn-pane.c \
+ cmd-respawn-window.c cmd-rotate-window.c cmd-run-shell.c \
+ cmd-save-buffer.c cmd-select-layout.c cmd-select-pane.c \
+ cmd-select-window.c cmd-send-keys.c cmd-server-access.c \
+ cmd-set-buffer.c cmd-set-environment.c cmd-set-option.c \
+ cmd-show-environment.c cmd-show-messages.c cmd-show-options.c \
+ cmd-show-prompt-history.c cmd-source-file.c cmd-split-window.c \
+ cmd-swap-pane.c cmd-swap-window.c cmd-switch-client.c \
+ cmd-unbind-key.c cmd-wait-for.c cmd.c colour.c compat.h \
+ control-notify.c control.c environ.c file.c format.c \
+ format-draw.c grid-reader.c grid-view.c grid.c hyperlinks.c \
+ input-keys.c input.c job.c key-bindings.c key-string.c \
+ layout-custom.c layout-set.c layout.c log.c menu.c mode-tree.c \
+ names.c notify.c options-table.c options.c paste.c popup.c \
+ proc.c regsub.c resize.c screen-redraw.c screen-write.c \
+ screen.c server-acl.c server-client.c server-fn.c server.c \
+ session.c spawn.c status.c style.c tmux.c tmux.h \
+ tmux-protocol.h tty-acs.c tty-features.c tty-keys.c tty-term.c \
+ tty.c utf8-combined.c utf8.c window-buffer.c window-client.c \
+ window-clock.c window-copy.c window-customize.c window-tree.c \
+ window.c xmalloc.c xmalloc.h $(am__append_15)
nodist_tmux_SOURCES = osdep-@PLATFORM@.c $(am__append_12) \
$(am__append_13) $(am__append_14)
@NEED_FUZZING_TRUE@fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)
@@ -810,6 +758,9 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grid-reader.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grid-view.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hyperlinks.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/image-sixel.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/image.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input-keys.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/job.Po@am__quote@
@@ -848,6 +799,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty-keys.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty-term.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utf8-combined.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utf8.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window-buffer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window-client.Po@am__quote@
@@ -876,9 +828,11 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/getopt.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/getpeereid.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/getprogname.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/htonll.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/imsg-buffer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/imsg.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/memmem.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/ntohll.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/reallocarray.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/recallocarray.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/setenv.Po@am__quote@
diff --git a/arguments.c b/arguments.c
index d0dc2d4..669375e 100644
--- a/arguments.c
+++ b/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,99 @@ 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);
+ return (-1);
+ }
+ }
+ if (argument == NULL) {
+ if (optional_argument) {
+ log_debug("%s: -%c (optional)", __func__, flag);
+ args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
+ return (0); /* either - or end */
+ }
+ xasprintf(cause, "-%c expects an argument", flag);
+ return (-1);
+ }
+ args_copy_value(new, argument);
+ (*i)++;
+
+out:
+ s = args_value_as_string(new);
+ log_debug("%s: -%c = %s", __func__, flag, s);
+ args_set(args, flag, new, 0);
+ return (0);
+}
+
+/* Parse flags argument. */
+static int
+args_parse_flags(const struct args_parse *parse, struct args_value *values,
+ u_int count, char **cause, struct args *args, u_int *i)
+{
+ struct args_value *value;
+ u_char flag;
+ const char *found, *string;
+ int optional_argument;
+
+ value = &values[*i];
+ if (value->type != ARGS_STRING)
+ return (1);
+
+ string = value->string;
+ log_debug("%s: next %s", __func__, string);
+ if (*string++ != '-' || *string == '\0')
+ return (1);
+ (*i)++;
+ if (string[0] == '-' && string[1] == '\0')
+ return (1);
+
+ for (;;) {
+ flag = *string++;
+ if (flag == '\0')
+ return (0);
+ if (flag == '?')
+ return (-1);
+ if (!isalnum(flag)) {
+ xasprintf(cause, "invalid flag -%c", flag);
+ return (-1);
+ }
+
+ found = strchr(parse->template, flag);
+ if (found == NULL) {
+ xasprintf(cause, "unknown flag -%c", flag);
+ return (-1);
+ }
+ if (found[1] != ':') {
+ log_debug("%s: -%c", __func__, flag);
+ args_set(args, flag, NULL, 0);
+ continue;
+ }
+ optional_argument = (found[2] == ':');
+ return (args_parse_flag_argument(values, count, cause, args, i,
+ string, flag, optional_argument));
+ }
+}
+
/* Parse arguments into a new argument set. */
struct args *
args_parse(const struct args_parse *parse, struct args_value *values,
@@ -131,86 +244,21 @@ args_parse(const struct args_parse *parse, struct args_value *values,
u_int i;
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 +266,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 +371,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 +535,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 +543,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 +556,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 +573,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 +646,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,6 +655,7 @@ 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
@@ -696,6 +761,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 +788,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 +815,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 +845,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 +919,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 +966,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 +984,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 +1018,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);
+}
diff --git a/cfg.c b/cfg.c
index e92faa7..adac60e 100644
--- a/cfg.c
+++ b/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;
diff --git a/client.c b/client.c
index df6cee9..9cd5ad8 100644
--- a/client.c
+++ b/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) {
@@ -527,11 +530,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 +708,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,
diff --git a/cmd-attach-session.c b/cmd-attach-session.c
index b92a7f2..4e2d15d 100644
--- a/cmd-attach-session.c
+++ b/cmd-attach-session.c
@@ -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);
}
diff --git a/cmd-break-pane.c b/cmd-break-pane.c
index 4f38d4b..9c4b150 100644
--- a/cmd-break-pane.c
+++ b/cmd-break-pane.c
@@ -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");
diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c
index 964f831..8f7250e 100644
--- a/cmd-capture-pane.c
+++ b/cmd-capture-pane.c
@@ -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);
}
diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c
index 7aa1d21..f2f4b2e 100644
--- a/cmd-choose-tree.c
+++ b/cmd-choose-tree.c
@@ -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) {
diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c
index 4455856..6010d0f 100644
--- a/cmd-command-prompt.c
+++ b/cmd-command-prompt.c
@@ -179,10 +179,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 +193,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);
}
diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c
index ce8c95e..485e6e6 100644
--- a/cmd-confirm-before.c
+++ b/cmd-confirm-before.c
@@ -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,7 +71,7 @@ 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);
@@ -79,11 +82,26 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
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");
+ 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 +125,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 +141,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);
}
diff --git a/cmd-display-menu.c b/cmd-display-menu.c
index e6a503b..d04a17c 100644
--- a/cmd-display-menu.c
+++ b/cmd-display-menu.c
@@ -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:Ot:T:x:y:", 1, -1, cmd_display_menu_args_parse },
+ .usage = "[-O] [-b border-lines] [-c target-client] "
+ "[-C starting-choice] [-H selected-style] [-s style] "
+ "[-S border-style] " CMD_TARGET_PANE_USAGE "[-T title] "
"[-x position] [-y position] name key command ...",
.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)
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);
diff --git a/cmd-display-message.c b/cmd-display-message.c
index 7828f69..512509f 100644
--- a/cmd-display-message.c
+++ b/cmd-display-message.c
@@ -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);
diff --git a/cmd-display-panes.c b/cmd-display-panes.c
index 5773a2d..06f6dc2 100644
--- a/cmd-display-panes.c
+++ b/cmd-display-panes.c
@@ -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);
diff --git a/cmd-find-window.c b/cmd-find-window.c
index 6e07537..5609983 100644
--- a/cmd-find-window.c
+++ b/cmd-find-window.c
@@ -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);
diff --git a/cmd-find.c b/cmd-find.c
index e98090d..8a3499a 100644
--- a/cmd-find.c
+++ b/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
diff --git a/cmd-join-pane.c b/cmd-join-pane.c
index cb3fb34..da1ba9a 100644
--- a/cmd-join-pane.c
+++ b/cmd-join-pane.c
@@ -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,24 +98,31 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT;
- size = -1;
- if (args_has(args, 'l')) {
- if (type == LAYOUT_TOPBOTTOM) {
- size = args_percentage(args, 'l', 0, INT_MAX,
- dst_wp->sy, &cause);
+ /* 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 {
- size = args_percentage(args, 'l', 0, INT_MAX,
- dst_wp->sx, &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;
+ curval = dst_wp->sy;
else
- size = (dst_wp->sx * percentage) / 100;
+ curval = dst_wp->sx;
}
}
+
+ size = -1;
+ if (args_has(args, 'l')) {
+ size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
+ item, &cause);
+ } else if (args_has(args, 'p')) {
+ size = args_strtonum_and_expand(args, 'l', 0, 100, item,
+ &cause);
+ if (cause == NULL)
+ size = curval * size / 100;
+ }
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(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();
diff --git a/cmd-list-clients.c b/cmd-list-clients.c
index 53a9917..da7541b 100644
--- a/cmd-list-clients.c
+++ b/cmd-list-clients.c
@@ -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);
diff --git a/cmd-list-keys.c b/cmd-list-keys.c
index ae9f995..395b147 100644
--- a/cmd-list-keys.c
+++ b/cmd-list-keys.c
@@ -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);
diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c
index 59810de..70fd7ed 100644
--- a/cmd-load-buffer.c
+++ b/cmd-load-buffer.c
@@ -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);
}
diff --git a/cmd-new-session.c b/cmd-new-session.c
index cb9abfb..c90369b 100644
--- a/cmd-new-session.c
+++ b/cmd-new-session.c
@@ -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);
diff --git a/cmd-new-window.c b/cmd-new-window.c
index e7f0868..f2d932d 100644
--- a/cmd-new-window.c
+++ b/cmd-new-window.c
@@ -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);
diff --git a/cmd-parse.c b/cmd-parse.c
index 309c6fe..4be4890 100644
--- a/cmd-parse.c
+++ b/cmd-parse.c
@@ -845,7 +845,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;
@@ -1373,13 +1374,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
@@ -1462,7 +1474,7 @@ error:
free(buf);
return (NULL);
}
-#line 1458 "cmd-parse.c"
+#line 1470 "cmd-parse.c"
/* allocate initial stack or double stack size, up to YYMAXDEPTH */
static int yygrowstack(void)
{
@@ -2156,7 +2168,7 @@ case 46:
free(yyvsp[-1].commands);
}
break;
-#line 2152 "cmd-parse.c"
+#line 2164 "cmd-parse.c"
}
yyssp -= yym;
yystate = *yyssp;
diff --git a/cmd-parse.y b/cmd-parse.y
index 1d69277..65ffad8 100644
--- a/cmd-parse.y
+++ b/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
diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c
index 36326e1..269e92f 100644
--- a/cmd-paste-buffer.c
+++ b/cmd-paste-buffer.c
@@ -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');
diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c
index 0fa656c..268b51b 100644
--- a/cmd-pipe-pane.c
+++ b/cmd-pipe-pane.c
@@ -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);
}
diff --git a/cmd-queue.c b/cmd-queue.c
index 8325e2e..e188afc 100644
--- a/cmd-queue.c
+++ b/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));
}
@@ -823,43 +825,28 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int 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. */
diff --git a/cmd-resize-window.c b/cmd-resize-window.c
index ad73916..c420451 100644
--- a/cmd-resize-window.c
+++ b/cmd-resize-window.c
@@ -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;
diff --git a/cmd-run-shell.c b/cmd-run-shell.c
index 560efac..ddb5b1b 100644
--- a/cmd-run-shell.c
+++ b/cmd-run-shell.c
@@ -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 },
@@ -103,6 +104,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 +139,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 +147,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)
diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c
index 7d67837..3e81500 100644
--- a/cmd-save-buffer.c
+++ b/cmd-save-buffer.c
@@ -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("-");
diff --git a/cmd-select-pane.c b/cmd-select-pane.c
index ae21d4c..135729f 100644
--- a/cmd-select-pane.c
+++ b/cmd-select-pane.c
@@ -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)
diff --git a/cmd-send-keys.c b/cmd-send-keys.c
index d6a9543..ac99a6f 100644
--- a/cmd-send-keys.c
+++ b/cmd-send-keys.c
@@ -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);
}
diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c
index 9112683..35e7295 100644
--- a/cmd-set-buffer.c
+++ b/cmd-set-buffer.c
@@ -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);
}
diff --git a/cmd-source-file.c b/cmd-source-file.c
index 255d443..b390ed6 100644
--- a/cmd-source-file.c
+++ b/cmd-source-file.c
@@ -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;
diff --git a/cmd-split-window.c b/cmd-split-window.c
index 9947dfd..637bad3 100644
--- a/cmd-split-window.c
+++ b/cmd-split-window.c
@@ -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, 'l', 0, 100, item,
+ &cause);
+ if (cause == NULL)
+ size = curval * size / 100;
+ }
+ if (cause != NULL) {
+ cmdq_error(item, "size %s", cause);
+ free(cause);
+ return (CMD_RETURN_ERROR);
+ }
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
input = (args_has(args, 'I') && count == 0);
diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c
index 4191b89..6931bd1 100644
--- a/cmd-swap-pane.c
+++ b/cmd-swap-pane.c
@@ -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);
diff --git a/cmd.c b/cmd.c
index ed4f994..32c00b4 100644
--- a/cmd.c
+++ b/cmd.c
@@ -812,10 +812,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;
diff --git a/colour.c b/colour.c
index a282d18..9bde646 100644
--- a/colour.c
+++ b/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);
}
-
}
diff --git a/compat.h b/compat.h
index 6eb9761..720cd90 100644
--- a/compat.h
+++ b/compat.h
@@ -334,6 +334,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 +435,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
diff --git a/compat/getpeereid.c b/compat/getpeereid.c
index c194e88..b79f420 100644
--- a/compat/getpeereid.c
+++ b/compat/getpeereid.c
@@ -18,6 +18,7 @@
#include <sys/socket.h>
#include <stdio.h>
+#include <unistd.h>
#ifdef HAVE_UCRED_H
#include <ucred.h>
@@ -49,6 +50,8 @@ getpeereid(int s, uid_t *uid, gid_t *gid)
ucred_free(ucred);
return (0);
#else
- return (getuid());
+ *uid = geteuid();
+ *gid = getegid();
+ return (0);
#endif
}
diff --git a/compat/htonll.c b/compat/htonll.c
new file mode 100644
index 0000000..aef6598
--- /dev/null
+++ b/compat/htonll.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2024 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include "compat.h"
+
+uint64_t
+htonll(uint64_t v)
+{
+ uint32_t b;
+ uint32_t t;
+
+ b = htonl (v & 0xffffffff);
+ t = htonl (v >> 32);
+ return ((uint64_t)b << 32 | t);
+}
diff --git a/compat/imsg-buffer.c b/compat/imsg-buffer.c
index 67d4c70..9aed0ed 100644
--- a/compat/imsg-buffer.c
+++ b/compat/imsg-buffer.c
@@ -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++;
}
@@ -178,15 +582,6 @@ again:
}
void
-ibuf_free(struct ibuf *buf)
-{
- if (buf == NULL)
- return;
- freezero(buf->buf, buf->size);
- free(buf);
-}
-
-void
msgbuf_init(struct msgbuf *msgbuf)
{
msgbuf->queued = 0;
@@ -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);
}
diff --git a/compat/imsg.c b/compat/imsg.c
index 54ac7e5..c65cd30 100644
--- a/compat/imsg.c
+++ b/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;
+
+ 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);
+ }
- memcpy(imsg->data, ibuf->r.rptr, datalen);
+ if (m.hdr.flags & IMSGF_HASFD)
+ m.fd = imsg_dequeue_fd(imsgbuf);
- 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.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;
+ ibuf_fd_set(wbuf, fd);
+ imsg_close(imsgbuf, wbuf);
- imsg_close(ibuf, wbuf);
+ return (1);
+}
+
+/*
+ * 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);
}
-/* ARGSUSED */
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);
}
diff --git a/compat/imsg.h b/compat/imsg.h
index 5b092cf..dd47b18 100644
--- a/compat/imsg.h
+++ b/compat/imsg.h
@@ -1,6 +1,7 @@
-/* $OpenBSD: imsg.h,v 1.5 2019/01/20 02:50:03 bcook Exp $ */
+/* $OpenBSD: imsg.h,v 1.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 *);
diff --git a/compat/ntohll.c b/compat/ntohll.c
new file mode 100644
index 0000000..c2fe1bb
--- /dev/null
+++ b/compat/ntohll.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2024 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include "compat.h"
+
+uint64_t
+ntohll(uint64_t v)
+{
+ uint32_t b;
+ uint32_t t;
+
+ b = ntohl (v & 0xffffffff);
+ t = ntohl (v >> 32);
+ return ((uint64_t)b << 32 | t);
+}
diff --git a/compat/systemd.c b/compat/systemd.c
index 7317e43..6f790a3 100644
--- a/compat/systemd.c
+++ b/compat/systemd.c
@@ -19,17 +19,28 @@
#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 <string.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 +67,149 @@ 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;
+ }
+
+ /*
+ * 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);
+}
diff --git a/configure b/configure
index 5e32bf3..41259cd 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for tmux 3.3a.
+# Generated by GNU Autoconf 2.69 for tmux 3.4.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -577,8 +577,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='tmux'
PACKAGE_TARNAME='tmux'
-PACKAGE_VERSION='3.3a'
-PACKAGE_STRING='tmux 3.3a'
+PACKAGE_VERSION='3.4'
+PACKAGE_STRING='tmux 3.4'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -625,6 +625,8 @@ LTLIBOBJS
AM_LDFLAGS
AM_CFLAGS
AM_CPPFLAGS
+DEFAULT_LOCK_CMD
+found_vlock
IS_UNKNOWN_FALSE
IS_UNKNOWN_TRUE
IS_HAIKU_FALSE
@@ -653,18 +655,23 @@ DEFAULT_TERM
NEED_FORKPTY_FALSE
NEED_FORKPTY_TRUE
XOPEN_DEFINES
+ENABLE_SIXEL_FALSE
+ENABLE_SIXEL_TRUE
HAVE_SYSTEMD_FALSE
HAVE_SYSTEMD_TRUE
SYSTEMD_LIBS
SYSTEMD_CFLAGS
HAVE_UTF8PROC_FALSE
HAVE_UTF8PROC_TRUE
+LIBUTF8PROC_LIBS
+LIBUTF8PROC_CFLAGS
LIBNCURSESW_LIBS
LIBNCURSESW_CFLAGS
LIBNCURSES_LIBS
LIBNCURSES_CFLAGS
LIBTINFO_LIBS
LIBTINFO_CFLAGS
+found_yacc
LIBEVENT_LIBS
LIBEVENT_CFLAGS
LIBEVENT_CORE_LIBS
@@ -790,6 +797,8 @@ with_TERM
enable_utempter
enable_utf8proc
enable_systemd
+enable_cgroups
+enable_sixel
'
ac_precious_vars='build_alias
host_alias
@@ -816,6 +825,8 @@ LIBNCURSES_CFLAGS
LIBNCURSES_LIBS
LIBNCURSESW_CFLAGS
LIBNCURSESW_LIBS
+LIBUTF8PROC_CFLAGS
+LIBUTF8PROC_LIBS
SYSTEMD_CFLAGS
SYSTEMD_LIBS'
@@ -1368,7 +1379,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures tmux 3.3a to adapt to many kinds of systems.
+\`configure' configures tmux 3.4 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1439,7 +1450,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of tmux 3.3a:";;
+ short | recursive ) echo "Configuration of tmux 3.4:";;
esac
cat <<\_ACEOF
@@ -1464,6 +1475,10 @@ Optional Features:
--enable-systemd enable systemd integration
+ --disable-cgroups disable adding panes to new cgroups with systemd
+
+ --enable-sixel enable sixel images
+
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -1512,6 +1527,10 @@ Some influential environment variables:
C compiler flags for LIBNCURSESW, overriding pkg-config
LIBNCURSESW_LIBS
linker flags for LIBNCURSESW, overriding pkg-config
+ LIBUTF8PROC_CFLAGS
+ C compiler flags for LIBUTF8PROC, overriding pkg-config
+ LIBUTF8PROC_LIBS
+ linker flags for LIBUTF8PROC, overriding pkg-config
SYSTEMD_CFLAGS
C compiler flags for SYSTEMD, overriding pkg-config
SYSTEMD_LIBS
@@ -1583,7 +1602,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-tmux configure 3.3a
+tmux configure 3.4
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1994,7 +2013,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by tmux $as_me 3.3a, which was
+It was created by tmux $as_me 3.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2860,7 +2879,7 @@ fi
# Define the identity of the package.
PACKAGE='tmux'
- VERSION='3.3a'
+ VERSION='3.4'
cat >>confdefs.h <<_ACEOF
@@ -5202,7 +5221,7 @@ for ac_func in \
prctl \
proc_pidinfo \
getpeerucred \
- sysconf \
+ sysconf
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
@@ -5373,6 +5392,19 @@ esac
fi
+ac_fn_c_check_func "$LINENO" "htonll" "ac_cv_func_htonll"
+if test "x$ac_cv_func_htonll" = xyes; then :
+ $as_echo "#define HAVE_HTONLL 1" >>confdefs.h
+
+else
+ case " $LIBOBJS " in
+ *" htonll.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS htonll.$ac_objext"
+ ;;
+esac
+
+fi
+
ac_fn_c_check_func "$LINENO" "memmem" "ac_cv_func_memmem"
if test "x$ac_cv_func_memmem" = xyes; then :
$as_echo "#define HAVE_MEMMEM 1" >>confdefs.h
@@ -5386,6 +5418,19 @@ esac
fi
+ac_fn_c_check_func "$LINENO" "ntohll" "ac_cv_func_ntohll"
+if test "x$ac_cv_func_ntohll" = xyes; then :
+ $as_echo "#define HAVE_NTOHLL 1" >>confdefs.h
+
+else
+ case " $LIBOBJS " in
+ *" ntohll.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS ntohll.$ac_objext"
+ ;;
+esac
+
+fi
+
ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv"
if test "x$ac_cv_func_setenv" = xyes; then :
$as_echo "#define HAVE_SETENV 1" >>confdefs.h
@@ -5978,6 +6023,49 @@ if test "x$found_libevent" = xno; then
as_fn_error $? "\"libevent not found\"" "$LINENO" 5
fi
+# Look for yacc.
+# Extract the first word of "$YACC", so it can be a program name with args.
+set dummy $YACC; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_found_yacc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$found_yacc"; then
+ ac_cv_prog_found_yacc="$found_yacc" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_found_yacc="yes"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_prog_found_yacc" && ac_cv_prog_found_yacc="no"
+fi
+fi
+found_yacc=$ac_cv_prog_found_yacc
+if test -n "$found_yacc"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $found_yacc" >&5
+$as_echo "$found_yacc" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+if test "x$found_yacc" = xno; then
+ as_fn_error $? "\"yacc not found\"" "$LINENO" 5
+fi
+
# Look for ncurses or curses. Try pkg-config first then directly for the
# library.
@@ -6243,7 +6331,7 @@ return setupterm ();
return 0;
}
_ACEOF
-for ac_lib in '' tinfo ncurses ncursesw; do
+for ac_lib in '' tinfo terminfo ncurses ncursesw; do
if test -z "$ac_lib"; then
ac_res="none required"
else
@@ -6356,6 +6444,21 @@ fi
as_fn_error $? "\"curses not found\"" "$LINENO" 5
fi
fi
+for ac_func in \
+ tiparm \
+ tiparm_s \
+
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
# Look for utempter.
# Check whether --enable-utempter was given.
@@ -6448,6 +6551,102 @@ if test "${enable_utf8proc+set}" = set; then :
fi
if test "x$enable_utf8proc" = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libutf8proc" >&5
+$as_echo_n "checking for libutf8proc... " >&6; }
+
+if test -n "$LIBUTF8PROC_CFLAGS"; then
+ pkg_cv_LIBUTF8PROC_CFLAGS="$LIBUTF8PROC_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libutf8proc\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libutf8proc") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_LIBUTF8PROC_CFLAGS=`$PKG_CONFIG --cflags "libutf8proc" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$LIBUTF8PROC_LIBS"; then
+ pkg_cv_LIBUTF8PROC_LIBS="$LIBUTF8PROC_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libutf8proc\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libutf8proc") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_LIBUTF8PROC_LIBS=`$PKG_CONFIG --libs "libutf8proc" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ LIBUTF8PROC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libutf8proc" 2>&1`
+ else
+ LIBUTF8PROC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libutf8proc" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$LIBUTF8PROC_PKG_ERRORS" >&5
+
+ as_fn_error $? "Package requirements (libutf8proc) were not met:
+
+$LIBUTF8PROC_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables LIBUTF8PROC_CFLAGS
+and LIBUTF8PROC_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables LIBUTF8PROC_CFLAGS
+and LIBUTF8PROC_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ LIBUTF8PROC_CFLAGS=$pkg_cv_LIBUTF8PROC_CFLAGS
+ LIBUTF8PROC_LIBS=$pkg_cv_LIBUTF8PROC_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+ AM_CPPFLAGS="$LIBUTF8PROC_CFLAGS $AM_CPPFLAGS"
+ CPPFLAGS="$LIBUTF8PROC_CFLAGS $SAVED_CPPFLAGS"
+ LIBS="$LIBUTF8PROC_LIBS $LIBS"
+
+
+fi
ac_fn_c_check_header_mongrel "$LINENO" "utf8proc.h" "ac_cv_header_utf8proc_h" "$ac_includes_default"
if test "x$ac_cv_header_utf8proc_h" = xyes; then :
enable_utf8proc=yes
@@ -6633,11 +6832,47 @@ else
HAVE_SYSTEMD_FALSE=
fi
+# Check whether --enable-cgroups was given.
+if test "${enable_cgroups+set}" = set; then :
+ enableval=$enable_cgroups;
+fi
+
+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
+ $as_echo "#define ENABLE_CGROUPS 1" >>confdefs.h
+
+ else
+ as_fn_error $? "\"cgroups requires systemd to be enabled\"" "$LINENO" 5
+ fi
+fi
+
+# Enable sixel support.
+# Check whether --enable-sixel was given.
+if test "${enable_sixel+set}" = set; then :
+ enableval=$enable_sixel;
+fi
+
+if test "x$enable_sixel" = xyes; then
+ $as_echo "#define ENABLE_SIXEL 1" >>confdefs.h
+
+fi
+ if test "x$enable_sixel" = xyes; then
+ ENABLE_SIXEL_TRUE=
+ ENABLE_SIXEL_FALSE='#'
+else
+ ENABLE_SIXEL_TRUE='#'
+ ENABLE_SIXEL_FALSE=
+fi
+
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for b64_ntop" >&5
$as_echo_n "checking for b64_ntop... " >&6; }
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/types.h>
@@ -7891,6 +8126,58 @@ else
fi
+# Set the default lock command
+DEFAULT_LOCK_CMD="lock -np"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking lock-command" >&5
+$as_echo_n "checking lock-command... " >&6; }
+if test "x$PLATFORM" = xlinux; then
+ # Extract the first word of "vlock", so it can be a program name with args.
+set dummy vlock; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_found_vlock+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$found_vlock"; then
+ ac_cv_prog_found_vlock="$found_vlock" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_found_vlock="yes"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_prog_found_vlock" && ac_cv_prog_found_vlock="no"
+fi
+fi
+found_vlock=$ac_cv_prog_found_vlock
+if test -n "$found_vlock"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $found_vlock" >&5
+$as_echo "$found_vlock" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test "x$found_vlock" = xyes; then
+ DEFAULT_LOCK_CMD="vlock"
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $DEFAULT_LOCK_CMD" >&5
+$as_echo "$DEFAULT_LOCK_CMD" >&6; }
+
+
+
# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user
# variables.
@@ -8096,6 +8383,10 @@ if test -z "${HAVE_SYSTEMD_TRUE}" && test -z "${HAVE_SYSTEMD_FALSE}"; then
as_fn_error $? "conditional \"HAVE_SYSTEMD\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
+if test -z "${ENABLE_SIXEL_TRUE}" && test -z "${ENABLE_SIXEL_FALSE}"; then
+ as_fn_error $? "conditional \"ENABLE_SIXEL\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
if test -z "${NEED_FORKPTY_TRUE}" && test -z "${NEED_FORKPTY_FALSE}"; then
as_fn_error $? "conditional \"NEED_FORKPTY\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -8541,7 +8832,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by tmux $as_me 3.3a, which was
+This file was extended by tmux $as_me 3.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -8598,7 +8889,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-tmux config.status 3.3a
+tmux config.status 3.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index 2b8b3b1..d800e45 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
# configure.ac
-AC_INIT([tmux], 3.3a)
+AC_INIT([tmux], 3.4)
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)
diff --git a/control-notify.c b/control-notify.c
index 6ff0e43..30f9419 100644
--- a/control-notify.c
+++ b/control-notify.c
@@ -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);
+ }
+}
diff --git a/control.c b/control.c
index 73286e0..578d04c 100644
--- a/control.c
+++ b/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)
diff --git a/environ.c b/environ.c
index 74d672e..5abf383 100644
--- a/environ.c
+++ b/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);
}
}
diff --git a/file.c b/file.c
index b2f155f..9bea517 100644
--- a/file.c
+++ b/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);
diff --git a/format-draw.c b/format-draw.c
index 1a7e60b..a42dfe1 100644
--- a/format-draw.c
+++ b/format-draw.c
@@ -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);
diff --git a/format.c b/format.c
index ccd259e..a5039df 100644
--- a/format.c
+++ b/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));
+}
diff --git a/grid-view.c b/grid-view.c
index f230d3c..4d68733 100644
--- a/grid-view.c
+++ b/grid-view.c
@@ -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));
}
diff --git a/grid.c b/grid.c
index 1109ac5..edada81 100644
--- a/grid.c
+++ b/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];
-
- 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);
+ int reset = (n != 0 && s[0] == 0);
+
+ 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--;
}
diff --git a/hyperlinks.c b/hyperlinks.c
new file mode 100644
index 0000000..70c2f4e
--- /dev/null
+++ b/hyperlinks.c
@@ -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);
+}
diff --git a/image-sixel.c b/image-sixel.c
new file mode 100644
index 0000000..3396a22
--- /dev/null
+++ b/image-sixel.c
@@ -0,0 +1,600 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+#define SIXEL_WIDTH_LIMIT 10000
+#define SIXEL_HEIGHT_LIMIT 10000
+
+struct sixel_line {
+ u_int x;
+ uint16_t *data;
+};
+
+struct sixel_image {
+ u_int x;
+ u_int y;
+ u_int xpixel;
+ u_int ypixel;
+
+ u_int *colours;
+ u_int ncolours;
+
+ u_int dx;
+ u_int dy;
+ u_int dc;
+
+ struct sixel_line *lines;
+};
+
+static int
+sixel_parse_expand_lines(struct sixel_image *si, u_int y)
+{
+ if (y <= si->y)
+ return (0);
+ if (y > SIXEL_HEIGHT_LIMIT)
+ return (1);
+ si->lines = xrecallocarray(si->lines, si->y, y, sizeof *si->lines);
+ si->y = y;
+ return (0);
+}
+
+static int
+sixel_parse_expand_line(struct sixel_image *si, struct sixel_line *sl, u_int x)
+{
+ if (x <= sl->x)
+ return (0);
+ if (x > SIXEL_WIDTH_LIMIT)
+ return (1);
+ if (x > si->x)
+ si->x = x;
+ sl->data = xrecallocarray(sl->data, sl->x, si->x, sizeof *sl->data);
+ sl->x = si->x;
+ return (0);
+}
+
+static u_int
+sixel_get_pixel(struct sixel_image *si, u_int x, u_int y)
+{
+ struct sixel_line *sl;
+
+ if (y >= si->y)
+ return (0);
+ sl = &si->lines[y];
+ if (x >= sl->x)
+ return (0);
+ return (sl->data[x]);
+}
+
+static int
+sixel_set_pixel(struct sixel_image *si, u_int x, u_int y, u_int c)
+{
+ struct sixel_line *sl;
+
+ if (sixel_parse_expand_lines(si, y + 1) != 0)
+ return (1);
+ sl = &si->lines[y];
+ if (sixel_parse_expand_line(si, sl, x + 1) != 0)
+ return (1);
+ sl->data[x] = c;
+ return (0);
+}
+
+static int
+sixel_parse_write(struct sixel_image *si, u_int ch)
+{
+ struct sixel_line *sl;
+ u_int i;
+
+ if (sixel_parse_expand_lines(si, si->dy + 6) != 0)
+ return (1);
+ sl = &si->lines[si->dy];
+
+ for (i = 0; i < 6; i++) {
+ if (sixel_parse_expand_line(si, sl, si->dx + 1) != 0)
+ return (1);
+ if (ch & (1 << i))
+ sl->data[si->dx] = si->dc;
+ sl++;
+ }
+ return (0);
+}
+
+static const char *
+sixel_parse_attributes(struct sixel_image *si, const char *cp, const char *end)
+{
+ const char *last;
+ char *endptr;
+ u_int x, y;
+
+ last = cp;
+ while (last != end) {
+ if (*last != ';' && (*last < '0' || *last > '9'))
+ break;
+ last++;
+ }
+ strtoul(cp, &endptr, 10);
+ if (endptr == last || *endptr != ';')
+ return (last);
+ strtoul(endptr + 1, &endptr, 10);
+ if (endptr == last)
+ return (last);
+ if (*endptr != ';') {
+ log_debug("%s: missing ;", __func__);
+ return (NULL);
+ }
+
+ x = strtoul(endptr + 1, &endptr, 10);
+ if (endptr == last || *endptr != ';') {
+ log_debug("%s: missing ;", __func__);
+ return (NULL);
+ }
+ if (x > SIXEL_WIDTH_LIMIT) {
+ log_debug("%s: image is too wide", __func__);
+ return (NULL);
+ }
+ y = strtoul(endptr + 1, &endptr, 10);
+ if (endptr != last) {
+ log_debug("%s: extra ;", __func__);
+ return (NULL);
+ }
+ if (y > SIXEL_HEIGHT_LIMIT) {
+ log_debug("%s: image is too tall", __func__);
+ return (NULL);
+ }
+
+ si->x = x;
+ sixel_parse_expand_lines(si, y);
+
+ return (last);
+}
+
+static const char *
+sixel_parse_colour(struct sixel_image *si, const char *cp, const char *end)
+{
+ const char *last;
+ char *endptr;
+ u_int c, type, r, g, b;
+
+ last = cp;
+ while (last != end) {
+ if (*last != ';' && (*last < '0' || *last > '9'))
+ break;
+ last++;
+ }
+
+ c = strtoul(cp, &endptr, 10);
+ if (c > SIXEL_COLOUR_REGISTERS) {
+ log_debug("%s: too many colours", __func__);
+ return (NULL);
+ }
+ si->dc = c + 1;
+ if (endptr == last || *endptr != ';')
+ return (last);
+
+ type = strtoul(endptr + 1, &endptr, 10);
+ if (endptr == last || *endptr != ';') {
+ log_debug("%s: missing ;", __func__);
+ return (NULL);
+ }
+ r = strtoul(endptr + 1, &endptr, 10);
+ if (endptr == last || *endptr != ';') {
+ log_debug("%s: missing ;", __func__);
+ return (NULL);
+ }
+ g = strtoul(endptr + 1, &endptr, 10);
+ if (endptr == last || *endptr != ';') {
+ log_debug("%s: missing ;", __func__);
+ return (NULL);
+ }
+ b = strtoul(endptr + 1, &endptr, 10);
+ if (endptr != last) {
+ log_debug("%s: missing ;", __func__);
+ return (NULL);
+ }
+
+ if (type != 1 && type != 2) {
+ log_debug("%s: invalid type %d", __func__, type);
+ return (NULL);
+ }
+ if (c + 1 > si->ncolours) {
+ si->colours = xrecallocarray(si->colours, si->ncolours, c + 1,
+ sizeof *si->colours);
+ si->ncolours = c + 1;
+ }
+ si->colours[c] = (type << 24) | (r << 16) | (g << 8) | b;
+ return (last);
+}
+
+static const char *
+sixel_parse_repeat(struct sixel_image *si, const char *cp, const char *end)
+{
+ const char *last;
+ char tmp[32], ch;
+ u_int n = 0, i;
+ const char *errstr = NULL;
+
+ last = cp;
+ while (last != end) {
+ if (*last < '0' || *last > '9')
+ break;
+ tmp[n++] = *last++;
+ if (n == (sizeof tmp) - 1) {
+ log_debug("%s: repeat not terminated", __func__);
+ return (NULL);
+ }
+ }
+ if (n == 0 || last == end) {
+ log_debug("%s: repeat not terminated", __func__);
+ return (NULL);
+ }
+ tmp[n] = '\0';
+
+ n = strtonum(tmp, 1, SIXEL_WIDTH_LIMIT, &errstr);
+ if (n == 0 || errstr != NULL) {
+ log_debug("%s: repeat too wide", __func__);
+ return (NULL);
+ }
+
+ ch = (*last++) - 0x3f;
+ for (i = 0; i < n; i++) {
+ if (sixel_parse_write(si, ch) != 0) {
+ log_debug("%s: width limit reached", __func__);
+ return (NULL);
+ }
+ si->dx++;
+ }
+ return (last);
+}
+
+struct sixel_image *
+sixel_parse(const char *buf, size_t len, u_int xpixel, u_int ypixel)
+{
+ struct sixel_image *si;
+ const char *cp = buf, *end = buf + len;
+ char ch;
+
+ if (len == 0 || len == 1 || *cp++ != 'q') {
+ log_debug("%s: empty image", __func__);
+ return (NULL);
+ }
+
+ si = xcalloc (1, sizeof *si);
+ si->xpixel = xpixel;
+ si->ypixel = ypixel;
+
+ while (cp != end) {
+ ch = *cp++;
+ switch (ch) {
+ case '"':
+ cp = sixel_parse_attributes(si, cp, end);
+ if (cp == NULL)
+ goto bad;
+ break;
+ case '#':
+ cp = sixel_parse_colour(si, cp, end);
+ if (cp == NULL)
+ goto bad;
+ break;
+ case '!':
+ cp = sixel_parse_repeat(si, cp, end);
+ if (cp == NULL)
+ goto bad;
+ break;
+ case '-':
+ si->dx = 0;
+ si->dy += 6;
+ break;
+ case '$':
+ si->dx = 0;
+ break;
+ default:
+ if (ch < 0x20)
+ break;
+ if (ch < 0x3f || ch > 0x7e)
+ goto bad;
+ if (sixel_parse_write(si, ch - 0x3f) != 0) {
+ log_debug("%s: width limit reached", __func__);
+ goto bad;
+ }
+ si->dx++;
+ break;
+ }
+ }
+
+ if (si->x == 0 || si->y == 0)
+ goto bad;
+ return (si);
+
+bad:
+ free(si);
+ return (NULL);
+}
+
+void
+sixel_free(struct sixel_image *si)
+{
+ u_int y;
+
+ for (y = 0; y < si->y; y++)
+ free(si->lines[y].data);
+ free(si->lines);
+
+ free(si->colours);
+ free(si);
+}
+
+void
+sixel_log(struct sixel_image *si)
+{
+ struct sixel_line *sl;
+ char s[SIXEL_WIDTH_LIMIT + 1];
+ u_int i, x, y, cx, cy;
+
+ sixel_size_in_cells(si, &cx, &cy);
+ log_debug("%s: image %ux%u (%ux%u)", __func__, si->x, si->y, cx, cy);
+ for (i = 0; i < si->ncolours; i++)
+ log_debug("%s: colour %u is %07x", __func__, i, si->colours[i]);
+ for (y = 0; y < si->y; y++) {
+ sl = &si->lines[y];
+ for (x = 0; x < si->x; x++) {
+ if (x >= sl->x)
+ s[x] = '_';
+ else if (sl->data[x] != 0)
+ s[x] = '0' + (sl->data[x] - 1) % 10;
+ else
+ s[x] = '.';
+ }
+ s[x] = '\0';
+ log_debug("%s: %4u: %s", __func__, y, s);
+ }
+}
+
+void
+sixel_size_in_cells(struct sixel_image *si, u_int *x, u_int *y)
+{
+ if ((si->x % si->xpixel) == 0)
+ *x = (si->x / si->xpixel);
+ else
+ *x = 1 + (si->x / si->xpixel);
+ if ((si->y % si->ypixel) == 0)
+ *y = (si->y / si->ypixel);
+ else
+ *y = 1 + (si->y / si->ypixel);
+}
+
+struct sixel_image *
+sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
+ u_int oy, u_int sx, u_int sy, int colours)
+{
+ struct sixel_image *new;
+ u_int cx, cy, pox, poy, psx, psy, tsx, tsy, px, py;
+ u_int x, y, i;
+
+ /*
+ * We want to get the section of the image at ox,oy in image cells and
+ * map it onto the same size in terminal cells, remembering that we
+ * can only draw vertical sections of six pixels.
+ */
+
+ sixel_size_in_cells(si, &cx, &cy);
+ if (ox >= cx)
+ return (NULL);
+ if (oy >= cy)
+ return (NULL);
+ if (ox + sx >= cx)
+ sx = cx - ox;
+ if (oy + sy >= cy)
+ sy = cy - oy;
+
+ if (xpixel == 0)
+ xpixel = si->xpixel;
+ if (ypixel == 0)
+ ypixel = si->ypixel;
+
+ pox = ox * si->xpixel;
+ poy = oy * si->ypixel;
+ psx = sx * si->xpixel;
+ psy = sy * si->ypixel;
+
+ tsx = sx * xpixel;
+ tsy = ((sy * ypixel) / 6) * 6;
+
+ new = xcalloc (1, sizeof *si);
+ new->xpixel = xpixel;
+ new->ypixel = ypixel;
+
+ for (y = 0; y < tsy; y++) {
+ py = poy + ((double)y * psy / tsy);
+ for (x = 0; x < tsx; x++) {
+ px = pox + ((double)x * psx / tsx);
+ sixel_set_pixel(new, x, y, sixel_get_pixel(si, px, py));
+ }
+ }
+
+ if (colours) {
+ new->colours = xmalloc(si->ncolours * sizeof *new->colours);
+ for (i = 0; i < si->ncolours; i++)
+ new->colours[i] = si->colours[i];
+ new->ncolours = si->ncolours;
+ }
+ return (new);
+}
+
+static void
+sixel_print_add(char **buf, size_t *len, size_t *used, const char *s,
+ size_t slen)
+{
+ if (*used + slen >= *len + 1) {
+ (*len) *= 2;
+ *buf = xrealloc(*buf, *len);
+ }
+ memcpy(*buf + *used, s, slen);
+ (*used) += slen;
+}
+
+static void
+sixel_print_repeat(char **buf, size_t *len, size_t *used, u_int count, char ch)
+{
+ char tmp[16];
+ size_t tmplen;
+
+ if (count == 1)
+ sixel_print_add(buf, len, used, &ch, 1);
+ else if (count == 2) {
+ sixel_print_add(buf, len, used, &ch, 1);
+ sixel_print_add(buf, len, used, &ch, 1);
+ } else if (count == 3) {
+ sixel_print_add(buf, len, used, &ch, 1);
+ sixel_print_add(buf, len, used, &ch, 1);
+ sixel_print_add(buf, len, used, &ch, 1);
+ } else if (count != 0) {
+ tmplen = xsnprintf(tmp, sizeof tmp, "!%u%c", count, ch);
+ sixel_print_add(buf, len, used, tmp, tmplen);
+ }
+}
+
+char *
+sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
+{
+ char *buf, tmp[64], *contains, data, last = 0;
+ size_t len, used = 0, tmplen;
+ u_int *colours, ncolours, i, c, x, y, count;
+ struct sixel_line *sl;
+
+ if (map != NULL) {
+ colours = map->colours;
+ ncolours = map->ncolours;
+ } else {
+ colours = si->colours;
+ ncolours = si->ncolours;
+ }
+ contains = xcalloc(1, ncolours);
+
+ len = 8192;
+ buf = xmalloc(len);
+
+ sixel_print_add(&buf, &len, &used, "\033Pq", 3);
+
+ tmplen = xsnprintf(tmp, sizeof tmp, "\"1;1;%u;%u", si->x, si->y);
+ sixel_print_add(&buf, &len, &used, tmp, tmplen);
+
+ for (i = 0; i < ncolours; i++) {
+ c = colours[i];
+ tmplen = xsnprintf(tmp, sizeof tmp, "#%u;%u;%u;%u;%u",
+ i, c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff);
+ sixel_print_add(&buf, &len, &used, tmp, tmplen);
+ }
+
+ for (y = 0; y < si->y; y += 6) {
+ memset(contains, 0, ncolours);
+ for (x = 0; x < si->x; x++) {
+ for (i = 0; i < 6; i++) {
+ if (y + i >= si->y)
+ break;
+ sl = &si->lines[y + i];
+ if (x < sl->x && sl->data[x] != 0)
+ contains[sl->data[x] - 1] = 1;
+ }
+ }
+
+ for (c = 0; c < ncolours; c++) {
+ if (!contains[c])
+ continue;
+ tmplen = xsnprintf(tmp, sizeof tmp, "#%u", c);
+ sixel_print_add(&buf, &len, &used, tmp, tmplen);
+
+ count = 0;
+ for (x = 0; x < si->x; x++) {
+ data = 0;
+ for (i = 0; i < 6; i++) {
+ if (y + i >= si->y)
+ break;
+ sl = &si->lines[y + i];
+ if (x < sl->x && sl->data[x] == c + 1)
+ data |= (1 << i);
+ }
+ data += 0x3f;
+ if (data != last) {
+ sixel_print_repeat(&buf, &len, &used,
+ count, last);
+ last = data;
+ count = 1;
+ } else
+ count++;
+ }
+ sixel_print_repeat(&buf, &len, &used, count, data);
+ sixel_print_add(&buf, &len, &used, "$", 1);
+ }
+
+ if (buf[used - 1] == '$')
+ used--;
+ if (buf[used - 1] != '-')
+ sixel_print_add(&buf, &len, &used, "-", 1);
+ }
+ if (buf[used - 1] == '$' || buf[used - 1] == '-')
+ used--;
+
+ sixel_print_add(&buf, &len, &used, "\033\\", 2);
+
+ buf[used] = '\0';
+ if (size != NULL)
+ *size = used;
+
+ free(contains);
+ return (buf);
+}
+
+struct screen *
+sixel_to_screen(struct sixel_image *si)
+{
+ struct screen *s;
+ struct screen_write_ctx ctx;
+ struct grid_cell gc;
+ u_int x, y, sx, sy;
+
+ sixel_size_in_cells(si, &sx, &sy);
+
+ s = xmalloc(sizeof *s);
+ screen_init(s, sx, sy, 0);
+
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ gc.attr |= (GRID_ATTR_CHARSET|GRID_ATTR_DIM);
+ utf8_set(&gc.data, '~');
+
+ screen_write_start(&ctx, s);
+ if (sx == 1 || sy == 1) {
+ for (y = 0; y < sy; y++) {
+ for (x = 0; x < sx; x++)
+ grid_view_set_cell(s->grid, x, y, &gc);
+ }
+ } else {
+ screen_write_box(&ctx, sx, sy, BOX_LINES_DEFAULT, NULL, NULL);
+ for (y = 1; y < sy - 1; y++) {
+ for (x = 1; x < sx - 1; x++)
+ grid_view_set_cell(s->grid, x, y, &gc);
+ }
+ }
+ screen_write_stop(&ctx);
+ return (s);
+}
diff --git a/image.c b/image.c
new file mode 100644
index 0000000..4380878
--- /dev/null
+++ b/image.c
@@ -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);
+}
diff --git a/input-keys.c b/input-keys.c
index ebf6133..0451b96 100644
--- a/input-keys.c
+++ b/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));
diff --git a/input.c b/input.c
index 693b6f3..eb421b2 100644
--- a/input.c
+++ b/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(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,6 +2364,8 @@ 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++;
@@ -2316,6 +2390,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 +2417,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 +2536,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 +2584,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 +2599,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 +2690,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 +2738,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 +2794,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 +2809,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 +2843,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 +2860,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 +2887,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 +2936,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) {
diff --git a/key-bindings.c b/key-bindings.c
index 9517196..eaf05f0 100644
--- a/key-bindings.c
+++ b/key-bindings.c
@@ -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 }",
diff --git a/key-string.c b/key-string.c
index 0ca9130..699d460 100644
--- a/key-string.c
+++ b/key-string.c
@@ -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);
diff --git a/layout-custom.c b/layout-custom.c
index 932b30e..d7be5b1 100644
--- a/layout-custom.c
+++ b/layout-custom.c
@@ -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");
diff --git a/menu.c b/menu.c
index 16120be..8d7b40a 100644
--- a/menu.c
+++ b/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 == '-'))
- md->choice++;
- if (md->choice == count)
- md->choice = -1;
+ 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 'G':
case KEYC_NPAGE:
- if (md->choice > count - 6)
+ if (md->choice > count - 6) {
md->choice = count - 1;
- else
- md->choice += 5;
- while (md->choice != -1 && (name == NULL || *name == '-'))
+ 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++;
+ name = menu->items[md->choice].name;
+ }
+ c->flags |= CLIENT_REDRAWOVERLAY;
+ break;
+ case 'G':
+ 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,
diff --git a/mode-tree.c b/mode-tree.c
index c007e27..cebd4f0 100644
--- a/mode-tree.c
+++ b/mode-tree.c
@@ -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);
}
diff --git a/notify.c b/notify.c
index 8525585..d42e2b9 100644
--- a/notify.c
+++ b/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);
+ }
}
diff --git a/options-table.c b/options-table.c
index 17be7ec..f030f2d 100644
--- a/options-table.c
+++ b/options-table.c
@@ -41,6 +41,9 @@ static const char *options_table_clock_mode_style_list[] = {
static const char *options_table_status_list[] = {
"off", "on", "2", "3", "4", "5", NULL
};
+static const char *options_table_message_line_list[] = {
+ "0", "1", "2", "3", "4", NULL
+};
static const char *options_table_status_keys_list[] = {
"emacs", "vi", NULL
};
@@ -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 \
@@ -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",
@@ -916,8 +974,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."
},
diff --git a/options.c b/options.c
index f9cf2af..fef5637 100644
--- a/options.c
+++ b/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)
diff --git a/paste.c b/paste.c
index 51ae2b1..608ac9c 100644
--- a/paste.c
+++ b/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. */
diff --git a/popup.c b/popup.c
index 2e57153..4742733 100644
--- a/popup.c
+++ b/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;
@@ -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);
diff --git a/proc.c b/proc.c
index 67ec214..7c4805f 100644
--- a/proc.c
+++ b/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);
diff --git a/regsub.c b/regsub.c
index 4039b9b..61a9c32 100644
--- a/regsub.c
+++ b/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;
diff --git a/screen-redraw.c b/screen-redraw.c
index c4906ab..ce79b41 100644
--- a/screen-redraw.c
+++ b/screen-redraw.c
@@ -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
}
diff --git a/screen-write.c b/screen-write.c
index 6b6a750..6892d04 100644
--- a/screen-write.c
+++ b/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,84 +691,53 @@ 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;
}
- }
- screen_write_set_cursor(ctx, cx, cy);
-}
+ if (choice >= 0 && i == (u_int)choice && *name != '-')
+ gc = choice_gc;
-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;
+ 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);
}
/* Draw a box on screen. */
@@ -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;
-
- /* Can't combine if at 0. */
- if (s->cx == 0)
- return (NULL);
+ 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;
- /* Empty data is out. */
- if (ud->size == 0)
- fatalx("UTF-8 data empty");
+ /*
+ * 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;
+
+ /* 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);
+
+ /* 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 != last.data.width || (last.flags & GRID_FLAG_PADDING))
+ return (zero_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;
+ /*
+ * 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);
}
- if (n > s->cx)
- return (NULL);
- *xx = s->cx - n;
- /* Check there is enough space. */
- if (gc.data.size + ud->size > sizeof gc.data.data)
- return (NULL);
+ /* Check if this combined character would be too long. */
+ if (last.data.size + ud->size > sizeof last.data.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);
+ /* 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, cy);
+
+ /*
+ * 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 (&gc);
+ 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,74 @@ 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;
+ 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 +2328,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 +2347,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);
}
diff --git a/screen.c b/screen.c
index eceef64..f73a850 100644
--- a/screen.c
+++ b/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,8 +308,12 @@ 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);
- if (reflow)
+ if (reflow) {
+#ifdef ENABLE_SIXEL
+ image_free_all(s);
+#endif
screen_reflow(s, sx, &cx, &cy, cursor);
+ }
if (cy >= s->grid->hsize) {
s->cx = cx;
@@ -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)
diff --git a/server-client.c b/server-client.c
index 8144bcd..a749450 100644
--- a/server-client.c
+++ b/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;
@@ -667,6 +668,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 +685,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_PANE:
+ fwp = window_pane_find_by_id(sr->argument);
+ if (fwp == NULL)
+ return (KEYC_UNKNOWN);
+ m->wp = sr->argument;
+
+ log_debug("mouse range: pane %%%u", m->wp);
+ where = STATUS;
+ break;
case STYLE_RANGE_WINDOW:
- wl = winlink_find_by_index(&s->windows,
+ fwl = winlink_find_by_index(&s->windows,
sr->argument);
- if (wl == NULL)
+ if (fwl == NULL)
return (KEYC_UNKNOWN);
- m->w = wl->window->id;
+ 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 +1780,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 +1863,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 +1907,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 +1935,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 +2271,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 +2768,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 +2843,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 +2998,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 +3268,69 @@ server_client_remove_pane(struct window_pane *wp)
}
}
}
+
+/* Print to a client. */
+void
+server_client_print(struct client *c, int parse, struct evbuffer *evb)
+{
+ void *data = EVBUFFER_DATA(evb);
+ size_t size = EVBUFFER_LENGTH(evb);
+ struct window_pane *wp;
+ struct window_mode_entry *wme;
+ char *sanitized, *msg, *line;
+
+ if (!parse) {
+ utf8_stravisx(&msg, data, size,
+ VIS_OCTAL|VIS_CSTYLE|VIS_NOSLASH);
+ log_debug("%s: %s", __func__, msg);
+ } else {
+ msg = EVBUFFER_DATA(evb);
+ if (msg[size - 1] != '\0')
+ evbuffer_add(evb, "", 1);
+ }
+
+ if (c == NULL)
+ goto out;
+
+ if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
+ if (~c->flags & CLIENT_UTF8) {
+ sanitized = utf8_sanitize(msg);
+ if (c->flags & CLIENT_CONTROL)
+ control_write(c, "%s", sanitized);
+ else
+ file_print(c, "%s\n", sanitized);
+ free(sanitized);
+ } else {
+ if (c->flags & CLIENT_CONTROL)
+ control_write(c, "%s", msg);
+ else
+ file_print(c, "%s\n", msg);
+ }
+ goto out;
+ }
+
+ wp = server_client_get_pane(c);
+ wme = TAILQ_FIRST(&wp->modes);
+ if (wme == NULL || wme->mode != &window_view_mode)
+ window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
+ if (parse) {
+ do {
+ line = evbuffer_readln(evb, NULL, EVBUFFER_EOL_LF);
+ if (line != NULL) {
+ window_copy_add(wp, 1, "%s", line);
+ free(line);
+ }
+ } while (line != NULL);
+
+ size = EVBUFFER_LENGTH(evb);
+ if (size != 0) {
+ line = EVBUFFER_DATA(evb);
+ window_copy_add(wp, 1, "%.*s", (int)size, line);
+ }
+ } else
+ window_copy_add(wp, 0, "%s", msg);
+
+out:
+ if (!parse)
+ free(msg);
+}
diff --git a/server-fn.c b/server-fn.c
index 2a79f3e..2f64932 100644
--- a/server-fn.c
+++ b/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,8 +465,23 @@ 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__);
}
}
diff --git a/server.c b/server.c
index 05bc50f..6fef468 100644
--- a/server.c
+++ b/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) {
diff --git a/session.c b/session.c
index 7502888..0823317 100644
--- a/session.c
+++ b/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). */
diff --git a/spawn.c b/spawn.c
index 2cb2b65..d321dba 100644
--- a/spawn.c
+++ b/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);
diff --git a/status.c b/status.c
index 929276d..a11cc8b 100644
--- a/status.c
+++ b/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;
-
- status_message_clear(c);
- status_push_screen(c);
+ struct timeval tv;
+ va_list ap;
+ char *s;
va_start(ap, fmt);
- xvasprintf(&c->message_string, fmt, ap);
+ xvasprintf(&s, fmt, ap);
va_end(ap);
- server_add_message("%s message: %s", c->name, c->message_string);
+ 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);
+ 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)
@@ -1452,8 +1480,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 +1773,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);
@@ -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);
diff --git a/style.c b/style.c
index 89a4e63..35293a1 100644
--- a/style.c
+++ b/style.c
@@ -30,18 +30,25 @@
/* Default style. */
static struct style style_default = {
- { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 },
+ { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0 },
0,
8,
STYLE_ALIGN_DEFAULT,
STYLE_LIST_OFF,
- STYLE_RANGE_NONE, 0,
+ STYLE_RANGE_NONE, 0, "",
STYLE_DEFAULT_BASE
};
+/* Set range string. */
+static void
+style_set_range_string(struct style *sy, const char *s)
+{
+ strlcpy(sy->range_string, s, sizeof sy->range_string);
+}
+
/*
* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". Note
* that this adds onto the given style, so it must have been initialized
@@ -77,6 +84,7 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
if (strcasecmp(tmp, "default") == 0) {
sy->gc.fg = base->fg;
sy->gc.bg = base->bg;
+ sy->gc.us = base->us;
sy->gc.attr = base->attr;
sy->gc.flags = base->flags;
} else if (strcasecmp(tmp, "ignore") == 0)
@@ -103,32 +111,67 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
} else if (strcasecmp(tmp, "norange") == 0) {
sy->range_type = style_default.range_type;
sy->range_argument = style_default.range_type;
+ strlcpy(sy->range_string, style_default.range_string,
+ sizeof sy->range_string);
} else if (end > 6 && strncasecmp(tmp, "range=", 6) == 0) {
found = strchr(tmp + 6, '|');
if (found != NULL) {
*found++ = '\0';
if (*found == '\0')
goto error;
- for (cp = found; *cp != '\0'; cp++) {
- if (!isdigit((u_char)*cp))
- goto error;
- }
}
if (strcasecmp(tmp + 6, "left") == 0) {
if (found != NULL)
goto error;
sy->range_type = STYLE_RANGE_LEFT;
sy->range_argument = 0;
+ style_set_range_string(sy, "");
} else if (strcasecmp(tmp + 6, "right") == 0) {
if (found != NULL)
goto error;
sy->range_type = STYLE_RANGE_RIGHT;
sy->range_argument = 0;
+ style_set_range_string(sy, "");
+ } else if (strcasecmp(tmp + 6, "pane") == 0) {
+ if (found == NULL)
+ goto error;
+ if (*found != '%' || found[1] == '\0')
+ goto error;
+ for (cp = found + 1; *cp != '\0'; cp++) {
+ if (!isdigit((u_char)*cp))
+ goto error;
+ }
+ sy->range_type = STYLE_RANGE_PANE;
+ sy->range_argument = atoi(found + 1);
+ style_set_range_string(sy, "");
} else if (strcasecmp(tmp + 6, "window") == 0) {
if (found == NULL)
goto error;
+ for (cp = found; *cp != '\0'; cp++) {
+ if (!isdigit((u_char)*cp))
+ goto error;
+ }
sy->range_type = STYLE_RANGE_WINDOW;
sy->range_argument = atoi(found);
+ style_set_range_string(sy, "");
+ } else if (strcasecmp(tmp + 6, "session") == 0) {
+ if (found == NULL)
+ goto error;
+ if (*found != '$' || found[1] == '\0')
+ goto error;
+ for (cp = found + 1; *cp != '\0'; cp++) {
+ if (!isdigit((u_char)*cp))
+ goto error;
+ }
+ sy->range_type = STYLE_RANGE_SESSION;
+ sy->range_argument = atoi(found + 1);
+ style_set_range_string(sy, "");
+ } else if (strcasecmp(tmp + 6, "user") == 0) {
+ if (found == NULL)
+ goto error;
+ sy->range_type = STYLE_RANGE_USER;
+ sy->range_argument = 0;
+ style_set_range_string(sy, found);
}
} else if (strcasecmp(tmp, "noalign") == 0)
sy->align = style_default.align;
@@ -162,6 +205,13 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
sy->gc.bg = base->bg;
} else
goto error;
+ } else if (end > 3 && strncasecmp(tmp, "us=", 3) == 0) {
+ if ((value = colour_fromstring(tmp + 3)) == -1)
+ goto error;
+ if (value != 8)
+ sy->gc.us = value;
+ else
+ sy->gc.us = base->us;
} else if (strcasecmp(tmp, "none") == 0)
sy->gc.attr = 0;
else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) {
@@ -192,7 +242,7 @@ style_tostring(struct style *sy)
int off = 0;
const char *comma = "", *tmp = "";
static char s[256];
- char b[16];
+ char b[21];
*s = '\0';
@@ -214,9 +264,19 @@ style_tostring(struct style *sy)
tmp = "left";
else if (sy->range_type == STYLE_RANGE_RIGHT)
tmp = "right";
- else if (sy->range_type == STYLE_RANGE_WINDOW) {
+ else if (sy->range_type == STYLE_RANGE_PANE) {
+ snprintf(b, sizeof b, "pane|%%%u", sy->range_argument);
+ tmp = b;
+ } else if (sy->range_type == STYLE_RANGE_WINDOW) {
snprintf(b, sizeof b, "window|%u", sy->range_argument);
tmp = b;
+ } else if (sy->range_type == STYLE_RANGE_SESSION) {
+ snprintf(b, sizeof b, "session|$%u",
+ sy->range_argument);
+ tmp = b;
+ } else if (sy->range_type == STYLE_RANGE_USER) {
+ snprintf(b, sizeof b, "user|%s", sy->range_string);
+ tmp = b;
}
off += xsnprintf(s + off, sizeof s - off, "%srange=%s", comma,
tmp);
@@ -258,6 +318,11 @@ style_tostring(struct style *sy)
colour_tostring(gc->bg));
comma = ",";
}
+ if (gc->us != 8) {
+ off += xsnprintf(s + off, sizeof s - off, "%sus=%s", comma,
+ colour_tostring(gc->us));
+ comma = ",";
+ }
if (gc->attr != 0) {
xsnprintf(s + off, sizeof s - off, "%s%s", comma,
attributes_tostring(gc->attr));
@@ -287,6 +352,8 @@ style_add(struct grid_cell *gc, struct options *oo, const char *name,
gc->fg = sy->gc.fg;
if (sy->gc.bg != 8)
gc->bg = sy->gc.bg;
+ if (sy->gc.us != 8)
+ gc->us = sy->gc.us;
gc->attr |= sy->gc.attr;
if (ft0 != NULL)
diff --git a/tmux-protocol.h b/tmux-protocol.h
index 0842229..3cf00c0 100644
--- a/tmux-protocol.h
+++ b/tmux-protocol.h
@@ -66,7 +66,8 @@ enum msgtype {
MSG_WRITE_OPEN,
MSG_WRITE,
MSG_WRITE_READY,
- MSG_WRITE_CLOSE
+ MSG_WRITE_CLOSE,
+ MSG_READ_CANCEL
};
/*
@@ -92,6 +93,10 @@ struct msg_read_done {
int error;
};
+struct msg_read_cancel {
+ int stream;
+};
+
struct msg_write_open {
int stream;
int fd;
diff --git a/tmux.1 b/tmux.1
index d89ee84..9c02835 100644
--- a/tmux.1
+++ b/tmux.1
@@ -23,7 +23,7 @@
.Sh SYNOPSIS
.Nm tmux
.Bk -words
-.Op Fl 2CDluvV
+.Op Fl 2CDlNuVv
.Op Fl c Ar shell-command
.Op Fl f Ar file
.Op Fl L Ar socket-name
@@ -140,10 +140,10 @@ By default,
loads the system configuration file from
.Pa @SYSCONFDIR@/tmux.conf ,
if present, then looks for a user configuration file at
-.Pa ~/.tmux.conf,
+.Pa \[ti]/.tmux.conf,
.Pa $XDG_CONFIG_HOME/tmux/tmux.conf
or
-.Pa ~/.config/tmux/tmux.conf .
+.Pa \[ti]/.tmux.conf .
.Pp
The configuration file is a set of
.Nm
@@ -206,6 +206,12 @@ If
is specified, the default socket directory is not used and any
.Fl L
flag is ignored.
+.It Fl T Ar features
+Set terminal features for the client.
+This is a comma-separated list of features.
+See the
+.Ic terminal-features
+option.
.It Fl u
Write UTF-8 output to the terminal even if the first environment
variable of
@@ -217,14 +223,10 @@ that is set does not contain
.Qq UTF-8
or
.Qq UTF8 .
-This is equivalent to
-.Fl T Ar UTF-8 .
-.It Fl T Ar features
-Set terminal features for the client.
-This is a comma-separated list of features.
-See the
-.Ic terminal-features
-option.
+.It Fl V
+Report the
+.Nm
+version.
.It Fl v
Request verbose logging.
Log messages will be saved into
@@ -249,10 +251,6 @@ signal may be sent to the
server process to toggle logging between on (as if
.Fl v
was given) and off.
-.It Fl V
-Report the
-.Nm
-version.
.It Ar command Op Ar flags
This specifies one of a set of commands used to control
.Nm ,
@@ -292,7 +290,7 @@ Rename the current session.
Split the current pane into two, left and right.
.It &
Kill the current window.
-.It '
+.It \[aq]
Prompt for a window index to select.
.It \&(
Switch the attached client to the previous session.
@@ -364,7 +362,7 @@ Toggle zoom state of the current pane.
Swap the current pane with the previous pane.
.It }
Swap the current pane with the next pane.
-.It ~
+.It \[ti]
Show previous messages from
.Nm ,
if any.
@@ -410,7 +408,7 @@ the command prompt.
For example, the same
.Ic set-option
command run from the shell prompt, from
-.Pa ~/.tmux.conf
+.Pa \[ti]/.tmux.conf
and bound to a key may look like:
.Bd -literal -offset indent
$ tmux set-option -g status-style bg=cyan
@@ -463,7 +461,7 @@ To execute commands, each client has a
.Ql command queue .
A global command queue not attached to any client is used on startup
for configuration files like
-.Pa ~/.tmux.conf .
+.Pa \[ti]/.tmux.conf .
Parsed commands added to the queue are executed in order.
Some commands, like
.Ic if-shell
@@ -471,7 +469,8 @@ and
.Ic confirm-before ,
parse their argument to create a new command which is inserted immediately
after themselves.
-This means that arguments can be parsed twice or more - once when the parent command (such as
+This means that arguments can be parsed twice or more - once when the parent
+command (such as
.Ic if-shell )
is parsed and again when it parses and executes its command.
Commands like
@@ -534,7 +533,7 @@ $ tmux neww \\; splitw
.Pp
Or:
.Bd -literal -offset indent
-$ tmux neww ';' splitw
+$ tmux neww \[aq];\[aq] splitw
.Ed
.Pp
Or from the tmux command prompt:
@@ -547,12 +546,12 @@ for example in these
.Xr sh 1
commands:
.Bd -literal -offset indent
-$ tmux neww\e\e; splitw
+$ tmux neww\e; splitw
.Ed
.Pp
Or:
.Bd -literal -offset indent
-$ tmux 'neww;' splitw
+$ tmux \[aq]neww;\[aq] splitw
.Ed
.Pp
As in these examples, when running tmux from the shell extra care must be taken
@@ -564,7 +563,7 @@ should be escaped according to the shell conventions.
For
.Xr sh 1
this typically means quoted (such as
-.Ql neww ';' splitw )
+.Ql neww \[aq];\[aq] splitw )
or escaped (such as
.Ql neww \e\e\e\e; splitw ) .
.It
@@ -574,14 +573,14 @@ a second time for
.Nm ;
for example:
.Bd -literal -offset indent
-$ tmux neww 'foo\e\e;' bar
+$ tmux neww \[aq]foo\e\e;\[aq] bar
$ tmux neww foo\e\e\e\e; bar
.Ed
.It
Semicolons that are not individual tokens or trailing another token should only
be escaped once according to shell conventions; for example:
.Bd -literal -offset indent
-$ tmux neww 'foo-;-bar'
+$ tmux neww \[aq]foo-;-bar\[aq]
$ tmux neww foo-\e\e;-bar
.Ed
.El
@@ -594,8 +593,8 @@ line (the \e and the newline are completely removed).
This is called line continuation and applies both inside and outside quoted
strings and in comments, but not inside braces.
.Pp
-Command arguments may be specified as strings surrounded by single (') quotes,
-double quotes (") or braces ({}).
+Command arguments may be specified as strings surrounded by single (\[aq])
+quotes, double quotes (\[dq]) or braces ({}).
.\" "
This is required when the argument contains any special character.
Single and double quoted strings cannot span multiple lines except with line
@@ -610,7 +609,7 @@ global environment (see the
.Sx GLOBAL AND SESSION ENVIRONMENT
section).
.It
-A leading ~ or ~user is expanded to the home directory of the current or
+A leading \[ti] or \[ti]user is expanded to the home directory of the current or
specified user.
.It
\euXXXX or \euXXXXXXXX is replaced by the Unicode codepoint corresponding to
@@ -642,10 +641,10 @@ These two examples produce an identical command - note that no escaping is
needed when using {}:
.Bd -literal -offset indent
if-shell true {
- display -p 'brace-dollar-foo: }$foo'
+ display -p \[aq]brace-dollar-foo: }$foo\[aq]
}
-if-shell true "display -p 'brace-dollar-foo: }\e$foo'"
+if-shell true "display -p \[aq]brace-dollar-foo: }\e$foo\[aq]"
.Ed
.Pp
Braces may be enclosed inside braces, for example:
@@ -889,14 +888,14 @@ may consist entirely of the token
.Ql {mouse}
(alternative form
.Ql = )
-to specify the session, window or pane where the most recent mouse event occurred
-(see the
+to specify the session, window or pane where the most recent mouse event
+occurred (see the
.Sx MOUSE SUPPORT
section)
or
.Ql {marked}
(alternative form
-.Ql ~ )
+.Ql \[ti] )
to specify the marked pane (see
.Ic select-pane
.Fl m ) .
@@ -936,12 +935,12 @@ arguments are
commands.
This may be a single argument passed to the shell, for example:
.Bd -literal -offset indent
-new-window 'vi ~/.tmux.conf'
+new-window \[aq]vi \[ti]/.tmux.conf\[aq]
.Ed
.Pp
Will run:
.Bd -literal -offset indent
-/bin/sh -c 'vi ~/.tmux.conf'
+/bin/sh -c \[aq]vi \[ti]/.tmux.conf\[aq]
.Ed
.Pp
Additionally, the
@@ -958,7 +957,7 @@ to be given as multiple arguments and executed directly (without
This can avoid issues with shell quoting.
For example:
.Bd -literal -offset indent
-$ tmux new-window vi ~/.tmux.conf
+$ tmux new-window vi \[ti]/.tmux.conf
.Ed
.Pp
Will run
@@ -966,7 +965,7 @@ Will run
directly without invoking the shell.
.Pp
.Ar command
-.Op Ar arguments
+.Op Ar argument ...
refers to a
.Nm
command, either passed with the command and arguments separately, for example:
@@ -993,7 +992,7 @@ set-option -wt:0 monitor-activity on
new-window ; split-window -d
-bind-key R source-file ~/.tmux.conf \e; \e
+bind-key R source-file \[ti]/.tmux.conf \e; \e
display-message "source-file done"
.Ed
.Pp
@@ -1004,7 +1003,7 @@ $ tmux kill-window -t :1
$ tmux new-window \e; split-window -d
-$ tmux new-session -d 'vi ~/.tmux.conf' \e; split-window -d \e; attach
+$ tmux new-session -d \[aq]vi \[ti]/.tmux.conf\[aq] \e; split-window -d \e; attach
.Ed
.Sh CLIENTS AND SESSIONS
The
@@ -1169,13 +1168,17 @@ session.
.Tg lsc
.It Xo Ic list-clients
.Op Fl F Ar format
+.Op Fl f Ar filter
.Op Fl t Ar target-session
.Xc
.D1 Pq alias: Ic lsc
List all clients attached to the server.
-For the meaning of the
.Fl F
-flag, see the
+specifies the format of each line and
+.Fl f
+a filter.
+Only clients for which the filter is true are shown.
+See the
.Sx FORMATS
section.
If
@@ -1278,7 +1281,10 @@ behave like
.Ic attach-session
if
.Ar session-name
-already exists; in this case,
+already exists;
+if
+.Fl A
+is given,
.Fl D
behaves like
.Fl d
@@ -1462,7 +1468,7 @@ requests the clipboard from the client using the
.Xr xterm 1
escape sequence.
If
-Ar target-pane
+.Ar target-pane
is given, the clipboard is sent (in encoded form), otherwise it is stored in a
new paste buffer.
.Pp
@@ -1543,8 +1549,8 @@ show debugging information about jobs and terminals.
.Tg source
.It Xo Ic source-file
.Op Fl Fnqv
-.Ar path
-.Ar ...
+.Op Fl t Ar target-pane
+.Ar path ...
.Xc
.D1 Pq alias: Ic source
Execute commands from one or more files specified by
@@ -1576,8 +1582,9 @@ server, if not already running, without creating any sessions.
.Pp
Note that as by default the
.Nm
-server will exit with no sessions, this is only useful if a session is created in
-.Pa ~/.tmux.conf ,
+server will exit with no sessions, this is only useful if a session is created
+in
+.Pa \[ti]/.tmux.conf ,
.Ic exit-empty
is turned off, or another command is run as part of the same command sequence.
For example:
@@ -1739,88 +1746,267 @@ Key tables may be viewed with the
command.
.Pp
The following commands are supported in copy mode:
-.Bl -column "CommandXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "viXXXXXXXXXX" "emacs" -offset indent
-.It Sy "Command" Ta Sy "vi" Ta Sy "emacs"
-.It Li "append-selection" Ta "" Ta ""
-.It Li "append-selection-and-cancel" Ta "A" Ta ""
-.It Li "back-to-indentation" Ta "^" Ta "M-m"
-.It Li "begin-selection" Ta "Space" Ta "C-Space"
-.It Li "bottom-line" Ta "L" Ta ""
-.It Li "cancel" Ta "q" Ta "Escape"
-.It Li "clear-selection" Ta "Escape" Ta "C-g"
-.It Li "copy-end-of-line [<prefix>]" Ta "" Ta ""
-.It Li "copy-end-of-line-and-cancel [<prefix>]" Ta "" Ta ""
-.It Li "copy-pipe-end-of-line [<command>] [<prefix>]" Ta "" Ta ""
-.It Li "copy-pipe-end-of-line-and-cancel [<command>] [<prefix>]" Ta "D" Ta "C-k"
-.It Li "copy-line [<prefix>]" Ta "" Ta ""
-.It Li "copy-line-and-cancel [<prefix>]" Ta "" Ta ""
-.It Li "copy-pipe-line [<command>] [<prefix>]" Ta "" Ta ""
-.It Li "copy-pipe-line-and-cancel [<command>] [<prefix>]" Ta "" Ta ""
-.It Li "copy-pipe [<command>] [<prefix>]" Ta "" Ta ""
-.It Li "copy-pipe-no-clear [<command>] [<prefix>]" Ta "" Ta ""
-.It Li "copy-pipe-and-cancel [<command>] [<prefix>]" Ta "" Ta ""
-.It Li "copy-selection [<prefix>]" Ta "" Ta ""
-.It Li "copy-selection-no-clear [<prefix>]" Ta "" Ta ""
-.It Li "copy-selection-and-cancel [<prefix>]" Ta "Enter" Ta "M-w"
-.It Li "cursor-down" Ta "j" Ta "Down"
-.It Li "cursor-down-and-cancel" Ta "" Ta ""
-.It Li "cursor-left" Ta "h" Ta "Left"
-.It Li "cursor-right" Ta "l" Ta "Right"
-.It Li "cursor-up" Ta "k" Ta "Up"
-.It Li "end-of-line" Ta "$" Ta "C-e"
-.It Li "goto-line <line>" Ta ":" Ta "g"
-.It Li "halfpage-down" Ta "C-d" Ta "M-Down"
-.It Li "halfpage-down-and-cancel" Ta "" Ta ""
-.It Li "halfpage-up" Ta "C-u" Ta "M-Up"
-.It Li "history-bottom" Ta "G" Ta "M->"
-.It Li "history-top" Ta "g" Ta "M-<"
-.It Li "jump-again" Ta ";" Ta ";"
-.It Li "jump-backward <to>" Ta "F" Ta "F"
-.It Li "jump-forward <to>" Ta "f" Ta "f"
-.It Li "jump-reverse" Ta "," Ta ","
-.It Li "jump-to-backward <to>" Ta "T" Ta ""
-.It Li "jump-to-forward <to>" Ta "t" Ta ""
-.It Li "jump-to-mark" Ta "M-x" Ta "M-x"
-.It Li "middle-line" Ta "M" Ta "M-r"
-.It Li "next-matching-bracket" Ta "%" Ta "M-C-f"
-.It Li "next-paragraph" Ta "}" Ta "M-}"
-.It Li "next-space" Ta "W" Ta ""
-.It Li "next-space-end" Ta "E" Ta ""
-.It Li "next-word" Ta "w" Ta ""
-.It Li "next-word-end" Ta "e" Ta "M-f"
-.It Li "other-end" Ta "o" Ta ""
-.It Li "page-down" Ta "C-f" Ta "PageDown"
-.It Li "page-down-and-cancel" Ta "" Ta ""
-.It Li "page-up" Ta "C-b" Ta "PageUp"
-.It Li "pipe [<command>] [<prefix>]" Ta "" Ta ""
-.It Li "pipe-no-clear [<command>] [<prefix>]" Ta "" Ta ""
-.It Li "pipe-and-cancel [<command>] [<prefix>]" Ta "" Ta ""
-.It Li "previous-matching-bracket" Ta "" Ta "M-C-b"
-.It Li "previous-paragraph" Ta "{" Ta "M-{"
-.It Li "previous-space" Ta "B" Ta ""
-.It Li "previous-word" Ta "b" Ta "M-b"
-.It Li "rectangle-on" Ta "" Ta ""
-.It Li "rectangle-off" Ta "" Ta ""
-.It Li "rectangle-toggle" Ta "v" Ta "R"
-.It Li "refresh-from-pane" Ta "r" Ta "r"
-.It Li "scroll-down" Ta "C-e" Ta "C-Down"
-.It Li "scroll-down-and-cancel" Ta "" Ta ""
-.It Li "scroll-up" Ta "C-y" Ta "C-Up"
-.It Li "search-again" Ta "n" Ta "n"
-.It Li "search-backward <for>" Ta "?" Ta ""
-.It Li "search-backward-incremental <for>" Ta "" Ta "C-r"
-.It Li "search-backward-text <for>" Ta "" Ta ""
-.It Li "search-forward <for>" Ta "/" Ta ""
-.It Li "search-forward-incremental <for>" Ta "" Ta "C-s"
-.It Li "search-forward-text <for>" Ta "" Ta ""
-.It Li "search-reverse" Ta "N" Ta "N"
-.It Li "select-line" Ta "V" Ta ""
-.It Li "select-word" Ta "" Ta ""
-.It Li "set-mark" Ta "X" Ta "X"
-.It Li "start-of-line" Ta "0" Ta "C-a"
-.It Li "stop-selection" Ta "" Ta ""
-.It Li "toggle-position" Ta "P" Ta "P"
-.It Li "top-line" Ta "H" Ta "M-R"
+.Bl -tag -width Ds
+.It Xo
+.Ic append-selection
+.Xc
+Append the selection to the top paste buffer.
+.It Xo
+.Ic append-selection-and-cancel
+(vi: A)
+.Xc
+Append the selection to the top paste buffer and exit copy mode.
+.It Xo
+.Ic back-to-indentation
+(vi: ^)
+(emacs: M-m)
+.Xc
+Move the cursor back to the indentation.
+.It Xo
+.Ic begin-selection
+(vi: Space)
+(emacs: C-Space)
+.Xc
+Begin selection.
+.It Xo
+.Ic bottom-line
+(vi: L)
+.Xc
+Move to the bottom line.
+.It Xo
+.Ic cancel
+(vi: q)
+(emacs: Escape)
+.Xc
+Exit copy mode.
+.It Xo
+.Ic clear-selection
+(vi: Escape)
+(emacs: C-g)
+.Xc
+Clear the current selection.
+.It Xo
+.Ic copy-end-of-line
+.Op Ar prefix
+.Xc
+Copy from the cursor position to the end of the line.
+.Ar prefix
+is used to name the new paste buffer.
+.It Xo
+.Ic copy-end-of-line-and-cancel
+.Op Ar prefix
+.Xc
+Copy from the cursor position and exit copy mode.
+.It Xo
+.Ic copy-line
+.Op Ar prefix
+.Xc
+Copy the entire line.
+.It Xo
+.Ic copy-line-and-cancel
+.Op Ar prefix
+.Xc
+Copy the entire line and exit copy mode.
+.It Xo
+.Ic copy-selection
+.Op Ar prefix
+.Xc
+Copies the current selection.
+.It Xo
+.Ic copy-selection-and-cancel
+.Op Ar prefix
+(vi: Enter)
+(emacs: M-w)
+.Xc
+Copy the current selection and exit copy mode.
+.It Xo
+.Ic cursor-down
+(vi: j)
+(emacs: Down)
+.Xc
+Move the cursor down.
+.It Xo
+.Ic cursor-left
+(vi: h)
+(emacs: Left)
+.Xc
+Move the cursor left.
+.It Xo
+.Ic cursor-right
+(vi: l)
+(emacs: Right)
+.Xc
+Move the cursor right.
+.It Xo
+.Ic cursor-up
+(vi: k)
+(emacs: Up)
+.Xc
+Move the cursor up.
+.It Xo
+.Ic end-of-line
+(vi: $)
+(emacs: C-e)
+.Xc
+Move the cursor to the end of the line.
+.It Xo
+.Ic goto-line
+.Ar line
+(vi: :)
+(emacs: g)
+.Xc
+Move the cursor to a specific line.
+.It Xo
+.Ic history-bottom
+(vi: G)
+(emacs: M->)
+.Xc
+Scroll to the bottom of the history.
+.It Xo
+.Ic history-top
+(vi: g)
+(emacs: M-<)
+.Xc
+Scroll to the top of the history.
+.It Xo
+.Ic jump-again
+(vi: ;)
+(emacs: ;)
+.Xc
+Repeat the last jump.
+.It Xo
+.Ic jump-backward
+.Ar to
+(vi: F)
+(emacs: F)
+.Xc
+Jump backwards to the specified text.
+.It Xo
+.Ic jump-forward
+.Ar to
+(vi: f)
+(emacs: f)
+.Xc
+Jump forward to the specified text.
+.It Xo
+.Ic jump-to-mark
+(vi: M-x)
+(emacs: M-x)
+.Xc
+Jump to the last mark.
+.It Xo
+.Ic middle-line
+(vi: M)
+(emacs: M-r)
+.Xc
+Move to the middle line.
+.It Xo
+.Ic next-matching-bracket
+(vi: %)
+(emacs: M-C-f)
+.Xc
+Move to the next matching bracket.
+.It Xo
+.Ic next-paragraph
+(vi: })
+(emacs: M-})
+.Xc
+Move to the next paragraph.
+.It Xo
+.Ic next-prompt
+.Op Fl o
+.Xc
+Move to the next prompt.
+.It Xo
+.Ic next-word
+(vi: w)
+.Xc
+Move to the next word.
+.It Xo
+.Ic page-down
+(vi: C-f)
+(emacs: PageDown)
+.Xc
+Scroll down by one page.
+.It Xo
+.Ic page-up
+(vi: C-b)
+(emacs: PageUp)
+.Xc
+Scroll up by one page.
+.It Xo
+.Ic previous-matching-bracket
+(emacs: M-C-b)
+.Xc
+Move to the previous matching bracket.
+.It Xo
+.Ic previous-paragraph
+(vi: {)
+(emacs: M-{)
+.Xc
+Move to the previous paragraph.
+.It Xo
+.Ic previous-prompt
+.Op Fl o
+.Xc
+Move to the previous prompt.
+.It Xo
+.Ic previous-word
+(vi: b)
+(emacs: M-b)
+.Xc
+Move to the previous word.
+.It Xo
+.Ic rectangle-toggle
+(vi: v)
+(emacs: R)
+.Xc
+Toggle rectangle selection mode.
+.It Xo
+.Ic refresh-from-pane
+(vi: r)
+(emacs: r)
+.Xc
+Refresh the content from the pane.
+.It Xo
+.Ic search-again
+(vi: n)
+(emacs: n)
+.Xc
+Repeat the last search.
+.It Xo
+.Ic search-backward
+.Ar text
+(vi: ?)
+.Xc
+Search backwards for the specified text.
+.It Xo
+.Ic search-forward
+.Ar text
+(vi: /)
+.Xc
+Search forward for the specified text.
+.It Xo
+.Ic select-line
+(vi: V)
+.Xc
+Select the current line.
+.It Xo
+.Ic select-word
+.Xc
+Select the current word.
+.It Xo
+.Ic start-of-line
+(vi: 0)
+(emacs: C-a)
+.Xc
+Move the cursor to the start of the line.
+.It Xo
+.Ic top-line
+(vi: H)
+(emacs: M-R)
+.Xc
+Move to the top line.
.El
.Pp
The search commands come in several varieties:
@@ -1843,6 +2029,19 @@ repeats the last search and
does the same but reverses the direction (forward becomes backward and backward
becomes forward).
.Pp
+The
+.Ql next-prompt
+and
+.Ql previous-prompt
+move between shell prompts, but require the shell to emit an escape sequence
+(\e033]133;A\e033\e\e) to tell
+.Nm
+where the prompts are located; if the shell does not do this, these commands
+will do nothing.
+The
+.Fl o
+flag jumps to the beginning of the command output instead of the shell prompt.
+.Pp
Copy commands may take an optional buffer prefix argument which is used
to generate the buffer name (the default is
.Ql buffer
@@ -1928,7 +2127,8 @@ bind PageUp copy-mode -eu
.Ed
.El
.Pp
-A number of preset arrangements of panes are available, these are called layouts.
+A number of preset arrangements of panes are available, these are called
+layouts.
These may be selected with the
.Ic select-layout
command or cycled with
@@ -1974,7 +2174,7 @@ For example:
$ tmux list-windows
0: ksh [159x48]
layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0}
-$ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0}
+$ tmux select-layout \[aq]bb62,159x48,0,0{79x48,0,0,79x48,80,0}\[aq]
.Ed
.Pp
.Nm
@@ -2015,7 +2215,7 @@ but a different format may be specified with
.Fl F .
.Tg capturep
.It Xo Ic capture-pane
-.Op Fl aepPqCJN
+.Op Fl aAepPqCJN
.Op Fl b Ar buffer-name
.Op Fl E Ar end-line
.Op Fl S Ar start-line
@@ -2040,10 +2240,15 @@ is given, the output includes escape sequences for text and background
attributes.
.Fl C
also escapes non-printable characters as octal \exxx.
+.Fl T
+ignores trailing positions that do not contain a character.
.Fl N
preserves trailing spaces at each line's end and
.Fl J
-preserves trailing spaces and joins any wrapped lines.
+preserves trailing spaces and joins any wrapped lines;
+.Fl J
+implies
+.Fl T .
.Fl P
captures only any output that the pane has received that is the beginning of an
as-yet incomplete escape sequence.
@@ -2109,15 +2314,17 @@ is replaced by the client name in
and the result executed as a command.
If
.Ar template
-is not given, "detach-client -t '%%'" is used.
+is not given, "detach-client -t \[aq]%%\[aq]" is used.
.Pp
.Fl O
specifies the initial sort field: one of
.Ql name ,
.Ql size ,
-.Ql creation ,
+.Ql creation
+(time),
or
-.Ql activity .
+.Ql activity
+(time).
.Fl r
reverses the sort order.
.Fl f
@@ -2192,14 +2399,15 @@ are replaced by the target in
and the result executed as a command.
If
.Ar template
-is not given, "switch-client -t '%%'" is used.
+is not given, "switch-client -t \[aq]%%\[aq]" is used.
.Pp
.Fl O
specifies the initial sort field: one of
.Ql index ,
.Ql name ,
or
-.Ql time .
+.Ql time
+(activity).
.Fl r
reverses the sort order.
.Fl f
@@ -2303,7 +2511,7 @@ to be executed as a command with
substituted by the pane ID.
The default
.Ar template
-is "select-pane -t '%%'".
+is "select-pane -t \[aq]%%\[aq]".
With
.Fl b ,
other commands are not blocked from running until the indicator is closed.
@@ -2665,7 +2873,7 @@ The
option only opens a new pipe if no previous pipe exists, allowing a pipe to
be toggled with a single key, for example:
.Bd -literal -offset indent
-bind-key C-p pipe-pane -o 'cat >>~/output.#I-#P'
+bind-key C-p pipe-pane -o \[aq]cat >>\[ti]/output.#I-#P\[aq]
.Ed
.Tg prevl
.It Xo Ic previous-layout
@@ -2969,7 +3177,7 @@ zooms if the window is not zoomed, or keeps it zoomed if already zoomed.
.Pp
An empty
.Ar shell-command
-('') will create a pane with no command running in it.
+(\[aq]\[aq]) will create a pane with no command running in it.
Output can be sent to such a pane with the
.Ic display-message
command.
@@ -3096,11 +3304,11 @@ and
Note that to bind the
.Ql \&"
or
-.Ql '
+.Ql \[aq]
keys, quotation marks are necessary, for example:
.Bd -literal -offset indent
-bind-key '"' split-window
-bind-key "'" new-window
+bind-key \[aq]"\[aq] split-window
+bind-key "\[aq]" new-window
.Ed
.Pp
A command bound to the
@@ -3114,7 +3322,7 @@ Commands related to key bindings are as follows:
.Op Fl nr
.Op Fl N Ar note
.Op Fl T Ar key-table
-.Ar key command Op Ar arguments
+.Ar key command Op Ar argument ...
.Xc
.D1 Pq alias: Ic bind
Bind key
@@ -3206,13 +3414,14 @@ lists only the first matching key.
lists the command for keys that do not have a note rather than skipping them.
.Tg send
.It Xo Ic send-keys
-.Op Fl FHlMRX
+.Op Fl FHKlMRX
+.Op Fl c Ar target-client
.Op Fl N Ar repeat-count
.Op Fl t Ar target-pane
-.Ar key Ar ...
+.Ar key ...
.Xc
.D1 Pq alias: Ic send
-Send a key or keys to a window.
+Send a key or keys to a window or client.
Each argument
.Ar key
is the name of the key (such as
@@ -3221,6 +3430,12 @@ or
.Ql NPage )
to send; if the string is not recognised as a key, it is sent as a series of
characters.
+If
+.Fl K
+is given, keys are sent to
+.Ar target-client ,
+so they are looked up in the client's key table, rather than to
+.Ar target-pane .
All arguments are sent sequentially from first to last.
If no keys are given and the command is bound to a key, then that key is used.
.Pp
@@ -3489,7 +3704,7 @@ it is replaced with
.Ar value .
For example, after:
.Pp
-.Dl set -s command-alias[100] zoom='resize-pane -Z'
+.Dl set -s command-alias[100] zoom=\[aq]resize-pane -Z\[aq]
.Pp
Using:
.Pp
@@ -3659,6 +3874,14 @@ Allows setting the cursor style.
Supports extended keys.
.It focus
Supports focus reporting.
+.It hyperlinks
+Supports OSC 8 hyperlinks.
+.It ignorefkeys
+Ignore function keys from
+.Xr terminfo 5
+and use the
+.Nm
+internal set only.
.It margins
Supports DECSLRM margins.
.It mouse
@@ -3673,6 +3896,8 @@ Supports the overline SGR attribute.
Supports the DECFRA rectangle fill escape sequence.
.It RGB
Supports RGB colour with the SGR escape sequences.
+.It sixel
+Supports SIXEL graphics.
.It strikethrough
Supports the strikethrough SGR escape sequence.
.It sync
@@ -3717,7 +3942,7 @@ and so on.
.Pp
For example:
.Bd -literal -offset indent
-set -s user-keys[0] "\ee[5;30012~"
+set -s user-keys[0] "\ee[5;30012\[ti]"
bind User0 resize-pane -L 3
.Ed
.El
@@ -3801,21 +4026,40 @@ The value is the width and height separated by an
character.
The default is 80x24.
.It Xo Ic destroy-unattached
-.Op Ic on | off
+.Op Ic off | on | keep-last | keep-group
.Xc
-If enabled and the session is no longer attached to any clients, it is
-destroyed.
+If
+.Ic on ,
+destroy the session after the last client has detached.
+If
+.Ic off
+(the default), leave the session orphaned.
+If
+.Ic keep-last ,
+destroy the session only if it is in a group and has other sessions in that group.
+If
+.Ic keep-group ,
+destroy the session unless it is in a group and is the only session in that group.
.It Xo Ic detach-on-destroy
-.Op Ic off | on | no-detached
+.Op Ic off | on | no-detached | previous | next
.Xc
-If on (the default), the client is detached when the session it is attached to
+If
+.Ic on
+(the default), the client is detached when the session it is attached to
is destroyed.
-If off, the client is switched to the most recently active of the remaining
+If
+.Ic off ,
+the client is switched to the most recently active of the remaining
sessions.
If
.Ic no-detached ,
the client is detached only if there are no detached sessions; if detached
sessions exist, the client is switched to the most recently active.
+If
+.Ic previous
+or
+.Ic next ,
+the client is switched to the previous or next session in alphabetical order.
.It Ic display-panes-active-colour Ar colour
Set the colour used by the
.Ic display-panes
@@ -3856,6 +4100,33 @@ The default is to run
.Xr lock 1
with
.Fl np .
+.It Ic menu-style Ar style
+Set the menu style.
+See the
+.Sx STYLES
+section on how to specify
+.Ar style .
+Attributes are ignored.
+.It Ic menu-selected-style Ar style
+Set the selected menu item style.
+See the
+.Sx STYLES
+section on how to specify
+.Ar style .
+Attributes are ignored.
+.It Ic menu-border-style Ar style
+Set the menu border style.
+See the
+.Sx STYLES
+section on how to specify
+.Ar style .
+Attributes are ignored.
+.It Ic menu-border-lines Ar type
+Set the type of characters used for drawing menu borders.
+See
+.Ic popup-border-lines
+for possible values for
+.Ar border-lines .
.It Ic message-command-style Ar style
Set status line message command style.
This is used for the command prompt with
@@ -3866,6 +4137,10 @@ For how to specify
see the
.Sx STYLES
section.
+.It Xo Ic message-line
+.Op Ic 0 | 1 | 2 | 3 | 4
+.Xc
+Set line on which status line messages and the command prompt are shown.
.It Ic message-style Ar style
Set status line message style.
This is used for messages and for the command prompt.
@@ -4319,20 +4594,18 @@ Attributes are ignored.
.Pp
.It Ic popup-style Ar style
Set the popup style.
-For how to specify
-.Ar style ,
-see the
+See the
.Sx STYLES
-section.
+section on how to specify
+.Ar style .
Attributes are ignored.
.Pp
.It Ic popup-border-style Ar style
Set the popup border style.
-For how to specify
-.Ar style ,
-see the
+See the
.Sx STYLES
-section.
+section on how to specify
+.Ar style .
Attributes are ignored.
.Pp
.It Ic popup-border-lines Ar type
@@ -4455,11 +4728,17 @@ Available pane options are:
.Pp
.Bl -tag -width Ds -compact
.It Xo Ic allow-passthrough
-.Op Ic on | off
+.Op Ic on | off | all
.Xc
Allow programs in the pane to bypass
.Nm
using a terminal escape sequence (\eePtmux;...\ee\e\e).
+If set to
+.Ic on ,
+passthrough sequences will be allowed only if the pane is visible.
+If set to
+.Ic all ,
+they will be allowed even if the pane is invisible.
.Pp
.It Xo Ic allow-rename
.Op Ic on | off
@@ -4559,7 +4838,8 @@ hook and there are a number of hooks not associated with commands.
.Pp
Hooks are stored as array options, members of the array are executed in
order when the hook is triggered.
-Like options different hooks may be global or belong to a session, window or pane.
+Like options different hooks may be global or belong to a session, window or
+pane.
Hooks may be configured with the
.Ic set-hook
or
@@ -4571,8 +4851,8 @@ or
.Fl H .
The following two commands are equivalent:
.Bd -literal -offset indent.
-set-hook -g pane-mode-changed[42] 'set -g status-left-style bg=red'
-set-option -g pane-mode-changed[42] 'set -g status-left-style bg=red'
+set-hook -g pane-mode-changed[42] \[aq]set -g status-left-style bg=red\[aq]
+set-option -g pane-mode-changed[42] \[aq]set -g status-left-style bg=red\[aq]
.Ed
.Pp
Setting a hook without specifying an array index clears the hook and sets the
@@ -4742,7 +5022,8 @@ or
.Ar target-pane
in commands bound to mouse key bindings.
It resolves to the window or pane over which the mouse event took place
-(for example, the window in the status line over which button 1 was released for a
+(for example, the window in the status line over which button 1 was released
+for a
.Ql MouseUp1Status
binding, or the pane over which the wheel was scrolled for a
.Ql WheelDownPane
@@ -4882,13 +5163,16 @@ ignores case.
For example:
.Ql #{C/r:^Start}
.Pp
-Numeric operators may be performed by prefixing two comma-separated alternatives with an
+Numeric operators may be performed by prefixing two comma-separated alternatives
+with an
.Ql e
and an operator.
An optional
.Ql f
-flag may be given after the operator to use floating point numbers, otherwise integers are used.
-This may be followed by a number giving the number of decimal places to use for the result.
+flag may be given after the operator to use floating point numbers, otherwise
+integers are used.
+This may be followed by a number giving the number of decimal places to use for
+the result.
The available operators are:
addition
.Ql + ,
@@ -5018,10 +5302,11 @@ but also expands
.Xr strftime 3
specifiers.
.Ql S:\& ,
-.Ql W:\&
-or
+.Ql W:\& ,
.Ql P:\&
-will loop over each session, window or pane and insert the format once
+or
+.Ql L:\&
+will loop over each session, window, pane or client and insert the format once
for each.
For windows and panes, two comma-separated formats may be given:
the second is used for the current window or active pane.
@@ -5048,7 +5333,8 @@ will substitute
with
.Ql bar
throughout.
-The first argument may be an extended regular expression and a final argument may be
+The first argument may be an extended regular expression and a final argument
+may be
.Ql i
to ignore case, for example
.Ql s/a(.)/\e1x/i:\&
@@ -5056,6 +5342,15 @@ would change
.Ql abABab
into
.Ql bxBxbx .
+A different delimiter character may also be used, to avoid collisions with
+literal slashes in the pattern.
+For example,
+.Ql s|foo/|bar/|:\&
+will substitute
+.Ql foo/
+with
+.Ql bar/
+throughout.
.Pp
In addition, the last line of a shell command's output may be inserted using
.Ql #() .
@@ -5066,10 +5361,10 @@ When constructing formats,
.Nm
does not wait for
.Ql #()
-commands to finish; instead, the previous result from running the same command is used,
-or a placeholder if the command has not been run before.
-If the command hasn't exited, the most recent line of output will be used, but the status
-line will not be updated more than once a second.
+commands to finish; instead, the previous result from running the same command
+is used, or a placeholder if the command has not been run before.
+If the command hasn't exited, the most recent line of output will be used, but
+the status line will not be updated more than once a second.
Commands are executed using
.Pa /bin/sh
and with the
@@ -5155,9 +5450,12 @@ The following variables are available, where appropriate:
.It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag"
.It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag"
.It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag"
+.It Li "mouse_hyperlink" Ta "" Ta "Hyperlink under mouse, if any"
.It Li "mouse_line" Ta "" Ta "Line under mouse, if any"
.It Li "mouse_sgr_flag" Ta "" Ta "Pane mouse SGR flag"
.It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag"
+.It Li "mouse_status_line" Ta "" Ta "Status line on which mouse event took place"
+.It Li "mouse_status_range" Ta "" Ta "Range type or argument of mouse event on status line"
.It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag"
.It Li "mouse_word" Ta "" Ta "Word under mouse, if any"
.It Li "mouse_x" Ta "" Ta "Mouse X position, if any"
@@ -5201,6 +5499,7 @@ The following variables are available, where appropriate:
.It Li "pane_title" Ta "#T" Ta "Title of pane (can be set by application)"
.It Li "pane_top" Ta "" Ta "Top of pane"
.It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane"
+.It Li "pane_unseen_changes" Ta "" Ta "1 if there were changes in pane while in mode"
.It Li "pane_width" Ta "" Ta "Width of pane"
.It Li "pid" Ta "" Ta "Server PID"
.It Li "rectangle_toggle" Ta "" Ta "1 if rectangle selection is activated"
@@ -5215,6 +5514,7 @@ The following variables are available, where appropriate:
.It Li "selection_present" Ta "" Ta "1 if selection started in copy mode"
.It Li "selection_start_x" Ta "" Ta "X position of the start of the selection"
.It Li "selection_start_y" Ta "" Ta "Y position of the start of the selection"
+.It Li "server_sessions" Ta "" Ta "Number of sessions"
.It Li "session_activity" Ta "" Ta "Time of session last activity"
.It Li "session_alerts" Ta "" Ta "List of window indexes with alerts"
.It Li "session_attached" Ta "" Ta "Number of clients session is attached to"
@@ -5323,6 +5623,8 @@ for the terminal default colour; or a hexadecimal RGB string such as
.Ql #ffffff .
.It Ic bg=colour
Set the background colour.
+.It Ic us=colour
+Set the underscore colour.
.It Ic none
Set no attributes (turn off any active attributes).
.It Xo Ic acs ,
@@ -5369,8 +5671,8 @@ option:
.Ic list=on
marks the start of the list;
.Ic list=focus
-is the part of the list that should be kept in focus if the entire list won't fit
-in the available space (typically the current window);
+is the part of the list that should be kept in focus if the entire list won't
+fit in the available space (typically the current window);
.Ic list=left-marker
and
.Ic list=right-marker
@@ -5392,26 +5694,56 @@ Only one default may be pushed (each
replaces the previous saved default).
.It Xo Ic range=left ,
.Ic range=right ,
+.Ic range=session|X ,
.Ic range=window|X ,
+.Ic range=pane|X ,
+.Ic range=user|X ,
.Ic norange
.Xc
-Mark a range in the
+Mark a range for mouse events in the
.Ic status-format
option.
+When a mouse event occurs in the
.Ic range=left
-and
+or
.Ic range=right
-are the text used for the
+range, the
.Ql StatusLeft
and
.Ql StatusRight
-mouse keys.
+key bindings are triggered.
+.Pp
+.Ic range=session|X ,
.Ic range=window|X
-is the range for a window passed to the
+and
+.Ic range=pane|X
+are ranges for a session, window or pane.
+These trigger the
.Ql Status
-mouse key, where
+mouse key with the target session, window or pane given by the
.Ql X
-is a window index.
+argument.
+.Ql X
+is a session ID, window index in the current session or a pane ID.
+For these, the
+.Ic mouse_status_range
+format variable will be set to
+.Ql session ,
+.Ql window
+or
+.Ql pane .
+.Pp
+.Ic range=user|X
+is a user-defined range; it triggers the
+.Ql Status
+mouse key.
+The argument
+.Ql X
+will be available in the
+.Ic mouse_status_range
+format variable.
+.Ql X
+must be at most 15 bytes in length.
.El
.Pp
Examples are:
@@ -5459,7 +5791,7 @@ An escape sequence (if the
.Ic allow-rename
option is turned on):
.Bd -literal -offset indent
-$ printf '\e033kWINDOW_NAME\e033\e\e'
+$ printf \[aq]\e033kWINDOW_NAME\e033\e\e\[aq]
.Ed
.It
Automatic renaming, which sets the name to the active command in the window's
@@ -5472,7 +5804,7 @@ option.
When a pane is first created, its title is the hostname.
A pane's title can be set via the title setting escape sequence, for example:
.Bd -literal -offset indent
-$ printf '\e033]2;My Title\e033\e\e'
+$ printf \[aq]\e033]2;My Title\e033\e\e\[aq]
.Ed
.Pp
It can also be modified with the
@@ -5597,7 +5929,7 @@ The flag is one of the following symbols appended to the window name:
.It Li "-" Ta "Marks the last window (previously selected)."
.It Li "#" Ta "Window activity is monitored and activity has been detected."
.It Li "\&!" Ta "Window bells are monitored and a bell has occurred in the window."
-.It Li "~" Ta "The window has been silent for the monitor-silence interval."
+.It Li "\[ti]" Ta "The window has been silent for the monitor-silence interval."
.It Li "M" Ta "The window contains the marked pane."
.It Li "Z" Ta "The window's active pane is zoomed."
.El
@@ -5747,7 +6079,8 @@ the prompt is shown in the background and the invoking client does not exit
until it is dismissed.
.Tg confirm
.It Xo Ic confirm-before
-.Op Fl b
+.Op Fl by
+.Op Fl c Ar confirm-key
.Op Fl p Ar prompt
.Op Fl t Ar target-client
.Ar command
@@ -5768,18 +6101,30 @@ With
.Fl b ,
the prompt is shown in the background and the invoking client does not exit
until it is dismissed.
+.Fl y
+changes the default behaviour (if Enter alone is pressed) of the prompt to
+run the command.
+.Fl c
+changes the confirmation key to
+.Ar confirm-key ;
+the default is
+.Ql y .
.Tg menu
.It Xo Ic display-menu
.Op Fl O
+.Op Fl b Ar border-lines
.Op Fl c Ar target-client
+.Op Fl C Ar starting-choice
+.Op Fl H Ar selected-style
+.Op Fl s Ar style
+.Op Fl S Ar border-style
.Op Fl t Ar target-pane
.Op Fl T Ar title
.Op Fl x Ar position
.Op Fl y Ar position
.Ar name
.Ar key
-.Ar command
-.Ar ...
+.Ar command Op Ar argument ...
.Xc
.D1 Pq alias: Ic menu
Display a menu on
@@ -5800,10 +6145,31 @@ may not be chosen.
The name may be empty for a separator line, in which case both the key and
command should be omitted.
.Pp
+.Fl b
+sets the type of characters used for drawing menu borders.
+See
+.Ic popup-border-lines
+for possible values for
+.Ar border-lines .
+.Pp
+.Fl H
+sets the style for the selected menu item (see
+.Sx STYLES ) .
+.Pp
+.Fl s
+sets the style for the menu and
+.Fl S
+sets the style for the menu border (see
+.Sx STYLES ) .
+.Pp
.Fl T
is a format for the menu title (see
.Sx FORMATS ) .
.Pp
+.Fl C
+sets the menu item selected by default, if the menu is not bound to a mouse key
+binding.
+.Pp
.Fl x
and
.Fl y
@@ -5862,7 +6228,7 @@ The following keys are also available:
.El
.Tg display
.It Xo Ic display-message
-.Op Fl aINpv
+.Op Fl aIlNpv
.Op Fl c Ar target-client
.Op Fl d Ar delay
.Op Fl t Ar target-pane
@@ -5884,7 +6250,12 @@ is not given, the
option is used; a delay of zero waits for a key press.
.Ql N
ignores key presses and closes only after the delay expires.
-The format of
+If
+.Fl l
+is given,
+.Ar message
+is printed unchanged.
+Otherwise, the format of
.Ar message
is described in the
.Sx FORMATS
@@ -5910,8 +6281,8 @@ forwards any input read from stdin to the empty pane given by
.Op Fl d Ar start-directory
.Op Fl e Ar environment
.Op Fl h Ar height
-.Op Fl s Ar style
-.Op Fl S Ar border-style
+.Op Fl s Ar border-style
+.Op Fl S Ar style
.Op Fl t Ar target-pane
.Op Fl T Ar title
.Op Fl w Ar width
@@ -5954,7 +6325,7 @@ If omitted, half of the terminal size is used.
does not surround the popup by a border.
.Pp
.Fl b
-sets the type of border line for the popup.
+sets the type of characters used for drawing popup borders.
When
.Fl B
is specified, the
@@ -5968,12 +6339,8 @@ for possible values for
.Fl s
sets the style for the popup and
.Fl S
-sets the style for the popup border.
-For how to specify
-.Ar style ,
-see the
-.Sx STYLES
-section.
+sets the style for the popup border (see
+.Sx STYLES ) .
.Pp
.Fl e
takes the form
@@ -6097,11 +6464,12 @@ is replaced by the buffer name in
and the result executed as a command.
If
.Ar template
-is not given, "paste-buffer -b '%%'" is used.
+is not given, "paste-buffer -b \[aq]%%\[aq]" is used.
.Pp
.Fl O
specifies the initial sort field: one of
-.Ql time ,
+.Ql time
+(creation),
.Ql name
or
.Ql size .
@@ -6119,9 +6487,14 @@ a format for each shortcut key; both are evaluated once for each line.
starts without the preview.
This command works only if at least one client is attached.
.Tg clearhist
-.It Ic clear-history Op Fl t Ar target-pane
+.It Xo Ic clear-history
+.Op Fl H
+.Op Fl t Ar target-pane
+.Xc
.D1 Pq alias: Ic clearhist
Remove and free the history for the specified pane.
+.Fl H
+also removes all hyperlinks.
.Tg deleteb
.It Ic delete-buffer Op Fl b Ar buffer-name
.D1 Pq alias: Ic deleteb
@@ -6277,6 +6650,7 @@ option.
.Tg run
.It Xo Ic run-shell
.Op Fl bC
+.Op Fl c Ar start-directory
.Op Fl d Ar delay
.Op Fl t Ar target-pane
.Op Ar shell-command
@@ -6304,6 +6678,10 @@ waits for
.Ar delay
seconds before starting the command.
If
+.Fl c
+is given, the current working directory is set to
+.Ar start-directory .
+If
.Fl C
is not given, any output to stdout is displayed in view mode (in the pane
specified by
@@ -6385,7 +6763,7 @@ If set, a sequence such as this may be used
to change the cursor colour from inside
.Nm :
.Bd -literal -offset indent
-$ printf '\e033]12;red\e033\e\e'
+$ printf \[aq]\e033]12;red\e033\e\e\[aq]
.Ed
.Pp
The colour is an
@@ -6409,6 +6787,12 @@ Disable and enable focus reporting.
These are set automatically if the
.Em XT
capability is present.
+.It Em \&Hls
+Set or clear a hyperlink annotation.
+.It Em \&Nobr
+Tell
+.Nm
+that the terminal does not use bright colors for bold display.
.It Em \&Rect
Tell
.Nm
@@ -6420,16 +6804,22 @@ Set a styled underscore.
The single parameter is one of: 0 for no underscore, 1 for normal
underscore, 2 for double underscore, 3 for curly underscore, 4 for dotted
underscore and 5 for dashed underscore.
-.It Em \&Setulc , \&ol
+.It Em \&Setulc , \&Setulc1, \&ol
Set the underscore colour or reset to the default.
-The argument is (red * 65536) + (green * 256) + blue where each is between 0
+.Em Setulc
+is for RGB colours and
+.Em Setulc1
+for ANSI or 256 colours.
+The
+.Em Setulc
+argument is (red * 65536) + (green * 256) + blue where each is between 0
and 255.
.It Em \&Ss , Se
Set or reset the cursor style.
If set, a sequence such as this may be used
to change the cursor to an underline:
.Bd -literal -offset indent
-$ printf '\e033[4 q'
+$ printf \[aq]\e033[4 q\[aq]
.Ed
.Pp
If
@@ -6440,6 +6830,8 @@ Set the opening sequence for the working directory notification.
The sequence is terminated using the standard
.Em fsl
capability.
+.It Em \&Sxl
+Indicates that the terminal supports SIXEL.
.It Em \&Sync
Start (parameter is 1) or end (parameter is 2) a synchronized update.
.It Em \&Tc
@@ -6498,8 +6890,8 @@ and matching
.Em %end
or
.Em %error
-have three arguments: an integer time (as seconds from epoch), command number and
-flags (currently not used).
+have three arguments: an integer time (as seconds from epoch), command number
+and flags (currently not used).
For example:
.Bd -literal -offset indent
%begin 1363006971 2 1
@@ -6526,6 +6918,8 @@ The client is now attached to the session with ID
.Ar session-id ,
which is named
.Ar name .
+.It Ic %config-error Ar error
+An error has happened in a configuration file.
.It Ic %continue Ar pane-id
The pane has been continued after being paused (if the
.Ar pause-after
@@ -6547,11 +6941,17 @@ sent when the
.Ar pause-after
flag is set.
.Ar age
-is the time in milliseconds for which tmux had buffered the output before it was sent.
+is the time in milliseconds for which tmux had buffered the output before it
+was sent.
Any subsequent arguments up until a single
.Ql \&:
are for future use and should be ignored.
-.It Ic %layout-change Ar window-id Ar window-layout Ar window-visible-layout Ar window-flags
+.It Xo Ic %layout-change
+.Ar window-id
+.Ar window-layout
+.Ar window-visible-layout
+.Ar window-flags
+.Xc
The layout of a window with ID
.Ar window-id
changed.
@@ -6561,6 +6961,10 @@ The window's visible layout is
.Ar window-visible-layout
and the window flags are
.Ar window-flags .
+.It Ic %message Ar message
+A message sent with the
+.Ic display-message
+command.
.It Ic %output Ar pane-id Ar value
A window pane produced output.
.Ar value
@@ -6569,6 +6973,14 @@ escapes non-printable characters and backslash as octal \\xxx.
The pane with ID
.Ar pane-id
has changed mode.
+.It Ic %paste-buffer-changed Ar name
+Paste buffer
+.Ar name
+has been changed.
+.It Ic %paste-buffer-deleted Ar name
+Paste buffer
+.Ar name
+has been deleted.
.It Ic %pause Ar pane-id
The pane has been paused (if the
.Ar pause-after
@@ -6726,9 +7138,9 @@ options.
.El
.Sh FILES
.Bl -tag -width "@SYSCONFDIR@/tmux.confXXX" -compact
-.It Pa ~/.tmux.conf
+.It Pa \[ti]/.tmux.conf
.It Pa $XDG_CONFIG_HOME/tmux/tmux.conf
-.It Pa ~/.config/tmux/tmux.conf
+.It Pa \[ti]/.config/tmux/tmux.conf
Default
.Nm
configuration file.
@@ -6794,7 +7206,7 @@ to exit from it.
Commands to be run when the
.Nm
server is started may be placed in the
-.Pa ~/.tmux.conf
+.Pa \[ti]/.tmux.conf
configuration file.
Common examples include:
.Pp
@@ -6821,8 +7233,8 @@ set-option -g lock-after-time 1800
Creating new key bindings:
.Bd -literal -offset indent
bind-key b set-option status
-bind-key / command-prompt "split-window 'exec man %%'"
-bind-key S command-prompt "new-window -n %1 'ssh %1'"
+bind-key / command-prompt "split-window \[aq]exec man %%\[aq]"
+bind-key S command-prompt "new-window -n %1 \[aq]ssh %1\[aq]"
.Ed
.Sh SEE ALSO
.Xr pty 4
diff --git a/tmux.c b/tmux.c
index b9f2be3..a01ed42 100644
--- a/tmux.c
+++ b/tmux.c
@@ -53,7 +53,7 @@ static __dead void
usage(void)
{
fprintf(stderr,
- "usage: %s [-2CDlNuvV] [-c shell-command] [-f file] [-L socket-name]\n"
+ "usage: %s [-2CDlNuVv] [-c shell-command] [-f file] [-L socket-name]\n"
" [-S socket-path] [-T features] [command [flags]]\n",
getprogname());
exit(1);
@@ -391,7 +391,7 @@ main(int argc, char **argv)
cfg_quiet = 0;
break;
case 'V':
- printf("%s %s\n", getprogname(), getversion());
+ printf("tmux %s\n", getversion());
exit(0);
case 'l':
flags |= CLIENT_LOGIN;
diff --git a/tmux.h b/tmux.h
index 53084b8..a50adbf 100644
--- a/tmux.h
+++ b/tmux.h
@@ -26,6 +26,7 @@
#include <stdarg.h>
#include <stdio.h>
#include <termios.h>
+#include <wchar.h>
#ifdef HAVE_UTEMPTER
#include <utempter.h>
@@ -50,6 +51,8 @@ struct control_state;
struct environ;
struct format_job_tree;
struct format_tree;
+struct hyperlinks_uri;
+struct hyperlinks;
struct input_ctx;
struct job;
struct menu_data;
@@ -62,6 +65,11 @@ struct screen_write_citem;
struct screen_write_cline;
struct screen_write_ctx;
struct session;
+
+#ifdef ENABLE_SIXEL
+struct sixel_image;
+#endif
+
struct tty_ctx;
struct tty_code;
struct tty_key;
@@ -79,6 +87,9 @@ struct winlink;
#ifndef TMUX_TERM
#define TMUX_TERM "screen"
#endif
+#ifndef TMUX_LOCK_CMD
+#define TMUX_LOCK_CMD "lock -np"
+#endif
/* Minimum layout cell size, NOT including border lines. */
#define PANE_MINIMUM 1
@@ -130,12 +141,14 @@ struct winlink;
#define KEYC_SHIFT 0x00400000000000ULL
/* Key flag bits. */
-#define KEYC_LITERAL 0x01000000000000ULL
-#define KEYC_KEYPAD 0x02000000000000ULL
-#define KEYC_CURSOR 0x04000000000000ULL
+#define KEYC_LITERAL 0x01000000000000ULL
+#define KEYC_KEYPAD 0x02000000000000ULL
+#define KEYC_CURSOR 0x04000000000000ULL
#define KEYC_IMPLIED_META 0x08000000000000ULL
#define KEYC_BUILD_MODIFIERS 0x10000000000000ULL
-#define KEYC_VI 0x20000000000000ULL
+#define KEYC_VI 0x20000000000000ULL
+#define KEYC_EXTENDED 0x40000000000000ULL
+#define KEYC_SENT 0x80000000000000ULL
/* Masks for key bits. */
#define KEYC_MASK_MODIFIERS 0x00f00000000000ULL
@@ -154,7 +167,9 @@ struct winlink;
#define KEYC_IS_UNICODE(key) \
(((key) & KEYC_MASK_KEY) > 0x7f && \
(((key) & KEYC_MASK_KEY) < KEYC_BASE || \
- ((key) & KEYC_MASK_KEY) >= KEYC_BASE_END))
+ ((key) & KEYC_MASK_KEY) >= KEYC_BASE_END) && \
+ (((key) & KEYC_MASK_KEY) < KEYC_USER || \
+ ((key) & KEYC_MASK_KEY) >= KEYC_USER + KEYC_NUSER))
/* Multiple click timeout. */
#define KEYC_CLICK_TIMEOUT 300
@@ -366,6 +381,7 @@ enum tty_code_code {
TTYC_ENFCS,
TTYC_ENMG,
TTYC_FSL,
+ TTYC_HLS,
TTYC_HOME,
TTYC_HPA,
TTYC_ICH,
@@ -512,6 +528,7 @@ enum tty_code_code {
TTYC_KUP6,
TTYC_KUP7,
TTYC_MS,
+ TTYC_NOBR,
TTYC_OL,
TTYC_OP,
TTYC_RECT,
@@ -529,6 +546,7 @@ enum tty_code_code {
TTYC_SETRGBB,
TTYC_SETRGBF,
TTYC_SETULC,
+ TTYC_SETULC1,
TTYC_SGR0,
TTYC_SITM,
TTYC_SMACS,
@@ -539,6 +557,7 @@ enum tty_code_code {
TTYC_SMUL,
TTYC_SMULX,
TTYC_SMXX,
+ TTYC_SXL,
TTYC_SS,
TTYC_SWD,
TTYC_SYNC,
@@ -663,7 +682,17 @@ struct colour_palette {
#define GRID_LINE_WRAPPED 0x1
#define GRID_LINE_EXTENDED 0x2
#define GRID_LINE_DEAD 0x4
+#define GRID_LINE_START_PROMPT 0x8
+#define GRID_LINE_START_OUTPUT 0x10
+
+/* Grid string flags. */
+#define GRID_STRING_WITH_SEQUENCES 0x1
+#define GRID_STRING_ESCAPE_SEQUENCES 0x2
+#define GRID_STRING_TRIM_SPACES 0x4
+#define GRID_STRING_USED_ONLY 0x8
+#define GRID_STRING_EMPTY_CELLS 0x10
+/* Cell positions. */
#define CELL_INSIDE 0
#define CELL_TOPBOTTOM 1
#define CELL_LEFTRIGHT 2
@@ -678,6 +707,7 @@ struct colour_palette {
#define CELL_JOIN 11
#define CELL_OUTSIDE 12
+/* Cell borders. */
#define CELL_BORDERS " xqlkmjwvtun~"
#define SIMPLE_BORDERS " |-+++++++++."
#define PADDED_BORDERS " "
@@ -690,6 +720,7 @@ struct grid_cell {
int fg;
int bg;
int us;
+ u_int link;
};
/* Grid extended cell entry. */
@@ -700,11 +731,11 @@ struct grid_extd_entry {
int fg;
int bg;
int us;
+ u_int link;
} __packed;
/* Grid cell entry. */
struct grid_cell_entry {
- u_char flags;
union {
u_int offset;
struct {
@@ -714,6 +745,7 @@ struct grid_cell_entry {
u_char data;
} data;
};
+ u_char flags;
} __packed;
/* Grid line. */
@@ -726,6 +758,7 @@ struct grid_line {
u_int extdsize;
int flags;
+ time_t time;
};
/* Entire grid of cells. */
@@ -773,11 +806,15 @@ enum style_range_type {
STYLE_RANGE_NONE,
STYLE_RANGE_LEFT,
STYLE_RANGE_RIGHT,
- STYLE_RANGE_WINDOW
+ STYLE_RANGE_PANE,
+ STYLE_RANGE_WINDOW,
+ STYLE_RANGE_SESSION,
+ STYLE_RANGE_USER
};
struct style_range {
enum style_range_type type;
u_int argument;
+ char string[16];
u_int start;
u_int end; /* not included */
@@ -804,10 +841,29 @@ struct style {
enum style_range_type range_type;
u_int range_argument;
+ char range_string[16];
enum style_default_type default_type;
};
+#ifdef ENABLE_SIXEL
+/* Image. */
+struct image {
+ struct screen *s;
+ struct sixel_image *data;
+ char *fallback;
+
+ u_int px;
+ u_int py;
+ u_int sx;
+ u_int sy;
+
+ TAILQ_ENTRY (image) all_entry;
+ TAILQ_ENTRY (image) entry;
+};
+TAILQ_HEAD(images, image);
+#endif
+
/* Cursor style. */
enum screen_cursor_style {
SCREEN_CURSOR_DEFAULT,
@@ -849,7 +905,13 @@ struct screen {
bitstr_t *tabs;
struct screen_sel *sel;
+#ifdef ENABLE_SIXEL
+ struct images images;
+#endif
+
struct screen_write_cline *write_list;
+
+ struct hyperlinks *hyperlinks;
};
/* Screen write context. */
@@ -861,7 +923,6 @@ struct screen_write_ctx {
int flags;
#define SCREEN_WRITE_SYNC 0x1
-#define SCREEN_WRITE_ZWJ 0x2
screen_write_init_ctx_cb init_ctx_cb;
void *arg;
@@ -1015,7 +1076,7 @@ struct window_pane {
#define PANE_REDRAW 0x1
#define PANE_DROP 0x2
#define PANE_FOCUSED 0x4
-/* 0x8 unused */
+#define PANE_VISITED 0x8
/* 0x10 unused */
/* 0x20 unused */
#define PANE_INPUTOFF 0x40
@@ -1025,6 +1086,7 @@ struct window_pane {
#define PANE_STATUSDRAWN 0x400
#define PANE_EMPTY 0x800
#define PANE_STYLECHANGED 0x1000
+#define PANE_UNSEENCHANGES 0x2000
int argc;
char **argv;
@@ -1069,7 +1131,8 @@ struct window_pane {
int border_gc_set;
struct grid_cell border_gc;
- TAILQ_ENTRY(window_pane) entry;
+ TAILQ_ENTRY(window_pane) entry; /* link in list of all panes */
+ TAILQ_ENTRY(window_pane) sentry; /* link in list of last visited */
RB_ENTRY(window_pane) tree_entry;
};
TAILQ_HEAD(window_panes, window_pane);
@@ -1090,7 +1153,7 @@ struct window {
struct timeval activity_time;
struct window_pane *active;
- struct window_pane *last;
+ struct window_panes last_panes;
struct window_panes panes;
int lastlayout;
@@ -1143,6 +1206,7 @@ struct winlink {
#define WINLINK_ACTIVITY 0x2
#define WINLINK_SILENCE 0x4
#define WINLINK_ALERTFLAGS (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_SILENCE)
+#define WINLINK_VISITED 0x8
RB_ENTRY(winlink) entry;
TAILQ_ENTRY(winlink) wentry;
@@ -1331,6 +1395,7 @@ struct tty_term {
#define TERM_DECFRA 0x8
#define TERM_RGBCOLOURS 0x10
#define TERM_VT100LIKE 0x20
+#define TERM_SIXEL 0x40
int flags;
LIST_ENTRY(tty_term) entry;
@@ -1342,9 +1407,11 @@ struct tty {
struct client *client;
struct event start_timer;
struct event clipboard_timer;
+ time_t last_requests;
u_int sx;
u_int sy;
+ /* Cell size in pixels. */
u_int xpixel;
u_int ypixel;
@@ -1353,6 +1420,8 @@ struct tty {
enum screen_cursor_style cstyle;
int ccolour;
+ /* Properties of the area being drawn on. */
+ /* When true, the drawing area is bigger than the terminal. */
int oflag;
u_int oox;
u_int ooy;
@@ -1360,6 +1429,8 @@ struct tty {
u_int osy;
int mode;
+ int fg;
+ int bg;
u_int rlower;
u_int rupper;
@@ -1387,9 +1458,12 @@ struct tty {
#define TTY_OPENED 0x20
#define TTY_OSC52QUERY 0x40
#define TTY_BLOCK 0x80
-#define TTY_HAVEDA 0x100
+#define TTY_HAVEDA 0x100 /* Primary DA. */
#define TTY_HAVEXDA 0x200
#define TTY_SYNCING 0x400
+#define TTY_HAVEDA2 0x800 /* Secondary DA. */
+#define TTY_ALL_REQUEST_FLAGS \
+ (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)
int flags;
struct tty_term *term;
@@ -1422,39 +1496,47 @@ struct tty_ctx {
u_int num;
void *ptr;
+ void *ptr2;
+
+ /*
+ * Whether this command should be sent even when the pane is not
+ * visible (used for a passthrough sequence when allow-passthrough is
+ * "all").
+ */
+ int allow_invisible_panes;
/*
* Cursor and region position before the screen was updated - this is
* where the command should be applied; the values in the screen have
* already been updated.
*/
- u_int ocx;
- u_int ocy;
+ u_int ocx;
+ u_int ocy;
- u_int orupper;
- u_int orlower;
+ u_int orupper;
+ u_int orlower;
/* Target region (usually pane) offset and size. */
- u_int xoff;
- u_int yoff;
- u_int rxoff;
- u_int ryoff;
- u_int sx;
- u_int sy;
+ u_int xoff;
+ u_int yoff;
+ u_int rxoff;
+ u_int ryoff;
+ u_int sx;
+ u_int sy;
/* The background colour used for clearing (erasing). */
- u_int bg;
+ u_int bg;
/* The default colours and palette. */
- struct grid_cell defaults;
- struct colour_palette *palette;
+ struct grid_cell defaults;
+ struct colour_palette *palette;
/* Containing region (usually window) offset and size. */
- int bigger;
- u_int wox;
- u_int woy;
- u_int wsx;
- u_int wsy;
+ int bigger;
+ u_int wox;
+ u_int woy;
+ u_int wsx;
+ u_int wsy;
};
/* Saved message entry. */
@@ -1776,6 +1858,7 @@ struct client {
#define CLIENT_CONTROL_WAITEXIT 0x200000000ULL
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL
+#define CLIENT_BRACKETPASTING 0x1000000000ULL
#define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \
@@ -2043,10 +2126,11 @@ extern char **cfg_files;
extern u_int cfg_nfiles;
extern int cfg_quiet;
void start_cfg(void);
-int load_cfg(const char *, struct client *, struct cmdq_item *, int,
- struct cmdq_item **);
+int load_cfg(const char *, struct client *, struct cmdq_item *,
+ struct cmd_find_state *, int, struct cmdq_item **);
int load_cfg_from_buffer(const void *, size_t, const char *,
- struct client *, struct cmdq_item *, int, struct cmdq_item **);
+ struct client *, struct cmdq_item *, struct cmd_find_state *,
+ int, struct cmdq_item **);
void printflike(1, 2) cfg_add_cause(const char *, ...);
void cfg_print_causes(struct cmdq_item *);
void cfg_show_causes(struct session *);
@@ -2058,6 +2142,7 @@ u_int paste_buffer_order(struct paste_buffer *);
time_t paste_buffer_created(struct paste_buffer *);
const char *paste_buffer_data(struct paste_buffer *, size_t *);
struct paste_buffer *paste_walk(struct paste_buffer *);
+int paste_is_empty(void);
struct paste_buffer *paste_get_top(const char **);
struct paste_buffer *paste_get_name(const char *);
void paste_free(struct paste_buffer *);
@@ -2094,6 +2179,7 @@ void format_add_cb(struct format_tree *, const char *, format_cb);
void format_log_debug(struct format_tree *, const char *);
void format_each(struct format_tree *, void (*)(const char *,
const char *, void *), void *);
+char *format_pretty_time(time_t, int);
char *format_expand_time(struct format_tree *, const char *);
char *format_expand(struct format_tree *, const char *);
char *format_single(struct cmdq_item *, const char *,
@@ -2116,6 +2202,8 @@ void format_defaults_paste_buffer(struct format_tree *,
struct paste_buffer *);
void format_lost_client(struct client *);
char *format_grid_word(struct grid *, u_int, u_int);
+char *format_grid_hyperlink(struct grid *, u_int, u_int,
+ struct screen *);
char *format_grid_line(struct grid *, u_int);
/* format-draw.c */
@@ -2134,6 +2222,7 @@ void notify_winlink(const char *, struct winlink *);
void notify_session_window(const char *, struct session *, struct window *);
void notify_window(const char *, struct window *);
void notify_pane(const char *, struct window_pane *);
+void notify_paste_buffer(const char *, int);
/* options.c */
struct options *options_create(struct options *);
@@ -2243,42 +2332,50 @@ void tty_update_window_offset(struct window *);
void tty_update_client_offset(struct client *);
void tty_raw(struct tty *, const char *);
void tty_attributes(struct tty *, const struct grid_cell *,
- const struct grid_cell *, struct colour_palette *);
+ const struct grid_cell *, struct colour_palette *,
+ struct hyperlinks *);
void tty_reset(struct tty *);
void tty_region_off(struct tty *);
void tty_margin_off(struct tty *);
void tty_cursor(struct tty *, u_int, u_int);
void tty_clipboard_query(struct tty *);
void tty_putcode(struct tty *, enum tty_code_code);
-void tty_putcode1(struct tty *, enum tty_code_code, int);
-void tty_putcode2(struct tty *, enum tty_code_code, int, int);
-void tty_putcode3(struct tty *, enum tty_code_code, int, int, int);
-void tty_putcode_ptr1(struct tty *, enum tty_code_code, const void *);
-void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *,
- const void *);
+void tty_putcode_i(struct tty *, enum tty_code_code, int);
+void tty_putcode_ii(struct tty *, enum tty_code_code, int, int);
+void tty_putcode_iii(struct tty *, enum tty_code_code, int, int, int);
+void tty_putcode_s(struct tty *, enum tty_code_code, const char *);
+void tty_putcode_ss(struct tty *, enum tty_code_code, const char *,
+ const char *);
void tty_puts(struct tty *, const char *);
void tty_putc(struct tty *, u_char);
void tty_putn(struct tty *, const void *, size_t, u_int);
void tty_cell(struct tty *, const struct grid_cell *,
- const struct grid_cell *, struct colour_palette *);
+ const struct grid_cell *, struct colour_palette *,
+ struct hyperlinks *);
int tty_init(struct tty *, struct client *);
void tty_resize(struct tty *);
void tty_set_size(struct tty *, u_int, u_int, u_int, u_int);
void tty_start_tty(struct tty *);
void tty_send_requests(struct tty *);
+void tty_repeat_requests(struct tty *);
void tty_stop_tty(struct tty *);
void tty_set_title(struct tty *, const char *);
void tty_set_path(struct tty *, const char *);
void tty_update_mode(struct tty *, int, struct screen *);
void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int,
u_int, u_int, const struct grid_cell *, struct colour_palette *);
+
+#ifdef ENABLE_SIXEL
+void tty_draw_images(struct client *, struct window_pane *, struct screen *);
+#endif
+
void tty_sync_start(struct tty *);
void tty_sync_end(struct tty *);
int tty_open(struct tty *, char **);
void tty_close(struct tty *);
void tty_free(struct tty *);
void tty_update_features(struct tty *);
-void tty_set_selection(struct tty *, const char *, size_t);
+void tty_set_selection(struct tty *, const char *, const char *, size_t);
void tty_write(void (*)(struct tty *, const struct tty_ctx *),
struct tty_ctx *);
void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *);
@@ -2302,6 +2399,11 @@ void tty_cmd_scrolldown(struct tty *, const struct tty_ctx *);
void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *);
void tty_cmd_setselection(struct tty *, const struct tty_ctx *);
void tty_cmd_rawstring(struct tty *, const struct tty_ctx *);
+
+#ifdef ENABLE_SIXEL
+void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *);
+#endif
+
void tty_cmd_syncstart(struct tty *, const struct tty_ctx *);
void tty_default_colours(struct grid_cell *, struct window_pane *);
@@ -2318,15 +2420,15 @@ int tty_term_read_list(const char *, int, char ***, u_int *,
void tty_term_free_list(char **, u_int);
int tty_term_has(struct tty_term *, enum tty_code_code);
const char *tty_term_string(struct tty_term *, enum tty_code_code);
-const char *tty_term_string1(struct tty_term *, enum tty_code_code, int);
-const char *tty_term_string2(struct tty_term *, enum tty_code_code, int,
+const char *tty_term_string_i(struct tty_term *, enum tty_code_code, int);
+const char *tty_term_string_ii(struct tty_term *, enum tty_code_code, int,
int);
-const char *tty_term_string3(struct tty_term *, enum tty_code_code, int,
+const char *tty_term_string_iii(struct tty_term *, enum tty_code_code, int,
int, int);
-const char *tty_term_ptr1(struct tty_term *, enum tty_code_code,
- const void *);
-const char *tty_term_ptr2(struct tty_term *, enum tty_code_code,
- const void *, const void *);
+const char *tty_term_string_s(struct tty_term *, enum tty_code_code,
+ const char *);
+const char *tty_term_string_ss(struct tty_term *, enum tty_code_code,
+ const char *, const char *);
int tty_term_number(struct tty_term *, enum tty_code_code);
int tty_term_flag(struct tty_term *, enum tty_code_code);
const char *tty_term_describe(struct tty_term *, enum tty_code_code);
@@ -2351,7 +2453,7 @@ void tty_keys_free(struct tty *);
int tty_keys_next(struct tty *);
/* arguments.c */
-void args_set(struct args *, u_char, struct args_value *);
+void args_set(struct args *, u_char, struct args_value *, int);
struct args *args_create(void);
struct args *args_parse(const struct args_parse *, struct args_value *,
u_int, char **);
@@ -2383,10 +2485,16 @@ struct args_value *args_first_value(struct args *, u_char);
struct args_value *args_next_value(struct args_value *);
long long args_strtonum(struct args *, u_char, long long, long long,
char **);
+long long args_strtonum_and_expand(struct args *, u_char, long long,
+ long long, struct cmdq_item *, char **);
long long args_percentage(struct args *, u_char, long long,
long long, long long, char **);
long long args_string_percentage(const char *, long long, long long,
long long, char **);
+long long args_percentage_and_expand(struct args *, u_char, long long,
+ long long, long long, struct cmdq_item *, char **);
+long long args_string_percentage_and_expand(const char *, long long,
+ long long, long long, struct cmdq_item *, char **);
/* cmd-find.c */
int cmd_find_target(struct cmd_find_state *, struct cmdq_item *,
@@ -2478,7 +2586,8 @@ struct cmd_parse_result *cmd_parse_from_arguments(struct args_value *, u_int,
struct cmdq_state *cmdq_new_state(struct cmd_find_state *, struct key_event *,
int);
struct cmdq_state *cmdq_link_state(struct cmdq_state *);
-struct cmdq_state *cmdq_copy_state(struct cmdq_state *);
+struct cmdq_state *cmdq_copy_state(struct cmdq_state *,
+ struct cmd_find_state *);
void cmdq_free_state(struct cmdq_state *);
void printflike(3, 4) cmdq_add_format(struct cmdq_state *, const char *,
const char *, ...);
@@ -2508,6 +2617,7 @@ u_int cmdq_next(struct client *);
struct cmdq_item *cmdq_running(struct client *);
void cmdq_guard(struct cmdq_item *, const char *, int);
void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...);
+void cmdq_print_data(struct cmdq_item *, int, struct evbuffer *);
void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...);
/* cmd-wait-for.c */
@@ -2562,7 +2672,9 @@ void file_print_buffer(struct client *, void *, size_t);
void printflike(2, 3) file_error(struct client *, const char *, ...);
void file_write(struct client *, const char *, int, const void *, size_t,
client_file_cb, void *);
-void file_read(struct client *, const char *, client_file_cb, void *);
+struct client_file *file_read(struct client *, const char *, client_file_cb,
+ void *);
+void file_cancel(struct client_file *);
void file_push(struct client_file *);
int file_write_left(struct client_files *);
void file_write_open(struct client_files *, struct tmuxpeer *,
@@ -2574,12 +2686,14 @@ void file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *,
void file_write_ready(struct client_files *, struct imsg *);
void file_read_data(struct client_files *, struct imsg *);
void file_read_done(struct client_files *, struct imsg *);
+void file_read_cancel(struct client_files *, struct imsg *);
/* server.c */
extern struct tmuxproc *server_proc;
extern struct clients clients;
extern struct cmd_find_state marked_pane;
extern struct message_list message_log;
+extern time_t current_time;
void server_set_marked(struct session *, struct winlink *,
struct window_pane *);
void server_clear_marked(void);
@@ -2624,6 +2738,7 @@ struct client_window *server_client_add_client_window(struct client *, u_int);
struct window_pane *server_client_get_pane(struct client *);
void server_client_set_pane(struct client *, struct window_pane *);
void server_client_remove_pane(struct window_pane *);
+void server_client_print(struct client *, int, struct evbuffer *);
/* server-fn.c */
void server_redraw_client(struct client *);
@@ -2716,6 +2831,7 @@ int colour_fromstring(const char *s);
int colour_256toRGB(int);
int colour_256to16(int);
int colour_byname(const char *);
+int colour_parseX11(const char *);
void colour_palette_init(struct colour_palette *);
void colour_palette_clear(struct colour_palette *);
void colour_palette_free(struct colour_palette *);
@@ -2754,7 +2870,7 @@ void grid_clear_lines(struct grid *, u_int, u_int, u_int);
void grid_move_lines(struct grid *, u_int, u_int, u_int, u_int);
void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int, u_int);
char *grid_string_cells(struct grid *, u_int, u_int, u_int,
- struct grid_cell **, int, int, int);
+ struct grid_cell **, int, struct screen *);
void grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int,
u_int);
void grid_reflow(struct grid *, u_int);
@@ -2827,12 +2943,14 @@ void screen_write_putc(struct screen_write_ctx *, const struct grid_cell *,
u_char);
void screen_write_fast_copy(struct screen_write_ctx *, struct screen *,
u_int, u_int, u_int, u_int);
-void screen_write_hline(struct screen_write_ctx *, u_int, int, int);
+void screen_write_hline(struct screen_write_ctx *, u_int, int, int,
+ enum box_lines, const struct grid_cell *);
void screen_write_vline(struct screen_write_ctx *, u_int, int, int);
void screen_write_menu(struct screen_write_ctx *, struct menu *, int,
+ enum box_lines, const struct grid_cell *, const struct grid_cell *,
const struct grid_cell *);
-void screen_write_box(struct screen_write_ctx *, u_int, u_int, int,
- const struct grid_cell *, const char *);
+void screen_write_box(struct screen_write_ctx *, u_int, u_int,
+ enum box_lines, const struct grid_cell *, const char *);
void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int,
u_int);
void screen_write_backspace(struct screen_write_ctx *);
@@ -2867,8 +2985,14 @@ void screen_write_collect_end(struct screen_write_ctx *);
void screen_write_collect_add(struct screen_write_ctx *,
const struct grid_cell *);
void screen_write_cell(struct screen_write_ctx *, const struct grid_cell *);
-void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int);
-void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int);
+void screen_write_setselection(struct screen_write_ctx *, const char *,
+ u_char *, u_int);
+void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int,
+ int);
+#ifdef ENABLE_SIXEL
+void screen_write_sixelimage(struct screen_write_ctx *,
+ struct sixel_image *, u_int);
+#endif
void screen_write_alternateon(struct screen_write_ctx *,
struct grid_cell *, int);
void screen_write_alternateoff(struct screen_write_ctx *,
@@ -2883,6 +3007,7 @@ void screen_init(struct screen *, u_int, u_int, u_int);
void screen_reinit(struct screen *);
void screen_free(struct screen *);
void screen_reset_tabs(struct screen *);
+void screen_reset_hyperlinks(struct screen *);
void screen_set_cursor_style(u_int, enum screen_cursor_style *, int *);
void screen_set_cursor_colour(struct screen *, int);
int screen_set_title(struct screen *, const char *);
@@ -2971,6 +3096,7 @@ int window_pane_key(struct window_pane *, struct client *,
struct session *, struct winlink *, key_code,
struct mouse_event *);
int window_pane_visible(struct window_pane *);
+int window_pane_exited(struct window_pane *);
u_int window_pane_search(struct window_pane *, const char *, int,
int);
const char *window_printable_flags(struct winlink *, int);
@@ -2978,6 +3104,10 @@ struct window_pane *window_pane_find_up(struct window_pane *);
struct window_pane *window_pane_find_down(struct window_pane *);
struct window_pane *window_pane_find_left(struct window_pane *);
struct window_pane *window_pane_find_right(struct window_pane *);
+void window_pane_stack_push(struct window_panes *,
+ struct window_pane *);
+void window_pane_stack_remove(struct window_panes *,
+ struct window_pane *);
void window_set_name(struct window *, const char *);
void window_add_ref(struct window *, const char *);
void window_remove_ref(struct window *, const char *);
@@ -2990,6 +3120,7 @@ void *window_pane_get_new_data(struct window_pane *,
void window_pane_update_used_data(struct window_pane *,
struct window_pane_offset *, size_t);
void window_set_fill_character(struct window *);
+void window_pane_default_cursor(struct window_pane *);
/* layout.c */
u_int layout_count_cells(struct layout_cell *);
@@ -3111,6 +3242,7 @@ char *parse_window_name(const char *);
/* control.c */
void control_discard(struct client *);
void control_start(struct client *);
+void control_ready(struct client *);
void control_stop(struct client *);
void control_set_pane_on(struct client *, struct window_pane *);
void control_set_pane_off(struct client *, struct window_pane *);
@@ -3141,6 +3273,8 @@ void control_notify_session_renamed(struct session *);
void control_notify_session_created(struct session *);
void control_notify_session_closed(struct session *);
void control_notify_session_window_changed(struct session *);
+void control_notify_paste_buffer_changed(const char *);
+void control_notify_paste_buffer_deleted(const char *);
/* session.c */
extern struct sessions sessions;
@@ -3183,6 +3317,8 @@ u_int session_group_attached_count(struct session_group *);
void session_renumber_windows(struct session *);
/* utf8.c */
+enum utf8_state utf8_towc (const struct utf8_data *, wchar_t *);
+int utf8_in_table(wchar_t, const wchar_t *, u_int);
utf8_char utf8_build_one(u_char);
enum utf8_state utf8_from_data(const struct utf8_data *, utf8_char *);
void utf8_to_data(utf8_char, struct utf8_data *);
@@ -3209,6 +3345,16 @@ char *osdep_get_name(int, char *);
char *osdep_get_cwd(int);
struct event_base *osdep_event_init(void);
+/* utf8-combined.c */
+int utf8_has_zwj(const struct utf8_data *);
+int utf8_is_zwj(const struct utf8_data *);
+int utf8_is_vs(const struct utf8_data *);
+int utf8_is_modifier(const struct utf8_data *);
+
+/* procname.c */
+char *get_proc_name(int, char *);
+char *get_proc_cwd(int);
+
/* log.c */
void log_add_level(void);
int log_get_level(void);
@@ -3231,11 +3377,13 @@ void menu_add_item(struct menu *, const struct menu_item *,
struct cmdq_item *, struct client *,
struct cmd_find_state *);
void menu_free(struct menu *);
-struct menu_data *menu_prepare(struct menu *, int, struct cmdq_item *, u_int,
- u_int, struct client *, struct cmd_find_state *,
+struct menu_data *menu_prepare(struct menu *, int, int, struct cmdq_item *,
+ u_int, u_int, struct client *, enum box_lines, const char *,
+ const char *, const char *, struct cmd_find_state *,
menu_choice_cb, void *);
-int menu_display(struct menu *, int, struct cmdq_item *, u_int,
- u_int, struct client *, struct cmd_find_state *,
+int menu_display(struct menu *, int, int, struct cmdq_item *,
+ u_int, u_int, struct client *, enum box_lines, const char *,
+ const char *, const char *, struct cmd_find_state *,
menu_choice_cb, void *);
struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *);
void menu_check_cb(struct client *, void *, u_int, u_int, u_int,
@@ -3251,11 +3399,11 @@ int menu_key_cb(struct client *, void *, struct key_event *);
#define POPUP_INTERNAL 0x4
typedef void (*popup_close_cb)(int, void *);
typedef void (*popup_finish_edit_cb)(char *, size_t, void *);
-int popup_display(int, int, struct cmdq_item *, u_int, u_int,
- u_int, u_int, struct environ *, const char *, int, char **,
- const char *, const char *, struct client *,
- struct session *, const char *, const char *,
- popup_close_cb, void *);
+int popup_display(int, enum box_lines, struct cmdq_item *, u_int,
+ u_int, u_int, u_int, struct environ *, const char *, int,
+ char **, const char *, const char *, struct client *,
+ struct session *, const char *, const char *,
+ popup_close_cb, void *);
int popup_editor(struct client *, const char *, size_t,
popup_finish_edit_cb, void *);
@@ -3277,6 +3425,27 @@ struct window_pane *spawn_pane(struct spawn_context *, char **);
/* regsub.c */
char *regsub(const char *, const char *, const char *, int);
+#ifdef ENABLE_SIXEL
+/* image.c */
+int image_free_all(struct screen *);
+struct image *image_store(struct screen *, struct sixel_image *);
+int image_check_line(struct screen *, u_int, u_int);
+int image_check_area(struct screen *, u_int, u_int, u_int, u_int);
+int image_scroll_up(struct screen *, u_int);
+
+/* image-sixel.c */
+#define SIXEL_COLOUR_REGISTERS 1024
+struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int);
+void sixel_free(struct sixel_image *);
+void sixel_log(struct sixel_image *);
+void sixel_size_in_cells(struct sixel_image *, u_int *, u_int *);
+struct sixel_image *sixel_scale(struct sixel_image *, u_int, u_int, u_int,
+ u_int, u_int, u_int, int);
+char *sixel_print(struct sixel_image *, struct sixel_image *,
+ size_t *);
+struct screen *sixel_to_screen(struct sixel_image *);
+#endif
+
/* server-acl.c */
void server_acl_init(void);
struct server_acl_user *server_acl_user_find(uid_t);
@@ -3288,4 +3457,13 @@ void server_acl_user_deny_write(uid_t);
int server_acl_join(struct client *);
uid_t server_acl_get_uid(struct server_acl_user *);
+/* hyperlink.c */
+u_int hyperlinks_put(struct hyperlinks *, const char *,
+ const char *);
+int hyperlinks_get(struct hyperlinks *, u_int,
+ const char **, const char **, const char **);
+struct hyperlinks *hyperlinks_init(void);
+void hyperlinks_reset(struct hyperlinks *);
+void hyperlinks_free(struct hyperlinks *);
+
#endif /* TMUX_H */
diff --git a/tty-acs.c b/tty-acs.c
index 64ba367..3dab31b 100644
--- a/tty-acs.c
+++ b/tty-acs.c
@@ -155,8 +155,8 @@ static const struct utf8_data tty_acs_rounded_borders_list[] = {
{ "\342\225\257", 0, 3, 1 }, /* U+256F */
{ "\342\224\263", 0, 3, 1 }, /* U+2533 */
{ "\342\224\273", 0, 3, 1 }, /* U+253B */
- { "\342\224\243", 0, 3, 1 }, /* U+2523 */
- { "\342\224\253", 0, 3, 1 }, /* U+252B */
+ { "\342\224\234", 0, 3, 1 }, /* U+2524 */
+ { "\342\224\244", 0, 3, 1 }, /* U+251C */
{ "\342\225\213", 0, 3, 1 }, /* U+254B */
{ "\302\267", 0, 2, 1 } /* U+00B7 */
};
diff --git a/tty-features.c b/tty-features.c
index 2848b4d..9bd0d84 100644
--- a/tty-features.c
+++ b/tty-features.c
@@ -21,6 +21,12 @@
#include <stdlib.h>
#include <string.h>
+#if defined(HAVE_CURSES_H)
+#include <curses.h>
+#elif defined(HAVE_NCURSES_H)
+#include <ncurses.h>
+#endif
+
#include "tmux.h"
/*
@@ -36,13 +42,13 @@
/* A named terminal feature. */
struct tty_feature {
- const char *name;
- const char **capabilities;
- int flags;
+ const char *name;
+ const char *const *capabilities;
+ int flags;
};
/* Terminal has xterm(1) title setting. */
-static const char *tty_feature_title_capabilities[] = {
+static const char *const tty_feature_title_capabilities[] = {
"tsl=\\E]0;", /* should be using TS really */
"fsl=\\a",
NULL
@@ -54,7 +60,7 @@ static const struct tty_feature tty_feature_title = {
};
/* Terminal has OSC 7 working directory. */
-static const char *tty_feature_osc7_capabilities[] = {
+static const char *const tty_feature_osc7_capabilities[] = {
"Swd=\\E]7;",
"fsl=\\a",
NULL
@@ -66,7 +72,7 @@ static const struct tty_feature tty_feature_osc7 = {
};
/* Terminal has mouse support. */
-static const char *tty_feature_mouse_capabilities[] = {
+static const char *const tty_feature_mouse_capabilities[] = {
"kmous=\\E[M",
NULL
};
@@ -77,7 +83,7 @@ static const struct tty_feature tty_feature_mouse = {
};
/* Terminal can set the clipboard with OSC 52. */
-static const char *tty_feature_clipboard_capabilities[] = {
+static const char *const tty_feature_clipboard_capabilities[] = {
"Ms=\\E]52;%p1%s;%p2%s\\a",
NULL
};
@@ -87,12 +93,27 @@ static const struct tty_feature tty_feature_clipboard = {
0
};
+/* Terminal supports OSC 8 hyperlinks. */
+static const char *tty_feature_hyperlinks_capabilities[] = {
+#if defined (__OpenBSD__) || (defined(NCURSES_VERSION_MAJOR) && \
+ (NCURSES_VERSION_MAJOR > 5 || \
+ (NCURSES_VERSION_MAJOR == 5 && NCURSES_VERSION_MINOR > 8)))
+ "*:Hls=\\E]8;%?%p1%l%tid=%p1%s%;;%p2%s\\E\\\\",
+#endif
+ NULL
+};
+static const struct tty_feature tty_feature_hyperlinks = {
+ "hyperlinks",
+ tty_feature_hyperlinks_capabilities,
+ 0
+};
+
/*
* Terminal supports RGB colour. This replaces setab and setaf also since
* terminals with RGB have versions that do not allow setting colours from the
* 256 palette.
*/
-static const char *tty_feature_rgb_capabilities[] = {
+static const char *const tty_feature_rgb_capabilities[] = {
"AX",
"setrgbf=\\E[38;2;%p1%d;%p2%d;%p3%dm",
"setrgbb=\\E[48;2;%p1%d;%p2%d;%p3%dm",
@@ -107,7 +128,7 @@ static const struct tty_feature tty_feature_rgb = {
};
/* Terminal supports 256 colours. */
-static const char *tty_feature_256_capabilities[] = {
+static const char *const tty_feature_256_capabilities[] = {
"AX",
"setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
"setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
@@ -120,7 +141,7 @@ static const struct tty_feature tty_feature_256 = {
};
/* Terminal supports overline. */
-static const char *tty_feature_overline_capabilities[] = {
+static const char *const tty_feature_overline_capabilities[] = {
"Smol=\\E[53m",
NULL
};
@@ -131,9 +152,10 @@ static const struct tty_feature tty_feature_overline = {
};
/* Terminal supports underscore styles. */
-static const char *tty_feature_usstyle_capabilities[] = {
+static const char *const tty_feature_usstyle_capabilities[] = {
"Smulx=\\E[4::%p1%dm",
"Setulc=\\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m",
+ "Setulc1=\\E[58::5::%p1%dm",
"ol=\\E[59m",
NULL
};
@@ -144,7 +166,7 @@ static const struct tty_feature tty_feature_usstyle = {
};
/* Terminal supports bracketed paste. */
-static const char *tty_feature_bpaste_capabilities[] = {
+static const char *const tty_feature_bpaste_capabilities[] = {
"Enbp=\\E[?2004h",
"Dsbp=\\E[?2004l",
NULL
@@ -156,7 +178,7 @@ static const struct tty_feature tty_feature_bpaste = {
};
/* Terminal supports focus reporting. */
-static const char *tty_feature_focus_capabilities[] = {
+static const char *const tty_feature_focus_capabilities[] = {
"Enfcs=\\E[?1004h",
"Dsfcs=\\E[?1004l",
NULL
@@ -168,7 +190,7 @@ static const struct tty_feature tty_feature_focus = {
};
/* Terminal supports cursor styles. */
-static const char *tty_feature_cstyle_capabilities[] = {
+static const char *const tty_feature_cstyle_capabilities[] = {
"Ss=\\E[%p1%d q",
"Se=\\E[2 q",
NULL
@@ -180,7 +202,7 @@ static const struct tty_feature tty_feature_cstyle = {
};
/* Terminal supports cursor colours. */
-static const char *tty_feature_ccolour_capabilities[] = {
+static const char *const tty_feature_ccolour_capabilities[] = {
"Cs=\\E]12;%p1%s\\a",
"Cr=\\E]112\\a",
NULL
@@ -192,7 +214,7 @@ static const struct tty_feature tty_feature_ccolour = {
};
/* Terminal supports strikethrough. */
-static const char *tty_feature_strikethrough_capabilities[] = {
+static const char *const tty_feature_strikethrough_capabilities[] = {
"smxx=\\E[9m",
NULL
};
@@ -203,8 +225,8 @@ static const struct tty_feature tty_feature_strikethrough = {
};
/* Terminal supports synchronized updates. */
-static const char *tty_feature_sync_capabilities[] = {
- "Sync=\\EP=%p1%ds\\E\\\\",
+static const char *const tty_feature_sync_capabilities[] = {
+ "Sync=\\E[?2026%?%p1%{1}%-%tl%eh%;",
NULL
};
static const struct tty_feature tty_feature_sync = {
@@ -214,7 +236,7 @@ static const struct tty_feature tty_feature_sync = {
};
/* Terminal supports extended keys. */
-static const char *tty_feature_extkeys_capabilities[] = {
+static const char *const tty_feature_extkeys_capabilities[] = {
"Eneks=\\E[>4;1m",
"Dseks=\\E[>4m",
NULL
@@ -226,7 +248,7 @@ static const struct tty_feature tty_feature_extkeys = {
};
/* Terminal supports DECSLRM margins. */
-static const char *tty_feature_margins_capabilities[] = {
+static const char *const tty_feature_margins_capabilities[] = {
"Enmg=\\E[?69h",
"Dsmg=\\E[?69l",
"Clmg=\\E[s",
@@ -240,7 +262,7 @@ static const struct tty_feature tty_feature_margins = {
};
/* Terminal supports DECFRA rectangle fill. */
-static const char *tty_feature_rectfill_capabilities[] = {
+static const char *const tty_feature_rectfill_capabilities[] = {
"Rect",
NULL
};
@@ -250,21 +272,109 @@ static const struct tty_feature tty_feature_rectfill = {
TERM_DECFRA
};
+/* Use builtin function keys only. */
+static const char *const tty_feature_ignorefkeys_capabilities[] = {
+ "kf0@",
+ "kf1@",
+ "kf2@",
+ "kf3@",
+ "kf4@",
+ "kf5@",
+ "kf6@",
+ "kf7@",
+ "kf8@",
+ "kf9@",
+ "kf10@",
+ "kf11@",
+ "kf12@",
+ "kf13@",
+ "kf14@",
+ "kf15@",
+ "kf16@",
+ "kf17@",
+ "kf18@",
+ "kf19@",
+ "kf20@",
+ "kf21@",
+ "kf22@",
+ "kf23@",
+ "kf24@",
+ "kf25@",
+ "kf26@",
+ "kf27@",
+ "kf28@",
+ "kf29@",
+ "kf30@",
+ "kf31@",
+ "kf32@",
+ "kf33@",
+ "kf34@",
+ "kf35@",
+ "kf36@",
+ "kf37@",
+ "kf38@",
+ "kf39@",
+ "kf40@",
+ "kf41@",
+ "kf42@",
+ "kf43@",
+ "kf44@",
+ "kf45@",
+ "kf46@",
+ "kf47@",
+ "kf48@",
+ "kf49@",
+ "kf50@",
+ "kf51@",
+ "kf52@",
+ "kf53@",
+ "kf54@",
+ "kf55@",
+ "kf56@",
+ "kf57@",
+ "kf58@",
+ "kf59@",
+ "kf60@",
+ "kf61@",
+ "kf62@",
+ "kf63@",
+ NULL
+};
+static const struct tty_feature tty_feature_ignorefkeys = {
+ "ignorefkeys",
+ tty_feature_ignorefkeys_capabilities,
+ 0
+};
+
+/* Terminal has sixel capability. */
+static const char *const tty_feature_sixel_capabilities[] = {
+ "Sxl",
+ NULL
+};
+static const struct tty_feature tty_feature_sixel = {
+ "sixel",
+ tty_feature_sixel_capabilities,
+ TERM_SIXEL
+};
+
/* Available terminal features. */
-static const struct tty_feature *tty_features[] = {
+static const struct tty_feature *const tty_features[] = {
&tty_feature_256,
&tty_feature_bpaste,
&tty_feature_ccolour,
&tty_feature_clipboard,
+ &tty_feature_hyperlinks,
&tty_feature_cstyle,
&tty_feature_extkeys,
&tty_feature_focus,
+ &tty_feature_ignorefkeys,
&tty_feature_margins,
&tty_feature_mouse,
&tty_feature_osc7,
&tty_feature_overline,
&tty_feature_rectfill,
&tty_feature_rgb,
+ &tty_feature_sixel,
&tty_feature_strikethrough,
&tty_feature_sync,
&tty_feature_title,
@@ -323,9 +433,9 @@ tty_get_features(int feat)
int
tty_apply_features(struct tty_term *term, int feat)
{
- const struct tty_feature *tf;
- const char **capability;
- u_int i;
+ const struct tty_feature *tf;
+ const char *const *capability;
+ u_int i;
if (feat == 0)
return (0);
@@ -356,7 +466,7 @@ tty_apply_features(struct tty_term *term, int feat)
void
tty_default_features(int *feat, const char *name, u_int version)
{
- static struct {
+ static const struct {
const char *name;
u_int version;
const char *features;
@@ -369,14 +479,14 @@ tty_default_features(int *feat, const char *name, u_int version)
},
{ .name = "tmux",
.features = TTY_FEATURES_BASE_MODERN_XTERM
- ",ccolour,cstyle,focus,overline,usstyle"
+ ",ccolour,cstyle,focus,overline,usstyle,hyperlinks"
},
{ .name = "rxvt-unicode",
- .features = "256,bpaste,ccolour,cstyle,mouse,title"
+ .features = "256,bpaste,ccolour,cstyle,mouse,title,ignorefkeys"
},
{ .name = "iTerm2",
.features = TTY_FEATURES_BASE_MODERN_XTERM
- ",cstyle,extkeys,margins,usstyle,sync,osc7"
+ ",cstyle,extkeys,margins,usstyle,sync,osc7,hyperlinks"
},
{ .name = "XTerm",
/*
diff --git a/tty-keys.c b/tty-keys.c
index 64dd91b..ef80abc 100644
--- a/tty-keys.c
+++ b/tty-keys.c
@@ -55,8 +55,11 @@ static int tty_keys_clipboard(struct tty *, const char *, size_t,
size_t *);
static int tty_keys_device_attributes(struct tty *, const char *, size_t,
size_t *);
+static int tty_keys_device_attributes2(struct tty *, const char *, size_t,
+ size_t *);
static int tty_keys_extended_device_attributes(struct tty *, const char *,
size_t, size_t *);
+static int tty_keys_colours(struct tty *, const char *, size_t, size_t *);
/* A key tree entry. */
struct tty_key {
@@ -126,7 +129,7 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
{ "\033\033[C", KEYC_RIGHT|KEYC_CURSOR|KEYC_META },
{ "\033\033[D", KEYC_LEFT|KEYC_CURSOR|KEYC_META },
- /* Other (xterm) "cursor" keys. */
+ /* Other xterm keys. */
{ "\033OH", KEYC_HOME },
{ "\033OF", KEYC_END },
@@ -139,7 +142,7 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
{ "\033\033[H", KEYC_HOME|KEYC_META|KEYC_IMPLIED_META },
{ "\033\033[F", KEYC_END|KEYC_META|KEYC_IMPLIED_META },
- /* rxvt-style arrow + modifier keys. */
+ /* rxvt arrow keys. */
{ "\033Oa", KEYC_UP|KEYC_CTRL },
{ "\033Ob", KEYC_DOWN|KEYC_CTRL },
{ "\033Oc", KEYC_RIGHT|KEYC_CTRL },
@@ -150,7 +153,31 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
{ "\033[c", KEYC_RIGHT|KEYC_SHIFT },
{ "\033[d", KEYC_LEFT|KEYC_SHIFT },
- /* rxvt-style function + modifier keys (C = ^, S = $, C-S = @). */
+ /* rxvt function keys. */
+ { "\033[11~", KEYC_F1 },
+ { "\033[12~", KEYC_F2 },
+ { "\033[13~", KEYC_F3 },
+ { "\033[14~", KEYC_F4 },
+ { "\033[15~", KEYC_F5 },
+ { "\033[17~", KEYC_F6 },
+ { "\033[18~", KEYC_F7 },
+ { "\033[19~", KEYC_F8 },
+ { "\033[20~", KEYC_F9 },
+ { "\033[21~", KEYC_F10 },
+
+ { "\033[23~", KEYC_F1|KEYC_SHIFT },
+ { "\033[24~", KEYC_F2|KEYC_SHIFT },
+ { "\033[25~", KEYC_F3|KEYC_SHIFT },
+ { "\033[26~", KEYC_F4|KEYC_SHIFT },
+ { "\033[28~", KEYC_F5|KEYC_SHIFT },
+ { "\033[29~", KEYC_F6|KEYC_SHIFT },
+ { "\033[31~", KEYC_F7|KEYC_SHIFT },
+ { "\033[32~", KEYC_F8|KEYC_SHIFT },
+ { "\033[33~", KEYC_F9|KEYC_SHIFT },
+ { "\033[34~", KEYC_F10|KEYC_SHIFT },
+ { "\033[23$", KEYC_F11|KEYC_SHIFT },
+ { "\033[24$", KEYC_F12|KEYC_SHIFT },
+
{ "\033[11^", KEYC_F1|KEYC_CTRL },
{ "\033[12^", KEYC_F2|KEYC_CTRL },
{ "\033[13^", KEYC_F3|KEYC_CTRL },
@@ -163,31 +190,6 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
{ "\033[21^", KEYC_F10|KEYC_CTRL },
{ "\033[23^", KEYC_F11|KEYC_CTRL },
{ "\033[24^", KEYC_F12|KEYC_CTRL },
- { "\033[2^", KEYC_IC|KEYC_CTRL },
- { "\033[3^", KEYC_DC|KEYC_CTRL },
- { "\033[7^", KEYC_HOME|KEYC_CTRL },
- { "\033[8^", KEYC_END|KEYC_CTRL },
- { "\033[6^", KEYC_NPAGE|KEYC_CTRL },
- { "\033[5^", KEYC_PPAGE|KEYC_CTRL },
-
- { "\033[11$", KEYC_F1|KEYC_SHIFT },
- { "\033[12$", KEYC_F2|KEYC_SHIFT },
- { "\033[13$", KEYC_F3|KEYC_SHIFT },
- { "\033[14$", KEYC_F4|KEYC_SHIFT },
- { "\033[15$", KEYC_F5|KEYC_SHIFT },
- { "\033[17$", KEYC_F6|KEYC_SHIFT },
- { "\033[18$", KEYC_F7|KEYC_SHIFT },
- { "\033[19$", KEYC_F8|KEYC_SHIFT },
- { "\033[20$", KEYC_F9|KEYC_SHIFT },
- { "\033[21$", KEYC_F10|KEYC_SHIFT },
- { "\033[23$", KEYC_F11|KEYC_SHIFT },
- { "\033[24$", KEYC_F12|KEYC_SHIFT },
- { "\033[2$", KEYC_IC|KEYC_SHIFT },
- { "\033[3$", KEYC_DC|KEYC_SHIFT },
- { "\033[7$", KEYC_HOME|KEYC_SHIFT },
- { "\033[8$", KEYC_END|KEYC_SHIFT },
- { "\033[6$", KEYC_NPAGE|KEYC_SHIFT },
- { "\033[5$", KEYC_PPAGE|KEYC_SHIFT },
{ "\033[11@", KEYC_F1|KEYC_CTRL|KEYC_SHIFT },
{ "\033[12@", KEYC_F2|KEYC_CTRL|KEYC_SHIFT },
@@ -201,12 +203,6 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
{ "\033[21@", KEYC_F10|KEYC_CTRL|KEYC_SHIFT },
{ "\033[23@", KEYC_F11|KEYC_CTRL|KEYC_SHIFT },
{ "\033[24@", KEYC_F12|KEYC_CTRL|KEYC_SHIFT },
- { "\033[2@", KEYC_IC|KEYC_CTRL|KEYC_SHIFT },
- { "\033[3@", KEYC_DC|KEYC_CTRL|KEYC_SHIFT },
- { "\033[7@", KEYC_HOME|KEYC_CTRL|KEYC_SHIFT },
- { "\033[8@", KEYC_END|KEYC_CTRL|KEYC_SHIFT },
- { "\033[6@", KEYC_NPAGE|KEYC_CTRL|KEYC_SHIFT },
- { "\033[5@", KEYC_PPAGE|KEYC_CTRL|KEYC_SHIFT },
/* Focus tracking. */
{ "\033[I", KEYC_FOCUS_IN },
@@ -215,6 +211,9 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
/* Paste keys. */
{ "\033[200~", KEYC_PASTE_START },
{ "\033[201~", KEYC_PASTE_END },
+
+ /* Extended keys. */
+ { "\033[1;5Z", '\011'|KEYC_CTRL|KEYC_SHIFT },
};
/* Default xterm keys. */
@@ -688,7 +687,7 @@ tty_keys_next(struct tty *tty)
goto partial_key;
}
- /* Is this a device attributes response? */
+ /* Is this a primary device attributes response? */
switch (tty_keys_device_attributes(tty, buf, len, &size)) {
case 0: /* yes */
key = KEYC_UNKNOWN;
@@ -699,6 +698,17 @@ tty_keys_next(struct tty *tty)
goto partial_key;
}
+ /* Is this a secondary device attributes response? */
+ switch (tty_keys_device_attributes2(tty, buf, len, &size)) {
+ case 0: /* yes */
+ key = KEYC_UNKNOWN;
+ goto complete_key;
+ case -1: /* no, or not valid */
+ break;
+ case 1: /* partial */
+ goto partial_key;
+ }
+
/* Is this an extended device attributes response? */
switch (tty_keys_extended_device_attributes(tty, buf, len, &size)) {
case 0: /* yes */
@@ -710,6 +720,17 @@ tty_keys_next(struct tty *tty)
goto partial_key;
}
+ /* Is this a colours response? */
+ switch (tty_keys_colours(tty, buf, len, &size)) {
+ case 0: /* yes */
+ key = KEYC_UNKNOWN;
+ goto complete_key;
+ case -1: /* no, or not valid */
+ break;
+ case 1: /* partial */
+ goto partial_key;
+ }
+
/* Is this a mouse key press? */
switch (tty_keys_mouse(tty, buf, len, &size, &m)) {
case 0: /* yes */
@@ -798,6 +819,8 @@ partial_key:
/* Get the time period. */
delay = options_get_number(global_options, "escape-time");
+ if (delay == 0)
+ delay = 1;
tv.tv_sec = delay / 1000;
tv.tv_usec = (delay % 1000) * 1000L;
@@ -939,34 +962,16 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
nkey = number;
/* Update the modifiers. */
- switch (modifiers) {
- case 2:
- nkey |= KEYC_SHIFT;
- break;
- case 3:
- nkey |= (KEYC_META|KEYC_IMPLIED_META);
- break;
- case 4:
- nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META);
- break;
- case 5:
- nkey |= KEYC_CTRL;
- break;
- case 6:
- nkey |= (KEYC_SHIFT|KEYC_CTRL);
- break;
- case 7:
- nkey |= (KEYC_META|KEYC_CTRL);
- break;
- case 8:
- nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL);
- break;
- case 9:
- nkey |= (KEYC_META|KEYC_IMPLIED_META);
- break;
- default:
- *key = KEYC_NONE;
- break;
+ if (modifiers > 0) {
+ modifiers--;
+ if (modifiers & 1)
+ nkey |= KEYC_SHIFT;
+ if (modifiers & 2)
+ nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Alt */
+ if (modifiers & 4)
+ nkey |= KEYC_CTRL;
+ if (modifiers & 8)
+ nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Meta */
}
/*
@@ -1003,7 +1008,8 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
/*
* Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial
- * (probably a mouse sequence but need more data).
+ * (probably a mouse sequence but need more data), -2 if an invalid mouse
+ * sequence.
*/
static int
tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
@@ -1064,7 +1070,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
if (b < MOUSE_PARAM_BTN_OFF ||
x < MOUSE_PARAM_POS_OFF ||
y < MOUSE_PARAM_POS_OFF)
- return (-1);
+ return (-2);
b -= MOUSE_PARAM_BTN_OFF;
x -= MOUSE_PARAM_POS_OFF;
y -= MOUSE_PARAM_POS_OFF;
@@ -1106,7 +1112,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
/* Check and return the mouse input. */
if (x < 1 || y < 1)
- return (-1);
+ return (-2);
x--;
y--;
b = sgr_b;
@@ -1154,7 +1160,7 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
{
struct client *c = tty->client;
struct window_pane *wp;
- size_t end, terminator, needed;
+ size_t end, terminator = 0, needed;
char *copy, *out;
int outlen;
u_int i;
@@ -1255,7 +1261,7 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
}
/*
- * Handle secondary device attributes input. Returns 0 for success, -1 for
+ * Handle primary device attributes input. Returns 0 for success, -1 for
* failure, 1 for partial.
*/
static int
@@ -1263,17 +1269,100 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
size_t *size)
{
struct client *c = tty->client;
+ int *features = &c->term_features;
u_int i, n = 0;
- char tmp[64], *endptr, p[32] = { 0 }, *cp, *next;
+ char tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
*size = 0;
if (tty->flags & TTY_HAVEDA)
return (-1);
+ /* First three bytes are always \033[?. */
+ if (buf[0] != '\033')
+ return (-1);
+ if (len == 1)
+ return (1);
+ if (buf[1] != '[')
+ return (-1);
+ if (len == 2)
+ return (1);
+ if (buf[2] != '?')
+ return (-1);
+ if (len == 3)
+ return (1);
+
+ /* Copy the rest up to a c. */
+ for (i = 0; i < (sizeof tmp); i++) {
+ if (3 + i == len)
+ return (1);
+ if (buf[3 + i] == 'c')
+ break;
+ tmp[i] = buf[3 + i];
+ }
+ if (i == (sizeof tmp))
+ return (-1);
+ tmp[i] = '\0';
+ *size = 4 + i;
+
+ /* Convert all arguments to numbers. */
+ cp = tmp;
+ while ((next = strsep(&cp, ";")) != NULL) {
+ p[n] = strtoul(next, &endptr, 10);
+ if (*endptr != '\0')
+ p[n] = 0;
+ if (++n == nitems(p))
+ break;
+ }
+
/*
- * First three bytes are always \033[>. Some older Terminal.app
- * versions respond as for DA (\033[?) so accept and ignore that.
+ * Add terminal features. Hardware level 5 does not offer SIXEL but
+ * some terminal emulators report it anyway and it does not harm
+ * to check it here.
+ *
+ * DECSLRM and DECFRA should be supported by level 5 as well as level
+ * 4, but VTE has rather ruined it by advertising level 5 despite not
+ * supporting them.
*/
+ switch (p[0]) {
+ case 64: /* level 4 */
+ tty_add_features(features, "margins,rectfill", ",");
+ /* FALLTHROUGH */
+ case 62: /* level 2 */
+ case 63: /* level 3 */
+ case 65: /* level 5 */
+ for (i = 1; i < n; i++) {
+ log_debug("%s: DA feature: %d", c->name, p[i]);
+ if (p[i] == 4)
+ tty_add_features(features, "sixel", ",");
+ }
+ break;
+ }
+ log_debug("%s: received primary DA %.*s", c->name, (int)*size, buf);
+
+ tty_update_features(tty);
+ tty->flags |= TTY_HAVEDA;
+
+ return (0);
+}
+
+/*
+ * Handle secondary device attributes input. Returns 0 for success, -1 for
+ * failure, 1 for partial.
+ */
+static int
+tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
+ size_t *size)
+{
+ struct client *c = tty->client;
+ int *features = &c->term_features;
+ u_int i, n = 0;
+ char tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
+
+ *size = 0;
+ if (tty->flags & TTY_HAVEDA2)
+ return (-1);
+
+ /* First three bytes are always \033[>. */
if (buf[0] != '\033')
return (-1);
if (len == 1)
@@ -1282,56 +1371,59 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
return (-1);
if (len == 2)
return (1);
- if (buf[2] != '>' && buf[2] != '?')
+ if (buf[2] != '>')
return (-1);
if (len == 3)
return (1);
- /* Copy the rest up to a 'c'. */
- for (i = 0; i < (sizeof tmp) - 1; i++) {
+ /* Copy the rest up to a c. */
+ for (i = 0; i < (sizeof tmp); i++) {
if (3 + i == len)
return (1);
if (buf[3 + i] == 'c')
break;
tmp[i] = buf[3 + i];
}
- if (i == (sizeof tmp) - 1)
+ if (i == (sizeof tmp))
return (-1);
tmp[i] = '\0';
*size = 4 + i;
- /* Ignore DA response. */
- if (buf[2] == '?')
- return (0);
-
/* Convert all arguments to numbers. */
cp = tmp;
while ((next = strsep(&cp, ";")) != NULL) {
p[n] = strtoul(next, &endptr, 10);
if (*endptr != '\0')
p[n] = 0;
- n++;
+ if (++n == nitems(p))
+ break;
}
- /* Add terminal features. */
+ /*
+ * Add terminal features. We add DECSLRM and DECFRA for some
+ * identification codes here, notably 64 will catch VT520, even though
+ * we can't use level 5 from DA because of VTE.
+ */
switch (p[0]) {
case 41: /* VT420 */
- tty_add_features(&c->term_features, "margins,rectfill", ",");
+ case 61: /* VT510 */
+ case 64: /* VT520 */
+ tty_add_features(features, "margins,rectfill", ",");
break;
case 'M': /* mintty */
- tty_default_features(&c->term_features, "mintty", 0);
+ tty_default_features(features, "mintty", 0);
break;
case 'T': /* tmux */
- tty_default_features(&c->term_features, "tmux", 0);
+ tty_default_features(features, "tmux", 0);
break;
case 'U': /* rxvt-unicode */
- tty_default_features(&c->term_features, "rxvt-unicode", 0);
+ tty_default_features(features, "rxvt-unicode", 0);
break;
}
log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf);
tty_update_features(tty);
- tty->flags |= TTY_HAVEDA;
+ tty->flags |= TTY_HAVEDA2;
return (0);
}
@@ -1345,6 +1437,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
size_t len, size_t *size)
{
struct client *c = tty->client;
+ int *features = &c->term_features;
u_int i;
char tmp[128];
@@ -1370,7 +1463,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
if (len == 4)
return (1);
- /* Copy the rest up to a '\033\\'. */
+ /* Copy the rest up to \033\. */
for (i = 0; i < (sizeof tmp) - 1; i++) {
if (4 + i == len)
return (1);
@@ -1385,13 +1478,13 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
/* Add terminal features. */
if (strncmp(tmp, "iTerm2 ", 7) == 0)
- tty_default_features(&c->term_features, "iTerm2", 0);
+ tty_default_features(features, "iTerm2", 0);
else if (strncmp(tmp, "tmux ", 5) == 0)
- tty_default_features(&c->term_features, "tmux", 0);
+ tty_default_features(features, "tmux", 0);
else if (strncmp(tmp, "XTerm(", 6) == 0)
- tty_default_features(&c->term_features, "XTerm", 0);
+ tty_default_features(features, "XTerm", 0);
else if (strncmp(tmp, "mintty ", 7) == 0)
- tty_default_features(&c->term_features, "mintty", 0);
+ tty_default_features(features, "mintty", 0);
log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
free(c->term_type);
@@ -1402,3 +1495,69 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
return (0);
}
+
+/*
+ * Handle foreground or background input. Returns 0 for success, -1 for
+ * failure, 1 for partial.
+ */
+static int
+tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size)
+{
+ struct client *c = tty->client;
+ u_int i;
+ char tmp[128];
+ int n;
+
+ *size = 0;
+
+ /* First four bytes are always \033]1 and 0 or 1 and ;. */
+ if (buf[0] != '\033')
+ return (-1);
+ if (len == 1)
+ return (1);
+ if (buf[1] != ']')
+ return (-1);
+ if (len == 2)
+ return (1);
+ if (buf[2] != '1')
+ return (-1);
+ if (len == 3)
+ return (1);
+ if (buf[3] != '0' && buf[3] != '1')
+ return (-1);
+ if (len == 4)
+ return (1);
+ if (buf[4] != ';')
+ return (-1);
+ if (len == 5)
+ return (1);
+
+ /* Copy the rest up to \033\ or \007. */
+ for (i = 0; i < (sizeof tmp) - 1; i++) {
+ if (5 + i == len)
+ return (1);
+ if (buf[5 + i - 1] == '\033' && buf[5 + i] == '\\')
+ break;
+ if (buf[5 + i] == '\007')
+ break;
+ tmp[i] = buf[5 + i];
+ }
+ if (i == (sizeof tmp) - 1)
+ return (-1);
+ if (tmp[i - 1] == '\033')
+ tmp[i - 1] = '\0';
+ else
+ tmp[i] = '\0';
+ *size = 6 + i;
+
+ n = colour_parseX11(tmp);
+ if (n != -1 && buf[3] == '0') {
+ log_debug("%s: foreground is %s", c->name, colour_tostring(n));
+ tty->fg = n;
+ } else if (n != -1) {
+ log_debug("%s: background is %s", c->name, colour_tostring(n));
+ tty->bg = n;
+ }
+
+ return (0);
+}
diff --git a/tty-term.c b/tty-term.c
index fdf0c4f..67face2 100644
--- a/tty-term.c
+++ b/tty-term.c
@@ -103,6 +103,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_ENFCS] = { TTYCODE_STRING, "Enfcs" },
[TTYC_ENMG] = { TTYCODE_STRING, "Enmg" },
[TTYC_FSL] = { TTYCODE_STRING, "fsl" },
+ [TTYC_HLS] = { TTYCODE_STRING, "Hls" },
[TTYC_HOME] = { TTYCODE_STRING, "home" },
[TTYC_HPA] = { TTYCODE_STRING, "hpa" },
[TTYC_ICH1] = { TTYCODE_STRING, "ich1" },
@@ -249,6 +250,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_KUP6] = { TTYCODE_STRING, "kUP6" },
[TTYC_KUP7] = { TTYCODE_STRING, "kUP7" },
[TTYC_MS] = { TTYCODE_STRING, "Ms" },
+ [TTYC_NOBR] = { TTYCODE_STRING, "Nobr" },
[TTYC_OL] = { TTYCODE_STRING, "ol" },
[TTYC_OP] = { TTYCODE_STRING, "op" },
[TTYC_RECT] = { TTYCODE_STRING, "Rect" },
@@ -265,7 +267,9 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_SETRGBB] = { TTYCODE_STRING, "setrgbb" },
[TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" },
[TTYC_SETULC] = { TTYCODE_STRING, "Setulc" },
+ [TTYC_SETULC1] = { TTYCODE_STRING, "Setulc1" },
[TTYC_SE] = { TTYCODE_STRING, "Se" },
+ [TTYC_SXL] = { TTYCODE_FLAG, "Sxl" },
[TTYC_SGR0] = { TTYCODE_STRING, "sgr0" },
[TTYC_SITM] = { TTYCODE_STRING, "sitm" },
[TTYC_SMACS] = { TTYCODE_STRING, "smacs" },
@@ -454,6 +458,9 @@ tty_term_apply_overrides(struct tty_term *term)
a = options_array_next(a);
}
+ /* Log the SIXEL flag. */
+ log_debug("SIXEL flag is %d", !!(term->flags & TERM_SIXEL));
+
/* Update the RGB flag if the terminal has RGB colours. */
if (tty_term_has(term, TTYC_SETRGBF) &&
tty_term_has(term, TTYC_SETRGBB))
@@ -712,7 +719,7 @@ tty_term_read_list(const char *name, int fd, char ***caps, u_int *ncaps,
s = tmp;
break;
case TTYCODE_FLAG:
- n = tigetflag((char *) ent->name);
+ n = tigetflag((char *)ent->name);
if (n == -1)
continue;
if (n)
@@ -720,6 +727,8 @@ tty_term_read_list(const char *name, int fd, char ***caps, u_int *ncaps,
else
s = "0";
break;
+ default:
+ fatalx("unknown capability type");
}
*caps = xreallocarray(*caps, (*ncaps) + 1, sizeof **caps);
xasprintf(&(*caps)[*ncaps], "%s=%s", ent->name, s);
@@ -760,35 +769,100 @@ tty_term_string(struct tty_term *term, enum tty_code_code code)
}
const char *
-tty_term_string1(struct tty_term *term, enum tty_code_code code, int a)
+tty_term_string_i(struct tty_term *term, enum tty_code_code code, int a)
{
- return (tparm((char *) tty_term_string(term, code), a, 0, 0, 0, 0, 0, 0, 0, 0));
+ const char *x = tty_term_string(term, code), *s;
+
+#if defined(HAVE_TIPARM_S)
+ s = tiparm_s(1, 0, x, a);
+#elif defined(HAVE_TIPARM)
+ s = tiparm(x, a);
+#else
+ s = tparm((char *)x, a, 0, 0, 0, 0, 0, 0, 0, 0);
+#endif
+ if (s == NULL) {
+ log_debug("could not expand %s", tty_term_codes[code].name);
+ return ("");
+ }
+ return (s);
}
const char *
-tty_term_string2(struct tty_term *term, enum tty_code_code code, int a, int b)
+tty_term_string_ii(struct tty_term *term, enum tty_code_code code, int a, int b)
{
- return (tparm((char *) tty_term_string(term, code), a, b, 0, 0, 0, 0, 0, 0, 0));
+ const char *x = tty_term_string(term, code), *s;
+
+#if defined(HAVE_TIPARM_S)
+ s = tiparm_s(2, 0, x, a, b);
+#elif defined(HAVE_TIPARM)
+ s = tiparm(x, a, b);
+#else
+ s = tparm((char *)x, a, b, 0, 0, 0, 0, 0, 0, 0);
+#endif
+ if (s == NULL) {
+ log_debug("could not expand %s", tty_term_codes[code].name);
+ return ("");
+ }
+ return (s);
}
const char *
-tty_term_string3(struct tty_term *term, enum tty_code_code code, int a, int b,
- int c)
+tty_term_string_iii(struct tty_term *term, enum tty_code_code code, int a,
+ int b, int c)
{
- return (tparm((char *) tty_term_string(term, code), a, b, c, 0, 0, 0, 0, 0, 0));
+ const char *x = tty_term_string(term, code), *s;
+
+#if defined(HAVE_TIPARM_S)
+ s = tiparm_s(3, 0, x, a, b, c);
+#elif defined(HAVE_TIPARM)
+ s = tiparm(x, a, b, c);
+#else
+ s = tparm((char *)x, a, b, c, 0, 0, 0, 0, 0, 0);
+#endif
+ if (s == NULL) {
+ log_debug("could not expand %s", tty_term_codes[code].name);
+ return ("");
+ }
+ return (s);
}
const char *
-tty_term_ptr1(struct tty_term *term, enum tty_code_code code, const void *a)
+tty_term_string_s(struct tty_term *term, enum tty_code_code code, const char *a)
{
- return (tparm((char *) tty_term_string(term, code), (long)a, 0, 0, 0, 0, 0, 0, 0, 0));
+ const char *x = tty_term_string(term, code), *s;
+
+#if defined(HAVE_TIPARM_S)
+ s = tiparm_s(1, 1, x, a);
+#elif defined(HAVE_TIPARM)
+ s = tiparm(x, a);
+#else
+ s = tparm((char *)x, (long)a, 0, 0, 0, 0, 0, 0, 0, 0);
+#endif
+ if (s == NULL) {
+ log_debug("could not expand %s", tty_term_codes[code].name);
+ return ("");
+ }
+ return (s);
}
const char *
-tty_term_ptr2(struct tty_term *term, enum tty_code_code code, const void *a,
- const void *b)
+tty_term_string_ss(struct tty_term *term, enum tty_code_code code,
+ const char *a, const char *b)
{
- return (tparm((char *) tty_term_string(term, code), (long)a, (long)b, 0, 0, 0, 0, 0, 0, 0));
+ const char *x = tty_term_string(term, code), *s;
+
+#if defined(HAVE_TIPARM_S)
+ s = tiparm_s(2, 3, x, a, b);
+#elif defined(HAVE_TIPARM)
+ s = tiparm(x, a, b);
+#else
+ s = tparm((char *)x, (long)a, (long)b, 0, 0, 0, 0, 0, 0, 0);
+#endif
+ if (s == NULL) {
+ log_debug("could not expand %s", tty_term_codes[code].name);
+ return ("");
+ }
+ return (s);
}
int
diff --git a/tty.c b/tty.c
index 49cf979..359dc13 100644
--- a/tty.c
+++ b/tty.c
@@ -34,8 +34,6 @@
static int tty_log_fd = -1;
-static int tty_client_ready(struct client *);
-
static void tty_set_italics(struct tty *);
static int tty_try_colour(struct tty *, int, const char *);
static void tty_force_cursor_colour(struct tty *, int);
@@ -69,11 +67,16 @@ static void tty_emulate_repeat(struct tty *, enum tty_code_code,
static void tty_repeat_space(struct tty *, u_int);
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
static void tty_default_attributes(struct tty *, const struct grid_cell *,
- struct colour_palette *, u_int);
+ struct colour_palette *, u_int, struct hyperlinks *);
static int tty_check_overlay(struct tty *, u_int, u_int);
static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int,
struct overlay_ranges *);
+#ifdef ENABLE_SIXEL
+static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
+ struct client *, struct tty_ctx *);
+#endif
+
#define tty_use_margin(tty) \
(tty->term->flags & TERM_DECSLRM)
#define tty_full_width(tty, ctx) \
@@ -84,6 +87,7 @@ static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int,
#define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8)
#define TTY_QUERY_TIMEOUT 5
+#define TTY_REQUEST_LIMIT 30
void
tty_create_log(void)
@@ -108,6 +112,7 @@ tty_init(struct tty *tty, struct client *c)
tty->cstyle = SCREEN_CURSOR_DEFAULT;
tty->ccolour = -1;
+ tty->fg = tty->bg = -1;
if (tcgetattr(c->fd, &tty->tio) != 0)
return (-1);
@@ -286,7 +291,6 @@ tty_open(struct tty *tty, char **cause)
evtimer_set(&tty->timer, tty_timer_callback, tty);
tty_start_tty(tty);
-
tty_keys_build(tty);
return (0);
@@ -299,9 +303,9 @@ tty_start_timer_callback(__unused int fd, __unused short events, void *data)
struct client *c = tty->client;
log_debug("%s: start timer fired", c->name);
- if ((tty->flags & (TTY_HAVEDA|TTY_HAVEXDA)) == 0)
+ if ((tty->flags & (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)) == 0)
tty_update_features(tty);
- tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA);
+ tty->flags |= TTY_ALL_REQUEST_FLAGS;
}
void
@@ -341,6 +345,8 @@ tty_start_tty(struct tty *tty)
tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l");
tty_puts(tty, "\033[?1006l\033[?1005l");
}
+ if (tty_term_has(tty->term, TTYC_ENBP))
+ tty_putcode(tty, TTYC_ENBP);
evtimer_set(&tty->start_timer, tty_start_timer_callback, tty);
evtimer_add(&tty->start_timer, &tv);
@@ -363,12 +369,35 @@ tty_send_requests(struct tty *tty)
return;
if (tty->term->flags & TERM_VT100LIKE) {
- if (~tty->flags & TTY_HAVEDA)
+ if (~tty->term->flags & TTY_HAVEDA)
+ tty_puts(tty, "\033[c");
+ if (~tty->flags & TTY_HAVEDA2)
tty_puts(tty, "\033[>c");
if (~tty->flags & TTY_HAVEXDA)
tty_puts(tty, "\033[>q");
+ tty_puts(tty, "\033]10;?\033\\");
+ tty_puts(tty, "\033]11;?\033\\");
} else
- tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA);
+ tty->flags |= TTY_ALL_REQUEST_FLAGS;
+ tty->last_requests = time (NULL);
+}
+
+void
+tty_repeat_requests(struct tty *tty)
+{
+ time_t t = time (NULL);
+
+ if (~tty->flags & TTY_STARTED)
+ return;
+
+ if (t - tty->last_requests <= TTY_REQUEST_LIMIT)
+ return;
+ tty->last_requests = t;
+
+ if (tty->term->flags & TERM_VT100LIKE) {
+ tty_puts(tty, "\033]10;?\033\\");
+ tty_puts(tty, "\033]11;?\033\\");
+ }
}
void
@@ -399,7 +428,7 @@ tty_stop_tty(struct tty *tty)
if (tcsetattr(c->fd, TCSANOW, &tty->tio) == -1)
return;
- tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
+ tty_raw(tty, tty_term_string_ii(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
if (tty_acs_needed(tty))
tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
@@ -409,10 +438,8 @@ tty_stop_tty(struct tty *tty)
if (tty_term_has(tty->term, TTYC_SE))
tty_raw(tty, tty_term_string(tty->term, TTYC_SE));
else if (tty_term_has(tty->term, TTYC_SS))
- tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0));
+ tty_raw(tty, tty_term_string_i(tty->term, TTYC_SS, 0));
}
- if (tty->mode & MODE_BRACKETPASTE)
- tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP));
if (tty->ccolour != -1)
tty_raw(tty, tty_term_string(tty->term, TTYC_CR));
@@ -421,6 +448,8 @@ tty_stop_tty(struct tty *tty)
tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l");
tty_raw(tty, "\033[?1006l\033[?1005l");
}
+ if (tty_term_has(tty->term, TTYC_DSBP))
+ tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP));
if (tty->term->flags & TERM_VT100LIKE)
tty_raw(tty, "\033[?7727l");
@@ -476,6 +505,14 @@ tty_update_features(struct tty *tty)
tty_puts(tty, tty_term_string(tty->term, TTYC_ENFCS));
if (tty->term->flags & TERM_VT100LIKE)
tty_puts(tty, "\033[?7727h");
+
+ /*
+ * Features might have changed since the first draw during attach. For
+ * example, this happens when DA responses are received.
+ */
+ server_redraw_client(c);
+
+ tty_invalidate(tty);
}
void
@@ -506,42 +543,42 @@ tty_putcode(struct tty *tty, enum tty_code_code code)
}
void
-tty_putcode1(struct tty *tty, enum tty_code_code code, int a)
+tty_putcode_i(struct tty *tty, enum tty_code_code code, int a)
{
if (a < 0)
return;
- tty_puts(tty, tty_term_string1(tty->term, code, a));
+ tty_puts(tty, tty_term_string_i(tty->term, code, a));
}
void
-tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b)
+tty_putcode_ii(struct tty *tty, enum tty_code_code code, int a, int b)
{
if (a < 0 || b < 0)
return;
- tty_puts(tty, tty_term_string2(tty->term, code, a, b));
+ tty_puts(tty, tty_term_string_ii(tty->term, code, a, b));
}
void
-tty_putcode3(struct tty *tty, enum tty_code_code code, int a, int b, int c)
+tty_putcode_iii(struct tty *tty, enum tty_code_code code, int a, int b, int c)
{
if (a < 0 || b < 0 || c < 0)
return;
- tty_puts(tty, tty_term_string3(tty->term, code, a, b, c));
+ tty_puts(tty, tty_term_string_iii(tty->term, code, a, b, c));
}
void
-tty_putcode_ptr1(struct tty *tty, enum tty_code_code code, const void *a)
+tty_putcode_s(struct tty *tty, enum tty_code_code code, const char *a)
{
if (a != NULL)
- tty_puts(tty, tty_term_ptr1(tty->term, code, a));
+ tty_puts(tty, tty_term_string_s(tty->term, code, a));
}
void
-tty_putcode_ptr2(struct tty *tty, enum tty_code_code code, const void *a,
- const void *b)
+tty_putcode_ss(struct tty *tty, enum tty_code_code code, const char *a,
+ const char *b)
{
if (a != NULL && b != NULL)
- tty_puts(tty, tty_term_ptr2(tty->term, code, a, b));
+ tty_puts(tty, tty_term_string_ss(tty->term, code, a, b));
}
static void
@@ -603,7 +640,7 @@ tty_putc(struct tty *tty, u_char ch)
* it works on sensible terminals as well.
*/
if (tty->term->flags & TERM_NOAM)
- tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx);
+ tty_putcode_ii(tty, TTYC_CUP, tty->cy, tty->cx);
} else
tty->cx++;
}
@@ -671,7 +708,7 @@ static void
tty_force_cursor_colour(struct tty *tty, int c)
{
u_char r, g, b;
- char s[13] = "";
+ char s[13];
if (c != -1)
c = colour_force_rgb(c);
@@ -682,7 +719,7 @@ tty_force_cursor_colour(struct tty *tty, int c)
else {
colour_split_rgb(c, &r, &g, &b);
xsnprintf(s, sizeof s, "rgb:%02hhx/%02hhx/%02hhx", r, g, b);
- tty_putcode_ptr1(tty, TTYC_CS, s);
+ tty_putcode_s(tty, TTYC_CS, s);
}
tty->ccolour = c;
}
@@ -743,7 +780,7 @@ tty_update_cursor(struct tty *tty, int mode, struct screen *s)
if (tty_term_has(tty->term, TTYC_SE))
tty_putcode(tty, TTYC_SE);
else
- tty_putcode1(tty, TTYC_SS, 0);
+ tty_putcode_i(tty, TTYC_SS, 0);
}
if (cmode & (MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE))
tty_putcode(tty, TTYC_CVVIS);
@@ -751,27 +788,27 @@ tty_update_cursor(struct tty *tty, int mode, struct screen *s)
case SCREEN_CURSOR_BLOCK:
if (tty_term_has(tty->term, TTYC_SS)) {
if (cmode & MODE_CURSOR_BLINKING)
- tty_putcode1(tty, TTYC_SS, 1);
+ tty_putcode_i(tty, TTYC_SS, 1);
else
- tty_putcode1(tty, TTYC_SS, 2);
+ tty_putcode_i(tty, TTYC_SS, 2);
} else if (cmode & MODE_CURSOR_BLINKING)
tty_putcode(tty, TTYC_CVVIS);
break;
case SCREEN_CURSOR_UNDERLINE:
if (tty_term_has(tty->term, TTYC_SS)) {
if (cmode & MODE_CURSOR_BLINKING)
- tty_putcode1(tty, TTYC_SS, 3);
+ tty_putcode_i(tty, TTYC_SS, 3);
else
- tty_putcode1(tty, TTYC_SS, 4);
+ tty_putcode_i(tty, TTYC_SS, 4);
} else if (cmode & MODE_CURSOR_BLINKING)
tty_putcode(tty, TTYC_CVVIS);
break;
case SCREEN_CURSOR_BAR:
if (tty_term_has(tty->term, TTYC_SS)) {
if (cmode & MODE_CURSOR_BLINKING)
- tty_putcode1(tty, TTYC_SS, 5);
+ tty_putcode_i(tty, TTYC_SS, 5);
else
- tty_putcode1(tty, TTYC_SS, 6);
+ tty_putcode_i(tty, TTYC_SS, 6);
} else if (cmode & MODE_CURSOR_BLINKING)
tty_putcode(tty, TTYC_CVVIS);
break;
@@ -819,12 +856,6 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s)
else if (mode & MODE_MOUSE_STANDARD)
tty_puts(tty, "\033[?1000h");
}
- if (changed & MODE_BRACKETPASTE) {
- if (mode & MODE_BRACKETPASTE)
- tty_putcode(tty, TTYC_ENBP);
- else
- tty_putcode(tty, TTYC_DSBP);
- }
tty->mode = mode;
}
@@ -833,7 +864,7 @@ tty_emulate_repeat(struct tty *tty, enum tty_code_code code,
enum tty_code_code code1, u_int n)
{
if (tty_term_has(tty->term, code))
- tty_putcode1(tty, code, n);
+ tty_putcode_i(tty, code, n);
else {
while (n-- > 0)
tty_putcode(tty, code1);
@@ -1122,7 +1153,7 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
/* Section of line. Use ECH if possible. */
if (tty_term_has(tty->term, TTYC_ECH)) {
tty_cursor(tty, px, py);
- tty_putcode1(tty, TTYC_ECH, nx);
+ tty_putcode_i(tty, TTYC_ECH, nx);
return;
}
}
@@ -1263,7 +1294,7 @@ tty_clear_area(struct tty *tty, const struct grid_cell *defaults, u_int py,
tty_term_has(tty->term, TTYC_INDN)) {
tty_region(tty, py, py + ny - 1);
tty_margin_off(tty);
- tty_putcode1(tty, TTYC_INDN, ny);
+ tty_putcode_i(tty, TTYC_INDN, ny);
return;
}
@@ -1278,7 +1309,7 @@ tty_clear_area(struct tty *tty, const struct grid_cell *defaults, u_int py,
tty_term_has(tty->term, TTYC_INDN)) {
tty_region(tty, py, py + ny - 1);
tty_margin(tty, px, px + nx - 1);
- tty_putcode1(tty, TTYC_INDN, ny);
+ tty_putcode_i(tty, TTYC_INDN, ny);
return;
}
}
@@ -1455,7 +1486,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
tty_term_has(tty->term, TTYC_EL1) &&
!tty_fake_bce(tty, defaults, 8) &&
c->overlay_check == NULL) {
- tty_default_attributes(tty, defaults, palette, 8);
+ tty_default_attributes(tty, defaults, palette, 8,
+ s->hyperlinks);
tty_cursor(tty, nx - 1, aty);
tty_putcode(tty, TTYC_EL1);
cleared = 1;
@@ -1480,9 +1512,11 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
gcp->fg != last.fg ||
gcp->bg != last.bg ||
gcp->us != last.us ||
+ gcp->link != last.link ||
ux + width + gcp->data.width > nx ||
(sizeof buf) - len < gcp->data.size)) {
- tty_attributes(tty, &last, defaults, palette);
+ tty_attributes(tty, &last, defaults, palette,
+ s->hyperlinks);
if (last.flags & GRID_FLAG_CLEARED) {
log_debug("%s: %zu cleared", __func__, len);
tty_clear_line(tty, defaults, aty, atx + ux,
@@ -1515,7 +1549,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
ux += gcp->data.width;
} else if (hidden != 0 || ux + gcp->data.width > nx) {
if (~gcp->flags & GRID_FLAG_PADDING) {
- tty_attributes(tty, &last, defaults, palette);
+ tty_attributes(tty, &last, defaults, palette,
+ s->hyperlinks);
for (j = 0; j < OVERLAY_MAX_RANGES; j++) {
if (r.nx[j] == 0)
continue;
@@ -1532,7 +1567,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
}
}
} else if (gcp->attr & GRID_ATTR_CHARSET) {
- tty_attributes(tty, &last, defaults, palette);
+ tty_attributes(tty, &last, defaults, palette,
+ s->hyperlinks);
tty_cursor(tty, atx + ux, aty);
for (j = 0; j < gcp->data.size; j++)
tty_putc(tty, gcp->data.data[j]);
@@ -1544,7 +1580,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
}
}
if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) {
- tty_attributes(tty, &last, defaults, palette);
+ tty_attributes(tty, &last, defaults, palette, s->hyperlinks);
if (last.flags & GRID_FLAG_CLEARED) {
log_debug("%s: %zu cleared (end)", __func__, len);
tty_clear_line(tty, defaults, aty, atx + ux, width,
@@ -1560,7 +1596,8 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
if (!cleared && ux < nx) {
log_debug("%s: %u to end of line (%zu cleared)", __func__,
nx - ux, len);
- tty_default_attributes(tty, defaults, palette, 8);
+ tty_default_attributes(tty, defaults, palette, 8,
+ s->hyperlinks);
tty_clear_line(tty, defaults, aty, atx + ux, nx - ux, 8);
}
@@ -1568,6 +1605,58 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
tty_update_mode(tty, tty->mode, s);
}
+#ifdef ENABLE_SIXEL
+/* Update context for client. */
+static int
+tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
+{
+ struct window_pane *wp = ttyctx->arg;
+
+ if (c->session->curw->window != wp->window)
+ return (0);
+ if (wp->layout_cell == NULL)
+ return (0);
+
+ /* Set the properties relevant to the current client. */
+ ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy,
+ &ttyctx->wsx, &ttyctx->wsy);
+
+ ttyctx->yoff = ttyctx->ryoff = wp->yoff;
+ if (status_at_line(c) == 0)
+ ttyctx->yoff += status_line_size(c);
+
+ return (1);
+}
+
+void
+tty_draw_images(struct client *c, struct window_pane *wp, struct screen *s)
+{
+ struct image *im;
+ struct tty_ctx ttyctx;
+
+ TAILQ_FOREACH(im, &s->images, entry) {
+ memset(&ttyctx, 0, sizeof ttyctx);
+
+ /* Set the client independent properties. */
+ ttyctx.ocx = im->px;
+ ttyctx.ocy = im->py;
+
+ ttyctx.orlower = s->rlower;
+ ttyctx.orupper = s->rupper;
+
+ ttyctx.xoff = ttyctx.rxoff = wp->xoff;
+ ttyctx.sx = wp->sx;
+ ttyctx.sy = wp->sy;
+
+ ttyctx.ptr = im;
+ ttyctx.arg = wp;
+ ttyctx.set_client_cb = tty_set_client_cb;
+ ttyctx.allow_invisible_panes = 1;
+ tty_write_one(tty_cmd_sixelimage, c, &ttyctx);
+ }
+}
+#endif
+
void
tty_sync_start(struct tty *tty)
{
@@ -1579,7 +1668,7 @@ tty_sync_start(struct tty *tty)
if (tty_term_has(tty->term, TTYC_SYNC)) {
log_debug("%s sync start", tty->client->name);
- tty_putcode1(tty, TTYC_SYNC, 1);
+ tty_putcode_i(tty, TTYC_SYNC, 1);
}
}
@@ -1594,16 +1683,26 @@ tty_sync_end(struct tty *tty)
if (tty_term_has(tty->term, TTYC_SYNC)) {
log_debug("%s sync end", tty->client->name);
- tty_putcode1(tty, TTYC_SYNC, 2);
+ tty_putcode_i(tty, TTYC_SYNC, 2);
}
}
static int
-tty_client_ready(struct client *c)
+tty_client_ready(const struct tty_ctx *ctx, struct client *c)
{
if (c->session == NULL || c->tty.term == NULL)
return (0);
- if (c->flags & (CLIENT_REDRAWWINDOW|CLIENT_SUSPENDED))
+ if (c->flags & CLIENT_SUSPENDED)
+ return (0);
+
+ /*
+ * If invisible panes are allowed (used for passthrough), don't care if
+ * redrawing or frozen.
+ */
+ if (ctx->allow_invisible_panes)
+ return (1);
+
+ if (c->flags & CLIENT_REDRAWWINDOW)
return (0);
if (c->tty.flags & TTY_FREEZE)
return (0);
@@ -1620,17 +1719,30 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
if (ctx->set_client_cb == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
- if (!tty_client_ready(c))
- continue;
- state = ctx->set_client_cb(ctx, c);
- if (state == -1)
- break;
- if (state == 0)
- continue;
- cmdfn(&c->tty, ctx);
+ if (tty_client_ready(ctx, c)) {
+ state = ctx->set_client_cb(ctx, c);
+ if (state == -1)
+ break;
+ if (state == 0)
+ continue;
+ cmdfn(&c->tty, ctx);
+ }
}
}
+#ifdef ENABLE_SIXEL
+/* Only write to the incoming tty instead of every client. */
+static void
+tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *),
+ struct client *c, struct tty_ctx *ctx)
+{
+ if (ctx->set_client_cb == NULL)
+ return;
+ if ((ctx->set_client_cb(ctx, c)) == 1)
+ cmdfn(&c->tty, ctx);
+}
+#endif
+
void
tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
{
@@ -1646,7 +1758,8 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
@@ -1668,7 +1781,8 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
@@ -1678,7 +1792,8 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
{
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->num, ctx->bg);
}
@@ -1700,7 +1815,8 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_off(tty);
@@ -1727,7 +1843,8 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_off(tty);
@@ -1740,7 +1857,8 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
{
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg);
}
@@ -1750,7 +1868,8 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
{
u_int nx = ctx->sx - ctx->ocx;
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg);
}
@@ -1758,7 +1877,8 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
{
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg);
}
@@ -1784,7 +1904,8 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
@@ -1793,7 +1914,7 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
if (tty_term_has(tty->term, TTYC_RI))
tty_putcode(tty, TTYC_RI);
else
- tty_putcode1(tty, TTYC_RIN, 1);
+ tty_putcode_i(tty, TTYC_RIN, 1);
}
void
@@ -1815,7 +1936,8 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
@@ -1855,7 +1977,8 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
@@ -1872,7 +1995,7 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
tty_cursor(tty, 0, 0);
else
tty_cursor(tty, 0, tty->cy);
- tty_putcode1(tty, TTYC_INDN, ctx->num);
+ tty_putcode_i(tty, TTYC_INDN, ctx->num);
}
}
@@ -1895,14 +2018,15 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx)
return;
}
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
tty_margin_pane(tty, ctx);
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
if (tty_term_has(tty->term, TTYC_RIN))
- tty_putcode1(tty, TTYC_RIN, ctx->num);
+ tty_putcode_i(tty, TTYC_RIN, ctx->num);
else {
for (i = 0; i < ctx->num; i++)
tty_putcode(tty, TTYC_RI);
@@ -1914,7 +2038,8 @@ tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
{
u_int px, py, nx, ny;
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, 0, ctx->sy - 1);
tty_margin_off(tty);
@@ -1938,7 +2063,8 @@ tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
{
u_int px, py, nx, ny;
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, 0, ctx->sy - 1);
tty_margin_off(tty);
@@ -1962,7 +2088,8 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
{
u_int px, py, nx, ny;
- tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
+ tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, 0, ctx->sy - 1);
tty_margin_off(tty);
@@ -1985,7 +2112,8 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
return;
}
- tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette);
+ tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette,
+ ctx->s->hyperlinks);
tty_region_pane(tty, ctx, 0, ctx->sy - 1);
tty_margin_off(tty);
@@ -2031,7 +2159,11 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
tty_margin_off(tty);
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
- tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette);
+ tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette,
+ ctx->s->hyperlinks);
+
+ if (ctx->num == 1)
+ tty_invalidate(tty);
}
void
@@ -2062,7 +2194,7 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
tty_margin_off(tty);
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
- tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette);
+ tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette, ctx->s->hyperlinks);
/* Get tty position from pane position for overlay check. */
px = ctx->xoff + ctx->ocx - ctx->wox;
@@ -2082,11 +2214,12 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx)
{
- tty_set_selection(tty, ctx->ptr, ctx->num);
+ tty_set_selection(tty, ctx->ptr2, ctx->ptr, ctx->num);
}
void
-tty_set_selection(struct tty *tty, const char *buf, size_t len)
+tty_set_selection(struct tty *tty, const char *flags, const char *buf,
+ size_t len)
{
char *encoded;
size_t size;
@@ -2101,7 +2234,7 @@ tty_set_selection(struct tty *tty, const char *buf, size_t len)
b64_ntop(buf, len, encoded, size);
tty->flags |= TTY_NOBLOCK;
- tty_putcode_ptr2(tty, TTYC_MS, "", encoded);
+ tty_putcode_ss(tty, TTYC_MS, flags, encoded);
free(encoded);
}
@@ -2114,6 +2247,58 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
tty_invalidate(tty);
}
+#ifdef ENABLE_SIXEL
+void
+tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
+{
+ struct image *im = ctx->ptr;
+ struct sixel_image *si = im->data;
+ struct sixel_image *new;
+ char *data;
+ size_t size;
+ u_int cx = ctx->ocx, cy = ctx->ocy, sx, sy;
+ u_int i, j, x, y, rx, ry;
+ int fallback = 0;
+
+ if ((~tty->term->flags & TERM_SIXEL) &&
+ !tty_term_has(tty->term, TTYC_SXL))
+ fallback = 1;
+ if (tty->xpixel == 0 || tty->ypixel == 0)
+ fallback = 1;
+
+ sixel_size_in_cells(si, &sx, &sy);
+ log_debug("%s: image is %ux%u", __func__, sx, sy);
+ if (!tty_clamp_area(tty, ctx, cx, cy, sx, sy, &i, &j, &x, &y, &rx, &ry))
+ return;
+ log_debug("%s: clamping to %u,%u-%u,%u", __func__, i, j, rx, ry);
+
+ if (fallback == 1) {
+ data = xstrdup(im->fallback);
+ size = strlen(data);
+ } else {
+ new = sixel_scale(si, tty->xpixel, tty->ypixel, i, j, rx, ry, 0);
+ if (new == NULL)
+ return;
+
+ data = sixel_print(new, si, &size);
+ }
+ if (data != NULL) {
+ log_debug("%s: %zu bytes: %s", __func__, size, data);
+ tty_region_off(tty);
+ tty_margin_off(tty);
+ tty_cursor(tty, x, y);
+
+ tty->flags |= TTY_NOBLOCK;
+ tty_add(tty, data, size);
+ tty_invalidate(tty);
+ free(data);
+ }
+
+ if (fallback == 0)
+ sixel_free(new);
+}
+#endif
+
void
tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
{
@@ -2135,7 +2320,8 @@ tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cell(struct tty *tty, const struct grid_cell *gc,
- const struct grid_cell *defaults, struct colour_palette *palette)
+ const struct grid_cell *defaults, struct colour_palette *palette,
+ struct hyperlinks *hl)
{
const struct grid_cell *gcp;
@@ -2151,11 +2337,11 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
/* Check the output codeset and apply attributes. */
gcp = tty_check_codeset(tty, gc);
- tty_attributes(tty, gcp, defaults, palette);
+ tty_attributes(tty, gcp, defaults, palette, hl);
/* If it is a single character, write with putc to handle ACS. */
if (gcp->data.size == 1) {
- tty_attributes(tty, gcp, defaults, palette);
+ tty_attributes(tty, gcp, defaults, palette, hl);
if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f)
return;
tty_putc(tty, *gcp->data.data);
@@ -2172,6 +2358,8 @@ tty_reset(struct tty *tty)
struct grid_cell *gc = &tty->cell;
if (!grid_cells_equal(gc, &grid_default_cell)) {
+ if (gc->link != 0)
+ tty_putcode_ss(tty, TTYC_HLS, "", "");
if ((gc->attr & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
tty_putcode(tty, TTYC_RMACS);
tty_putcode(tty, TTYC_SGR0);
@@ -2246,7 +2434,7 @@ tty_region(struct tty *tty, u_int rupper, u_int rlower)
tty_cursor(tty, 0, tty->cy);
}
- tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower);
+ tty_putcode_ii(tty, TTYC_CSR, tty->rupper, tty->rlower);
tty->cx = tty->cy = UINT_MAX;
}
@@ -2274,7 +2462,7 @@ tty_margin(struct tty *tty, u_int rleft, u_int rright)
if (tty->rleft == rleft && tty->rright == rright)
return;
- tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower);
+ tty_putcode_ii(tty, TTYC_CSR, tty->rupper, tty->rlower);
tty->rleft = rleft;
tty->rright = rright;
@@ -2282,7 +2470,7 @@ tty_margin(struct tty *tty, u_int rleft, u_int rright)
if (rleft == 0 && rright == tty->sx - 1)
tty_putcode(tty, TTYC_CLMG);
else
- tty_putcode2(tty, TTYC_CMG, rleft, rright);
+ tty_putcode_ii(tty, TTYC_CMG, rleft, rright);
tty->cx = tty->cy = UINT_MAX;
}
@@ -2392,7 +2580,7 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy)
* the cursor with CUB/CUF.
*/
if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) {
- tty_putcode1(tty, TTYC_HPA, cx);
+ tty_putcode_i(tty, TTYC_HPA, cx);
goto out;
} else if (change > 0 &&
tty_term_has(term, TTYC_CUB) &&
@@ -2402,12 +2590,12 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy)
tty_putcode(tty, TTYC_CUB1);
goto out;
}
- tty_putcode1(tty, TTYC_CUB, change);
+ tty_putcode_i(tty, TTYC_CUB, change);
goto out;
} else if (change < 0 &&
tty_term_has(term, TTYC_CUF) &&
!tty_use_margin(tty)) {
- tty_putcode1(tty, TTYC_CUF, -change);
+ tty_putcode_i(tty, TTYC_CUF, -change);
goto out;
}
} else if (cx == thisx) {
@@ -2440,30 +2628,50 @@ tty_cursor(struct tty *tty, u_int cx, u_int cy)
(change < 0 && cy - change > tty->rlower) ||
(change > 0 && cy - change < tty->rupper)) {
if (tty_term_has(term, TTYC_VPA)) {
- tty_putcode1(tty, TTYC_VPA, cy);
+ tty_putcode_i(tty, TTYC_VPA, cy);
goto out;
}
} else if (change > 0 && tty_term_has(term, TTYC_CUU)) {
- tty_putcode1(tty, TTYC_CUU, change);
+ tty_putcode_i(tty, TTYC_CUU, change);
goto out;
} else if (change < 0 && tty_term_has(term, TTYC_CUD)) {
- tty_putcode1(tty, TTYC_CUD, -change);
+ tty_putcode_i(tty, TTYC_CUD, -change);
goto out;
}
}
absolute:
/* Absolute movement. */
- tty_putcode2(tty, TTYC_CUP, cy, cx);
+ tty_putcode_ii(tty, TTYC_CUP, cy, cx);
out:
tty->cx = cx;
tty->cy = cy;
}
+static void
+tty_hyperlink(struct tty *tty, const struct grid_cell *gc,
+ struct hyperlinks *hl)
+{
+ const char *uri, *id;
+
+ if (gc->link == tty->cell.link)
+ return;
+ tty->cell.link = gc->link;
+
+ if (hl == NULL)
+ return;
+
+ if (gc->link == 0 || !hyperlinks_get(hl, gc->link, &uri, NULL, &id))
+ tty_putcode_ss(tty, TTYC_HLS, "", "");
+ else
+ tty_putcode_ss(tty, TTYC_HLS, id, uri);
+}
+
void
tty_attributes(struct tty *tty, const struct grid_cell *gc,
- const struct grid_cell *defaults, struct colour_palette *palette)
+ const struct grid_cell *defaults, struct colour_palette *palette,
+ struct hyperlinks *hl)
{
struct grid_cell *tc = &tty->cell, gc2;
int changed;
@@ -2481,7 +2689,8 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
if (gc2.attr == tty->last_cell.attr &&
gc2.fg == tty->last_cell.fg &&
gc2.bg == tty->last_cell.bg &&
- gc2.us == tty->last_cell.us)
+ gc2.us == tty->last_cell.us &&
+ gc2.link == tty->last_cell.link)
return;
/*
@@ -2533,13 +2742,13 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
!tty_term_has(tty->term, TTYC_SMULX))
tty_putcode(tty, TTYC_SMUL);
else if (changed & GRID_ATTR_UNDERSCORE_2)
- tty_putcode1(tty, TTYC_SMULX, 2);
+ tty_putcode_i(tty, TTYC_SMULX, 2);
else if (changed & GRID_ATTR_UNDERSCORE_3)
- tty_putcode1(tty, TTYC_SMULX, 3);
+ tty_putcode_i(tty, TTYC_SMULX, 3);
else if (changed & GRID_ATTR_UNDERSCORE_4)
- tty_putcode1(tty, TTYC_SMULX, 4);
+ tty_putcode_i(tty, TTYC_SMULX, 4);
else if (changed & GRID_ATTR_UNDERSCORE_5)
- tty_putcode1(tty, TTYC_SMULX, 5);
+ tty_putcode_i(tty, TTYC_SMULX, 5);
}
if (changed & GRID_ATTR_BLINK)
tty_putcode(tty, TTYC_BLINK);
@@ -2558,6 +2767,9 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
tty_putcode(tty, TTYC_SMACS);
+ /* Set hyperlink if any. */
+ tty_hyperlink(tty, gc, hl);
+
memcpy(&tty->last_cell, &gc2, sizeof tty->last_cell);
}
@@ -2593,14 +2805,14 @@ tty_colours(struct tty *tty, const struct grid_cell *gc)
if (have_ax)
tty_puts(tty, "\033[39m");
else if (tc->fg != 7)
- tty_putcode1(tty, TTYC_SETAF, 7);
+ tty_putcode_i(tty, TTYC_SETAF, 7);
tc->fg = gc->fg;
}
if (COLOUR_DEFAULT(gc->bg) && !COLOUR_DEFAULT(tc->bg)) {
if (have_ax)
tty_puts(tty, "\033[49m");
else if (tc->bg != 0)
- tty_putcode1(tty, TTYC_SETAB, 0);
+ tty_putcode_i(tty, TTYC_SETAB, 0);
tc->bg = gc->bg;
}
}
@@ -2632,12 +2844,14 @@ tty_check_fg(struct tty *tty, struct colour_palette *palette,
/*
* Perform substitution if this pane has a palette. If the bright
- * attribute is set, use the bright entry in the palette by changing to
- * the aixterm colour.
+ * attribute is set and Nobr is not present, use the bright entry in
+ * the palette by changing to the aixterm colour
*/
if (~gc->flags & GRID_FLAG_NOPALETTE) {
c = gc->fg;
- if (c < 8 && gc->attr & GRID_ATTR_BRIGHT)
+ if (c < 8 &&
+ gc->attr & GRID_ATTR_BRIGHT &&
+ !tty_term_has(tty->term, TTYC_NOBR))
c += 90;
if ((c = colour_palette_get(palette, c)) != -1)
gc->fg = c;
@@ -2743,9 +2957,13 @@ tty_check_us(__unused struct tty *tty, struct colour_palette *palette,
gc->us = c;
}
- /* Underscore colour is set as RGB so convert a 256 colour to RGB. */
- if (gc->us & COLOUR_FLAG_256)
- gc->us = colour_256toRGB (gc->us);
+ /* Convert underscore colour if only RGB can be supported. */
+ if (!tty_term_has(tty->term, TTYC_SETULC1)) {
+ if ((c = colour_force_rgb (gc->us)) == -1)
+ gc->us = 8;
+ else
+ gc->us = c;
+ }
}
static void
@@ -2768,12 +2986,12 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
xsnprintf(s, sizeof s, "\033[%dm", gc->fg);
tty_puts(tty, s);
} else
- tty_putcode1(tty, TTYC_SETAF, gc->fg - 90 + 8);
+ tty_putcode_i(tty, TTYC_SETAF, gc->fg - 90 + 8);
goto save;
}
/* Otherwise set the foreground colour. */
- tty_putcode1(tty, TTYC_SETAF, gc->fg);
+ tty_putcode_i(tty, TTYC_SETAF, gc->fg);
save:
/* Save the new values in the terminal current cell. */
@@ -2800,12 +3018,12 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10);
tty_puts(tty, s);
} else
- tty_putcode1(tty, TTYC_SETAB, gc->bg - 90 + 8);
+ tty_putcode_i(tty, TTYC_SETAB, gc->bg - 90 + 8);
goto save;
}
/* Otherwise set the background colour. */
- tty_putcode1(tty, TTYC_SETAB, gc->bg);
+ tty_putcode_i(tty, TTYC_SETAB, gc->bg);
save:
/* Save the new values in the terminal current cell. */
@@ -2820,14 +3038,22 @@ tty_colours_us(struct tty *tty, const struct grid_cell *gc)
u_char r, g, b;
/* Clear underline colour. */
- if (gc->us == 0) {
+ if (COLOUR_DEFAULT(gc->us)) {
tty_putcode(tty, TTYC_OL);
goto save;
}
- /* Must be an RGB colour - this should never happen. */
- if (~gc->us & COLOUR_FLAG_RGB)
+ /*
+ * If this is not an RGB colour, use Setulc1 if it exists, otherwise
+ * convert.
+ */
+ if (~gc->us & COLOUR_FLAG_RGB) {
+ c = gc->us;
+ if ((~c & COLOUR_FLAG_256) && (c >= 90 && c <= 97))
+ c -= 82;
+ tty_putcode_i(tty, TTYC_SETULC1, c & ~COLOUR_FLAG_256);
return;
+ }
/*
* Setulc and setal follows the ncurses(3) one argument "direct colour"
@@ -2841,10 +3067,10 @@ tty_colours_us(struct tty *tty, const struct grid_cell *gc)
* non-RGB version may be wrong.
*/
if (tty_term_has(tty->term, TTYC_SETULC))
- tty_putcode1(tty, TTYC_SETULC, c);
+ tty_putcode_i(tty, TTYC_SETULC, c);
else if (tty_term_has(tty->term, TTYC_SETAL) &&
tty_term_has(tty->term, TTYC_RGB))
- tty_putcode1(tty, TTYC_SETAL, c);
+ tty_putcode_i(tty, TTYC_SETAL, c);
save:
/* Save the new values in the terminal current cell. */
@@ -2858,18 +3084,18 @@ tty_try_colour(struct tty *tty, int colour, const char *type)
if (colour & COLOUR_FLAG_256) {
if (*type == '3' && tty_term_has(tty->term, TTYC_SETAF))
- tty_putcode1(tty, TTYC_SETAF, colour & 0xff);
+ tty_putcode_i(tty, TTYC_SETAF, colour & 0xff);
else if (tty_term_has(tty->term, TTYC_SETAB))
- tty_putcode1(tty, TTYC_SETAB, colour & 0xff);
+ tty_putcode_i(tty, TTYC_SETAB, colour & 0xff);
return (0);
}
if (colour & COLOUR_FLAG_RGB) {
colour_split_rgb(colour & 0xffffff, &r, &g, &b);
if (*type == '3' && tty_term_has(tty->term, TTYC_SETRGBF))
- tty_putcode3(tty, TTYC_SETRGBF, r, g, b);
+ tty_putcode_iii(tty, TTYC_SETRGBF, r, g, b);
else if (tty_term_has(tty->term, TTYC_SETRGBB))
- tty_putcode3(tty, TTYC_SETRGBB, r, g, b);
+ tty_putcode_iii(tty, TTYC_SETRGBB, r, g, b);
return (0);
}
@@ -2923,13 +3149,13 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp)
static void
tty_default_attributes(struct tty *tty, const struct grid_cell *defaults,
- struct colour_palette *palette, u_int bg)
+ struct colour_palette *palette, u_int bg, struct hyperlinks *hl)
{
struct grid_cell gc;
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.bg = bg;
- tty_attributes(tty, &gc, defaults, palette);
+ tty_attributes(tty, &gc, defaults, palette, hl);
}
static void
@@ -2953,7 +3179,7 @@ tty_clipboard_query(struct tty *tty)
if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY))
return;
- tty_putcode_ptr2(tty, TTYC_MS, "", "?");
+ tty_putcode_ss(tty, TTYC_MS, "", "?");
tty->flags |= TTY_OSC52QUERY;
evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty);
diff --git a/utf8-combined.c b/utf8-combined.c
new file mode 100644
index 0000000..05958d4
--- /dev/null
+++ b/utf8-combined.c
@@ -0,0 +1,100 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2023 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 <wchar.h>
+
+#include "tmux.h"
+
+static const wchar_t utf8_modifier_table[] = {
+ 0x1F1E6,
+ 0x1F1E7,
+ 0x1F1E8,
+ 0x1F1E9,
+ 0x1F1EA,
+ 0x1F1EB,
+ 0x1F1EC,
+ 0x1F1ED,
+ 0x1F1EE,
+ 0x1F1EF,
+ 0x1F1F0,
+ 0x1F1F1,
+ 0x1F1F2,
+ 0x1F1F3,
+ 0x1F1F4,
+ 0x1F1F5,
+ 0x1F1F6,
+ 0x1F1F7,
+ 0x1F1F8,
+ 0x1F1F9,
+ 0x1F1FA,
+ 0x1F1FB,
+ 0x1F1FC,
+ 0x1F1FD,
+ 0x1F1FE,
+ 0x1F1FF,
+ 0x1F3FB,
+ 0x1F3FC,
+ 0x1F3FD,
+ 0x1F3FE,
+ 0x1F3FF
+};
+
+/* Has this got a zero width joiner at the end? */
+int
+utf8_has_zwj(const struct utf8_data *ud)
+{
+ if (ud->size < 3)
+ return (0);
+ return (memcmp(ud->data + ud->size - 3, "\342\200\215", 3) == 0);
+}
+
+/* Is this a zero width joiner? */
+int
+utf8_is_zwj(const struct utf8_data *ud)
+{
+ if (ud->size != 3)
+ return (0);
+ return (memcmp(ud->data, "\342\200\215", 3) == 0);
+}
+
+/* Is this a variation selector? */
+int
+utf8_is_vs(const struct utf8_data *ud)
+{
+ if (ud->size != 3)
+ return (0);
+ return (memcmp(ud->data, "\357\270\217", 3) == 0);
+}
+
+/* Is this in the modifier table? */
+int
+utf8_is_modifier(const struct utf8_data *ud)
+{
+ wchar_t wc;
+
+ if (utf8_towc(ud, &wc) != UTF8_DONE)
+ return (0);
+ if (!utf8_in_table(wc, utf8_modifier_table,
+ nitems(utf8_modifier_table)))
+ return (0);
+ return (1);
+}
diff --git a/utf8.c b/utf8.c
index df75a76..5053e45 100644
--- a/utf8.c
+++ b/utf8.c
@@ -26,6 +26,171 @@
#include "tmux.h"
+static const wchar_t utf8_force_wide[] = {
+ 0x0261D,
+ 0x026F9,
+ 0x0270A,
+ 0x0270B,
+ 0x0270C,
+ 0x0270D,
+ 0x1F1E6,
+ 0x1F1E7,
+ 0x1F1E8,
+ 0x1F1E9,
+ 0x1F1EA,
+ 0x1F1EB,
+ 0x1F1EC,
+ 0x1F1ED,
+ 0x1F1EE,
+ 0x1F1EF,
+ 0x1F1F0,
+ 0x1F1F1,
+ 0x1F1F2,
+ 0x1F1F3,
+ 0x1F1F4,
+ 0x1F1F5,
+ 0x1F1F6,
+ 0x1F1F7,
+ 0x1F1F8,
+ 0x1F1F9,
+ 0x1F1FA,
+ 0x1F1FB,
+ 0x1F1FC,
+ 0x1F1FD,
+ 0x1F1FE,
+ 0x1F1FF,
+ 0x1F385,
+ 0x1F3C2,
+ 0x1F3C3,
+ 0x1F3C4,
+ 0x1F3C7,
+ 0x1F3CA,
+ 0x1F3CB,
+ 0x1F3CC,
+ 0x1F3FB,
+ 0x1F3FC,
+ 0x1F3FD,
+ 0x1F3FE,
+ 0x1F3FF,
+ 0x1F442,
+ 0x1F443,
+ 0x1F446,
+ 0x1F447,
+ 0x1F448,
+ 0x1F449,
+ 0x1F44A,
+ 0x1F44B,
+ 0x1F44C,
+ 0x1F44D,
+ 0x1F44E,
+ 0x1F44F,
+ 0x1F450,
+ 0x1F466,
+ 0x1F467,
+ 0x1F468,
+ 0x1F469,
+ 0x1F46B,
+ 0x1F46C,
+ 0x1F46D,
+ 0x1F46E,
+ 0x1F470,
+ 0x1F471,
+ 0x1F472,
+ 0x1F473,
+ 0x1F474,
+ 0x1F475,
+ 0x1F476,
+ 0x1F477,
+ 0x1F478,
+ 0x1F47C,
+ 0x1F481,
+ 0x1F482,
+ 0x1F483,
+ 0x1F485,
+ 0x1F486,
+ 0x1F487,
+ 0x1F48F,
+ 0x1F491,
+ 0x1F4AA,
+ 0x1F574,
+ 0x1F575,
+ 0x1F57A,
+ 0x1F590,
+ 0x1F595,
+ 0x1F596,
+ 0x1F645,
+ 0x1F646,
+ 0x1F647,
+ 0x1F64B,
+ 0x1F64C,
+ 0x1F64D,
+ 0x1F64E,
+ 0x1F64F,
+ 0x1F6A3,
+ 0x1F6B4,
+ 0x1F6B5,
+ 0x1F6B6,
+ 0x1F6C0,
+ 0x1F6CC,
+ 0x1F90C,
+ 0x1F90F,
+ 0x1F918,
+ 0x1F919,
+ 0x1F91A,
+ 0x1F91B,
+ 0x1F91C,
+ 0x1F91D,
+ 0x1F91E,
+ 0x1F91F,
+ 0x1F926,
+ 0x1F930,
+ 0x1F931,
+ 0x1F932,
+ 0x1F933,
+ 0x1F934,
+ 0x1F935,
+ 0x1F936,
+ 0x1F937,
+ 0x1F938,
+ 0x1F939,
+ 0x1F93D,
+ 0x1F93E,
+ 0x1F977,
+ 0x1F9B5,
+ 0x1F9B6,
+ 0x1F9B8,
+ 0x1F9B9,
+ 0x1F9BB,
+ 0x1F9CD,
+ 0x1F9CE,
+ 0x1F9CF,
+ 0x1F9D1,
+ 0x1F9D2,
+ 0x1F9D3,
+ 0x1F9D4,
+ 0x1F9D5,
+ 0x1F9D6,
+ 0x1F9D7,
+ 0x1F9D8,
+ 0x1F9D9,
+ 0x1F9DA,
+ 0x1F9DB,
+ 0x1F9DC,
+ 0x1F9DD,
+ 0x1FAC3,
+ 0x1FAC4,
+ 0x1FAC5,
+ 0x1FAF0,
+ 0x1FAF1,
+ 0x1FAF2,
+ 0x1FAF3,
+ 0x1FAF4,
+ 0x1FAF5,
+ 0x1FAF6,
+ 0x1FAF7,
+ 0x1FAF8
+};
+
struct utf8_item {
RB_ENTRY(utf8_item) index_entry;
u_int index;
@@ -71,7 +236,7 @@ static u_int utf8_next_index;
/* Get a UTF-8 item from data. */
static struct utf8_item *
-utf8_item_by_data(const char *data, size_t size)
+utf8_item_by_data(const u_char *data, size_t size)
{
struct utf8_item ui;
@@ -94,7 +259,7 @@ utf8_item_by_index(u_int index)
/* Add a UTF-8 item. */
static int
-utf8_put_item(const char *data, size_t size, u_int *index)
+utf8_put_item(const u_char *data, size_t size, u_int *index)
{
struct utf8_item *ui;
@@ -122,6 +287,28 @@ utf8_put_item(const char *data, size_t size, u_int *index)
return (0);
}
+static int
+utf8_table_cmp(const void *vp1, const void *vp2)
+{
+ const wchar_t *wc1 = vp1, *wc2 = vp2;
+
+ if (*wc1 < *wc2)
+ return (-1);
+ if (*wc1 > *wc2)
+ return (1);
+ return (0);
+}
+
+/* Check if character in table. */
+int
+utf8_in_table(wchar_t find, const wchar_t *table, u_int count)
+{
+ wchar_t *found;
+
+ found = bsearch(&find, table, count, sizeof *table, utf8_table_cmp);
+ return (found != NULL);
+}
+
/* Get UTF-8 character from data. */
enum utf8_state
utf8_from_data(const struct utf8_data *ud, utf8_char *uc)
@@ -135,8 +322,8 @@ utf8_from_data(const struct utf8_data *ud, utf8_char *uc)
goto fail;
if (ud->size <= 3) {
index = (((utf8_char)ud->data[2] << 16)|
- ((utf8_char)ud->data[1] << 8)|
- ((utf8_char)ud->data[0]));
+ ((utf8_char)ud->data[1] << 8)|
+ ((utf8_char)ud->data[0]));
} else if (utf8_put_item(ud->data, ud->size, &index) != 0)
goto fail;
*uc = UTF8_SET_SIZE(ud->size)|UTF8_SET_WIDTH(ud->width)|index;
@@ -216,10 +403,39 @@ utf8_width(struct utf8_data *ud, int *width)
{
wchar_t wc;
+ if (utf8_towc(ud, &wc) != UTF8_DONE)
+ return (UTF8_ERROR);
+ if (utf8_in_table(wc, utf8_force_wide, nitems(utf8_force_wide))) {
+ *width = 2;
+ return (UTF8_DONE);
+ }
+#ifdef HAVE_UTF8PROC
+ *width = utf8proc_wcwidth(wc);
+ log_debug("utf8proc_wcwidth(%05X) returned %d", (u_int)wc, *width);
+#else
+ *width = wcwidth(wc);
+ log_debug("wcwidth(%05X) returned %d", (u_int)wc, *width);
+ if (*width < 0) {
+ /*
+ * C1 control characters are nonprintable, so they are always
+ * zero width.
+ */
+ *width = (wc >= 0x80 && wc <= 0x9f) ? 0 : 1;
+ }
+#endif
+ if (*width >= 0 && *width <= 0xff)
+ return (UTF8_DONE);
+ return (UTF8_ERROR);
+}
+
+/* Convert UTF-8 character to wide character. */
+enum utf8_state
+utf8_towc(const struct utf8_data *ud, wchar_t *wc)
+{
#ifdef HAVE_UTF8PROC
- switch (utf8proc_mbtowc(&wc, ud->data, ud->size)) {
+ switch (utf8proc_mbtowc(wc, ud->data, ud->size)) {
#else
- switch (mbtowc(&wc, ud->data, ud->size)) {
+ switch (mbtowc(wc, ud->data, ud->size)) {
#endif
case -1:
log_debug("UTF-8 %.*s, mbtowc() %d", (int)ud->size, ud->data,
@@ -229,15 +445,8 @@ utf8_width(struct utf8_data *ud, int *width)
case 0:
return (UTF8_ERROR);
}
-#ifdef HAVE_UTF8PROC
- *width = utf8proc_wcwidth(wc);
-#else
- *width = wcwidth(wc);
-#endif
- if (*width >= 0 && *width <= 0xff)
- return (UTF8_DONE);
- log_debug("UTF-8 %.*s, wcwidth() %d", (int)ud->size, ud->data, *width);
- return (UTF8_ERROR);
+ log_debug("UTF-8 %.*s is %05X", (int)ud->size, ud->data, (u_int)*wc);
+ return (UTF8_DONE);
}
/*
diff --git a/window-buffer.c b/window-buffer.c
index 2f52ee3..2e0a9e8 100644
--- a/window-buffer.c
+++ b/window-buffer.c
@@ -508,6 +508,11 @@ window_buffer_key(struct window_mode_entry *wme, struct client *c,
struct window_buffer_itemdata *item;
int finished;
+ if (paste_get_top(NULL) == NULL) {
+ finished = 1;
+ goto out;
+ }
+
finished = mode_tree_key(mtd, c, &key, m, NULL, NULL);
switch (key) {
case 'e':
@@ -534,6 +539,8 @@ window_buffer_key(struct window_mode_entry *wme, struct client *c,
finished = 1;
break;
}
+
+out:
if (finished || paste_get_top(NULL) == NULL)
window_pane_reset_mode(wp);
else {
diff --git a/window-client.c b/window-client.c
index 8d501b0..b704b53 100644
--- a/window-client.c
+++ b/window-client.c
@@ -242,7 +242,7 @@ window_client_draw(__unused void *modedata, void *itemdata,
screen_write_cursormove(ctx, cx, cy + 2, 0);
else
screen_write_cursormove(ctx, cx, cy + sy - 1 - lines, 0);
- screen_write_hline(ctx, sx, 0, 0);
+ screen_write_hline(ctx, sx, 0, 0, BOX_LINES_DEFAULT, NULL);
if (at != 0)
screen_write_cursormove(ctx, cx, cy, 0);
diff --git a/window-copy.c b/window-copy.c
index 0307055..bc3713b 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -131,6 +131,8 @@ static void window_copy_cursor_previous_word_pos(struct window_mode_entry *,
const char *, u_int *, u_int *);
static void window_copy_cursor_previous_word(struct window_mode_entry *,
const char *, int);
+static void window_copy_cursor_prompt(struct window_mode_entry *, int,
+ const char *);
static void window_copy_scroll_up(struct window_mode_entry *, u_int);
static void window_copy_scroll_down(struct window_mode_entry *, u_int);
static void window_copy_rectangle_set(struct window_mode_entry *, int);
@@ -293,6 +295,7 @@ struct window_copy_mode_data {
int timeout; /* search has timed out */
#define WINDOW_COPY_SEARCH_TIMEOUT 10000
#define WINDOW_COPY_SEARCH_ALL_TIMEOUT 200
+#define WINDOW_COPY_SEARCH_MAX_LINE 2000
int jumptype;
struct utf8_data *jumpchar;
@@ -1250,6 +1253,64 @@ window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs)
return (WINDOW_COPY_CMD_NOTHING);
}
+/* Scroll line containing the cursor to the given position. */
+static enum window_copy_cmd_action
+window_copy_cmd_scroll_to(struct window_copy_cmd_state *cs, u_int to)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int oy, delta;
+ int scroll_up; /* >0 up, <0 down */
+
+ scroll_up = data->cy - to;
+ delta = abs(scroll_up);
+ oy = screen_hsize(data->backing) - data->oy;
+
+ /*
+ * oy is the maximum scroll down amount, while data->oy is the maximum
+ * scroll up amount.
+ */
+ if (scroll_up > 0 && data->oy >= delta) {
+ window_copy_scroll_up(wme, delta);
+ data->cy -= delta;
+ } else if (scroll_up < 0 && oy >= delta) {
+ window_copy_scroll_down(wme, delta);
+ data->cy += delta;
+ }
+
+ window_copy_update_selection(wme, 0, 0);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+/* Scroll line containing the cursor to the bottom. */
+static enum window_copy_cmd_action
+window_copy_cmd_scroll_bottom(struct window_copy_cmd_state *cs)
+{
+ struct window_copy_mode_data *data = cs->wme->data;
+ u_int bottom;
+
+ bottom = screen_size_y(&data->screen) - 1;
+ return (window_copy_cmd_scroll_to(cs, bottom));
+}
+
+/* Scroll line containing the cursor to the middle. */
+static enum window_copy_cmd_action
+window_copy_cmd_scroll_middle(struct window_copy_cmd_state *cs)
+{
+ struct window_copy_mode_data *data = cs->wme->data;
+ u_int mid_value;
+
+ mid_value = (screen_size_y(&data->screen) - 1) / 2;
+ return (window_copy_cmd_scroll_to(cs, mid_value));
+}
+
+/* Scroll line containing the cursor to the top. */
+static enum window_copy_cmd_action
+window_copy_cmd_scroll_top(struct window_copy_cmd_state *cs)
+{
+ return (window_copy_cmd_scroll_to(cs, 0));
+}
+
static enum window_copy_cmd_action
window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs)
{
@@ -2183,6 +2244,26 @@ window_copy_cmd_jump_to_mark(struct window_copy_cmd_state *cs)
}
static enum window_copy_cmd_action
+window_copy_cmd_next_prompt(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ const char *arg1 = args_string(cs->args, 1);
+
+ window_copy_cursor_prompt(wme, 1, arg1);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_previous_prompt(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ const char *arg1 = args_string(cs->args, 1);
+
+ window_copy_cursor_prompt(wme, 0, arg1);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
window_copy_cmd_search_backward(struct window_copy_cmd_state *cs)
{
struct window_mode_entry *wme = cs->wme;
@@ -2636,6 +2717,18 @@ static const struct {
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_jump_to_mark
},
+ { .command = "next-prompt",
+ .minargs = 0,
+ .maxargs = 1,
+ .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
+ .f = window_copy_cmd_next_prompt
+ },
+ { .command = "previous-prompt",
+ .minargs = 0,
+ .maxargs = 1,
+ .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
+ .f = window_copy_cmd_previous_prompt
+ },
{ .command = "middle-line",
.minargs = 0,
.maxargs = 0,
@@ -2768,6 +2861,12 @@ static const struct {
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_refresh_from_pane
},
+ { .command = "scroll-bottom",
+ .minargs = 0,
+ .maxargs = 0,
+ .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
+ .f = window_copy_cmd_scroll_bottom
+ },
{ .command = "scroll-down",
.minargs = 0,
.maxargs = 0,
@@ -2780,6 +2879,18 @@ static const struct {
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_down_and_cancel
},
+ { .command = "scroll-middle",
+ .minargs = 0,
+ .maxargs = 0,
+ .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
+ .f = window_copy_cmd_scroll_middle
+ },
+ { .command = "scroll-top",
+ .minargs = 0,
+ .maxargs = 0,
+ .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
+ .f = window_copy_cmd_scroll_top
+ },
{ .command = "scroll-up",
.minargs = 0,
.maxargs = 0,
@@ -3095,7 +3206,9 @@ window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
len = gd->sx - first;
endline = gd->hsize + gd->sy - 1;
pywrap = py;
- while (buf != NULL && pywrap <= endline) {
+ while (buf != NULL &&
+ pywrap <= endline &&
+ len < WINDOW_COPY_SEARCH_MAX_LINE) {
gl = grid_get_line(gd, pywrap);
if (~gl->flags & GRID_LINE_WRAPPED)
break;
@@ -3152,7 +3265,9 @@ window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
len = gd->sx - first;
endline = gd->hsize + gd->sy - 1;
pywrap = py;
- while (buf != NULL && (pywrap <= endline)) {
+ while (buf != NULL &&
+ pywrap <= endline &&
+ len < WINDOW_COPY_SEARCH_MAX_LINE) {
gl = grid_get_line(gd, pywrap);
if (~gl->flags & GRID_LINE_WRAPPED)
break;
@@ -3491,10 +3606,11 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
int direction, int regex)
{
- u_int i, px, sx, ssize = 1;
- int found = 0, cflags = REG_EXTENDED;
- char *sbuf;
- regex_t reg;
+ u_int i, px, sx, ssize = 1;
+ int found = 0, cflags = REG_EXTENDED;
+ char *sbuf;
+ regex_t reg;
+ struct grid_line *gl;
if (regex) {
sbuf = xmalloc(ssize);
@@ -3511,6 +3627,9 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
if (direction) {
for (i = fy; i <= endline; i++) {
+ gl = grid_get_line(gd, i);
+ if (i != endline && gl->flags & GRID_LINE_WRAPPED)
+ continue;
if (regex) {
found = window_copy_search_lr_regex(gd,
&px, &sx, i, fx, gd->sx, &reg);
@@ -3524,6 +3643,9 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
}
} else {
for (i = fy + 1; endline < i; i--) {
+ gl = grid_get_line(gd, i - 1);
+ if (i != endline && gl->flags & GRID_LINE_WRAPPED)
+ continue;
if (regex) {
found = window_copy_search_rl_regex(gd,
&px, &sx, i - 1, 0, fx + 1, &reg);
@@ -3612,6 +3734,8 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
data->searchall = 0;
} else
visible_only = (strcmp(wp->searchstr, str) == 0);
+ if (visible_only == 0 && data->searchmark != NULL)
+ window_copy_clear_marks(wme);
free(wp->searchstr);
wp->searchstr = xstrdup(str);
wp->searchregex = regex;
@@ -3651,8 +3775,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
}
}
endline = gd->hsize + gd->sy - 1;
- }
- else {
+ } else {
window_copy_move_left(s, &fx, &fy, wrapflag);
endline = 0;
}
@@ -3671,6 +3794,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
if (direction &&
window_copy_search_mark_at(data, fx, fy, &at) == 0 &&
at > 0 &&
+ data->searchmark != NULL &&
data->searchmark[at] == data->searchmark[at - 1]) {
window_copy_move_after_search_mark(data, &fx, &fy,
wrapflag);
@@ -3693,8 +3817,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
data->cy = fy - screen_hsize(data->backing) +
data-> oy;
}
- }
- else {
+ } else {
/*
* When searching backward, position the cursor at the
* beginning of the mark.
@@ -3703,6 +3826,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
&start) == 0) {
while (window_copy_search_mark_at(data, fx, fy,
&at) == 0 &&
+ data->searchmark != NULL &&
data->searchmark[at] ==
data->searchmark[start]) {
data->cx = fx;
@@ -4092,8 +4216,9 @@ window_copy_write_line(struct window_mode_entry *wme,
struct window_copy_mode_data *data = wme->data;
struct screen *s = &data->screen;
struct options *oo = wp->window->options;
+ struct grid_line *gl;
struct grid_cell gc, mgc, cgc, mkgc;
- char hdr[512];
+ char hdr[512], tmp[256], *t;
size_t size = 0;
u_int hsize = screen_hsize(data->backing);
@@ -4107,23 +4232,29 @@ window_copy_write_line(struct window_mode_entry *wme,
mkgc.flags |= GRID_FLAG_NOPALETTE;
if (py == 0 && s->rupper < s->rlower && !data->hide_position) {
+ gl = grid_get_line(data->backing->grid, hsize - data->oy);
+ if (gl->time == 0)
+ xsnprintf(tmp, sizeof tmp, "[%u/%u]", data->oy, hsize);
+ else {
+ t = format_pretty_time(gl->time, 1);
+ xsnprintf(tmp, sizeof tmp, "%s [%u/%u]", t, data->oy,
+ hsize);
+ free(t);
+ }
+
if (data->searchmark == NULL) {
if (data->timeout) {
size = xsnprintf(hdr, sizeof hdr,
- "(timed out) [%u/%u]", data->oy, hsize);
- } else {
- size = xsnprintf(hdr, sizeof hdr,
- "[%u/%u]", data->oy, hsize);
- }
+ "(timed out) %s", tmp);
+ } else
+ size = xsnprintf(hdr, sizeof hdr, "%s", tmp);
} else {
- if (data->searchcount == -1) {
- size = xsnprintf(hdr, sizeof hdr,
- "[%u/%u]", data->oy, hsize);
- } else {
+ if (data->searchcount == -1)
+ size = xsnprintf(hdr, sizeof hdr, "%s", tmp);
+ else {
size = xsnprintf(hdr, sizeof hdr,
- "(%d%s results) [%u/%u]", data->searchcount,
- data->searchmore ? "+" : "", data->oy,
- hsize);
+ "(%d%s results) %s", data->searchcount,
+ data->searchmore ? "+" : "", tmp);
}
}
if (size > screen_size_x(s))
@@ -4570,7 +4701,7 @@ window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix,
if (options_get_number(global_options, "set-clipboard") != 0) {
screen_write_start_pane(&ctx, wp, NULL);
- screen_write_setselection(&ctx, buf, len);
+ screen_write_setselection(&ctx, "", buf, len);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
}
@@ -4644,7 +4775,7 @@ window_copy_append_selection(struct window_mode_entry *wme)
if (options_get_number(global_options, "set-clipboard") != 0) {
screen_write_start_pane(&ctx, wp, NULL);
- screen_write_setselection(&ctx, buf, len);
+ screen_write_setselection(&ctx, "", buf, len);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
}
@@ -5271,6 +5402,54 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme,
}
static void
+window_copy_cursor_prompt(struct window_mode_entry *wme, int direction,
+ const char *args)
+{
+ struct window_copy_mode_data *data = wme->data;
+ struct screen *s = data->backing;
+ struct grid *gd = s->grid;
+ u_int end_line;
+ u_int line = gd->hsize - data->oy + data->cy;
+ int add, line_flag;
+
+ if (args != NULL && strcmp(args, "-o") == 0)
+ line_flag = GRID_LINE_START_OUTPUT;
+ else
+ line_flag = GRID_LINE_START_PROMPT;
+
+ if (direction == 0) { /* up */
+ add = -1;
+ end_line = 0;
+ } else { /* down */
+ add = 1;
+ end_line = gd->hsize + gd->sy - 1;
+ }
+
+ if (line == end_line)
+ return;
+ for (;;) {
+ if (line == end_line)
+ return;
+ line += add;
+
+ if (grid_get_line(gd, line)->flags & line_flag)
+ break;
+ }
+
+ data->cx = 0;
+ if (line > gd->hsize) {
+ data->cy = line - gd->hsize;
+ data->oy = 0;
+ } else {
+ data->cy = 0;
+ data->oy = gd->hsize - line;
+ }
+
+ window_copy_update_selection(wme, 1, 0);
+ window_copy_redraw_screen(wme);
+}
+
+static void
window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
{
struct window_pane *wp = wme->wp;
diff --git a/window-tree.c b/window-tree.c
index d90bf81..b2f397f 100644
--- a/window-tree.c
+++ b/window-tree.c
@@ -272,9 +272,10 @@ window_tree_cmp_window(const void *a0, const void *b0)
static int
window_tree_cmp_pane(const void *a0, const void *b0)
{
- const struct window_pane *const *a = a0;
- const struct window_pane *const *b = b0;
- int result;
+ struct window_pane **a = (struct window_pane **)a0;
+ struct window_pane **b = (struct window_pane **)b0;
+ int result;
+ u_int ai, bi;
if (window_tree_sort->field == WINDOW_TREE_BY_TIME)
result = (*a)->active_point - (*b)->active_point;
@@ -283,7 +284,9 @@ window_tree_cmp_pane(const void *a0, const void *b0)
* Panes don't have names, so use number order for any other
* sort field.
*/
- result = (*a)->id - (*b)->id;
+ window_pane_index(*a, &ai);
+ window_pane_index(*b, &bi);
+ result = ai - bi;
}
if (window_tree_sort->reversed)
result = -result;
@@ -668,9 +671,9 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
struct window_pane *wp;
u_int cx = ctx->s->cx, cy = ctx->s->cy;
u_int loop, total, visible, each, width, offset;
- u_int current, start, end, remaining, i;
+ u_int current, start, end, remaining, i, pane_idx;
struct grid_cell gc;
- int colour, active_colour, left, right, pane_idx;
+ int colour, active_colour, left, right;
char *label;
total = window_count_panes(w);
@@ -1243,12 +1246,17 @@ window_tree_key(struct window_mode_entry *wme, struct client *c,
item = mode_tree_get_current(data->data);
finished = mode_tree_key(data->data, c, &key, m, &x, &y);
+
+again:
if (item != (new_item = mode_tree_get_current(data->data))) {
item = new_item;
data->offset = 0;
}
- if (KEYC_IS_MOUSE(key) && m != NULL)
+ if (KEYC_IS_MOUSE(key) && m != NULL) {
key = window_tree_mouse(data, key, x, item);
+ goto again;
+ }
+
switch (key) {
case '<':
data->offset--;
diff --git a/window.c b/window.c
index c0cd9bd..77ce41d 100644
--- a/window.c
+++ b/window.c
@@ -64,6 +64,7 @@ static u_int next_active_point;
struct window_pane_input_data {
struct cmdq_item *item;
u_int wp;
+ struct client_file *file;
};
static struct window_pane *window_pane_create(struct window *, u_int, u_int,
@@ -245,21 +246,15 @@ winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
winlink_stack_remove(stack, wl);
TAILQ_INSERT_HEAD(stack, wl, sentry);
+ wl->flags |= WINLINK_VISITED;
}
void
winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
{
- struct winlink *wl2;
-
- if (wl == NULL)
- return;
-
- TAILQ_FOREACH(wl2, stack, sentry) {
- if (wl2 == wl) {
- TAILQ_REMOVE(stack, wl, sentry);
- return;
- }
+ if (wl != NULL && (wl->flags & WINLINK_VISITED)) {
+ TAILQ_REMOVE(stack, wl, sentry);
+ wl->flags &= ~WINLINK_VISITED;
}
}
@@ -309,6 +304,7 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
w->flags = 0;
TAILQ_INIT(&w->panes);
+ TAILQ_INIT(&w->last_panes);
w->active = NULL;
w->lastlayout = -1;
@@ -342,6 +338,7 @@ window_destroy(struct window *w)
{
log_debug("window @%u destroyed (%d references)", w->id, w->references);
+ window_unzoom(w);
RB_REMOVE(windows, &windows, w);
if (w->layout_root != NULL)
@@ -518,18 +515,23 @@ window_pane_update_focus(struct window_pane *wp)
int
window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
{
+ struct window_pane *lastwp;
+
log_debug("%s: pane %%%u", __func__, wp->id);
if (wp == w->active)
return (0);
- w->last = w->active;
+ lastwp = w->active;
+
+ window_pane_stack_remove(&w->last_panes, wp);
+ window_pane_stack_push(&w->last_panes, lastwp);
w->active = wp;
w->active->active_point = next_active_point++;
w->active->flags |= PANE_CHANGED;
if (options_get_number(global_options, "focus-events")) {
- window_pane_update_focus(w->last);
+ window_pane_update_focus(lastwp);
window_pane_update_focus(w->active);
}
@@ -752,21 +754,21 @@ window_lost_pane(struct window *w, struct window_pane *wp)
if (wp == marked_pane.wp)
server_clear_marked();
+ window_pane_stack_remove(&w->last_panes, wp);
if (wp == w->active) {
- w->active = w->last;
- w->last = NULL;
+ w->active = TAILQ_FIRST(&w->last_panes);
if (w->active == NULL) {
w->active = TAILQ_PREV(wp, window_panes, entry);
if (w->active == NULL)
w->active = TAILQ_NEXT(wp, entry);
}
if (w->active != NULL) {
+ window_pane_stack_remove(&w->last_panes, w->active);
w->active->flags |= PANE_CHANGED;
notify_window("window-pane-changed", w);
window_update_focus(w);
}
- } else if (wp == w->last)
- w->last = NULL;
+ }
}
void
@@ -850,6 +852,11 @@ window_destroy_panes(struct window *w)
{
struct window_pane *wp;
+ while (!TAILQ_EMPTY(&w->last_panes)) {
+ wp = TAILQ_FIRST(&w->last_panes);
+ window_pane_stack_remove(&w->last_panes, wp);
+ }
+
while (!TAILQ_EMPTY(&w->panes)) {
wp = TAILQ_FIRST(&w->panes);
TAILQ_REMOVE(&w->panes, wp, entry);
@@ -940,6 +947,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
screen_init(&wp->base, sx, sy, hlimit);
wp->screen = &wp->base;
+ window_pane_default_cursor(wp);
screen_init(&wp->status_screen, 1, 1, 0);
@@ -1042,6 +1050,8 @@ window_pane_set_event(struct window_pane *wp)
wp->event = bufferevent_new(wp->fd, window_pane_read_callback,
NULL, window_pane_error_callback, wp);
+ if (wp->event == NULL)
+ fatalx("out of memory");
wp->ictx = input_init(wp, wp->event, &wp->palette);
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
@@ -1126,6 +1136,7 @@ window_pane_reset_mode(struct window_pane *wp)
next = TAILQ_FIRST(&wp->modes);
if (next == NULL) {
+ wp->flags &= ~PANE_UNSEENCHANGES;
log_debug("%s: no next mode", __func__);
wp->screen = &wp->base;
} else {
@@ -1203,6 +1214,12 @@ window_pane_visible(struct window_pane *wp)
return (wp == wp->window->active);
}
+int
+window_pane_exited(struct window_pane *wp)
+{
+ return (wp->fd == -1 || (wp->flags & PANE_EXITED));
+}
+
u_int
window_pane_search(struct window_pane *wp, const char *term, int regex,
int ignore)
@@ -1483,6 +1500,25 @@ window_pane_find_right(struct window_pane *wp)
return (best);
}
+void
+window_pane_stack_push(struct window_panes *stack, struct window_pane *wp)
+{
+ if (wp != NULL) {
+ window_pane_stack_remove(stack, wp);
+ TAILQ_INSERT_HEAD(stack, wp, sentry);
+ wp->flags |= PANE_VISITED;
+ }
+}
+
+void
+window_pane_stack_remove(struct window_panes *stack, struct window_pane *wp)
+{
+ if (wp != NULL && (wp->flags & PANE_VISITED)) {
+ TAILQ_REMOVE(stack, wp, sentry);
+ wp->flags &= ~PANE_VISITED;
+ }
+}
+
/* Clear alert flags for a winlink */
void
winlink_clear_flags(struct winlink *wl)
@@ -1540,18 +1576,18 @@ window_pane_input_callback(struct client *c, __unused const char *path,
size_t len = EVBUFFER_LENGTH(buffer);
wp = window_pane_find_by_id(cdata->wp);
- if (wp == NULL || closed || error != 0 || (c->flags & CLIENT_DEAD)) {
- if (wp == NULL)
+ if (cdata->file != NULL && (wp == NULL || c->flags & CLIENT_DEAD)) {
+ if (wp == NULL) {
+ c->retval = 1;
c->flags |= CLIENT_EXIT;
-
- evbuffer_drain(buffer, len);
+ }
+ file_cancel(cdata->file);
+ } else if (cdata->file == NULL || closed || error != 0) {
cmdq_continue(cdata->item);
-
server_client_unref(c);
free(cdata);
- return;
- }
- input_parse_buffer(wp, buf, len);
+ } else
+ input_parse_buffer(wp, buf, len);
evbuffer_drain(buffer, len);
}
@@ -1574,9 +1610,8 @@ window_pane_start_input(struct window_pane *wp, struct cmdq_item *item,
cdata = xmalloc(sizeof *cdata);
cdata->item = item;
cdata->wp = wp->id;
-
+ cdata->file = file_read(c, "-", window_pane_input_callback, cdata);
c->references++;
- file_read(c, "-", window_pane_input_callback, cdata);
return (0);
}
@@ -1618,3 +1653,17 @@ window_set_fill_character(struct window *w)
w->fill_character = ud;
}
}
+
+void
+window_pane_default_cursor(struct window_pane *wp)
+{
+ struct screen *s = wp->screen;
+ int c;
+
+ c = options_get_number(wp->options, "cursor-colour");
+ s->default_ccolour = c;
+
+ c = options_get_number(wp->options, "cursor-style");
+ s->default_mode = 0;
+ screen_set_cursor_style(c, &s->default_cstyle, &s->default_mode);
+}