437 Commits
2.7 ... 2.9

Author SHA1 Message Date
Nicholas Marriott
cb75ec25c8 2.9 now. 2019-04-24 22:35:23 +01:00
nicm
7d06216289 Do not loop forever if there is a nonprintable character in the format. 2019-04-24 22:34:56 +01:00
Nicholas Marriott
d24a44230a Update CHANGES for 2.9. 2019-04-18 22:12:15 +01:00
nicm
cd4c94f76b Current window style also needs to be tested for default. 2019-04-11 09:34:22 +01:00
Nicholas Marriott
bba1809eac Merge a number of fixes from master for layouts, mostly prompted by testing by
Thomas Sattler.
2019-04-11 09:26:34 +01:00
nicm
73b54a0e5f Fix size check for splitw -f and top level pane size for tiled layout,
problems reported by Thomas Sattler.
2019-04-04 18:31:35 +01:00
nicm
481c3f3f2e screen_write_fast_copy can no longer assume the target screen is default
(it isn't for the pane status lines).
2019-04-03 09:31:32 +01:00
nicm
8968acd678 Silence flag should use the same option as activity, reported by Thomas
Sattler.
2019-04-02 22:20:36 +01:00
Nicholas Marriott
916c4c499d Version RC2. 2019-04-02 11:12:24 +01:00
Nicholas Marriott
fac792dc27 Update regress from master. 2019-04-02 11:11:19 +01:00
nicm
bbcfee362f Store and restore cursor across reflow by working out a position based
on unwrapped lines, rather than a grid offset. Fixes problems reported
by Thomas Sattler and Paul de Weerd.
2019-04-02 11:11:12 +01:00
nicm
6d071c468c Restore a check to stop scrolled lines becoming larger than total lines,
fixes a crash reported by Thomas Sattler.
2019-04-02 07:33:33 +01:00
nicm
92da105b58 Free old strings after they have been expanded in format_choose. 2019-03-29 11:19:55 +00:00
nicm
00fda57ddf Fix offset of list ranges. 2019-03-29 07:05:40 +00:00
Nicholas Marriott
e0e08fcd2d Update CHANGES & TODO. 2019-03-26 15:05:28 +00:00
Nicholas Marriott
4f7a5d1e40 2.9 bits. 2019-03-26 14:37:27 +00:00
Thomas Adam
6ac84585e6 Merge branch 'obsd-master' 2019-03-25 20:02:38 +00:00
nicm
d21f8ecc13 Add StatusDefault binding for the mouse on any otherwise unassigned
parts of the status line, from Avi Halachmi.
2019-03-25 18:59:55 +00:00
Thomas Adam
5e2150cf18 Merge branch 'obsd-master' 2019-03-25 16:00:36 +00:00
Thomas Adam
a07ad6b5a3 Merge branch 'obsd-master' 2019-03-25 15:57:47 +00:00
nicm
ff4d7d541f Fix columns of cursor_character format in list. 2019-03-25 14:29:36 +00:00
Nicholas Marriott
e8f4ca6a52 Tweaks to example config. 2019-03-25 12:05:58 +00:00
Nicholas Marriott
a084f2dcb7 Add to TODO. 2019-03-25 10:06:03 +00:00
nicm
517d673dbe Ignore mouse on status line which are not part of a range, GitHub issue 1649. 2019-03-25 09:22:09 +00:00
nicm
b4a301f8fe Clarify that styles can be space or comma separated, from Stephen Zapatka. 2019-03-22 10:45:17 +00:00
nicm
04a1fc9d36 I forgot to document resize-window, window-size and default-size;
reminded by okan@.
2019-03-22 09:33:04 +00:00
Thomas Adam
0292243b29 Merge branch 'obsd-master' 2019-03-20 20:02:37 +00:00
Nicholas Marriott
ef38a420b2 Update TODO. 2019-03-20 20:02:13 +00:00
Nicholas Marriott
7536d57b16 Update TODO. 2019-03-20 19:26:20 +00:00
nicm
9ee1a8f701 Improve cursor positioning after reflow by storing the position as an
offset into the entire history before reflow and restoring it aftewards.
2019-03-20 19:19:11 +00:00
Nicholas Marriott
0e0f2f765b Update CHANGES. 2019-03-20 14:01:46 +00:00
Thomas Adam
bff957a475 Merge branch 'obsd-master' 2019-03-20 08:02:38 +00:00
nicm
ed962e7612 Include function name in logging. 2019-03-20 07:30:05 +00:00
nicm
458b87150b Do not leak ranges on error. 2019-03-20 07:28:31 +00:00
nicm
ae46a19b8e Ignore invalid styles rather than throwing away the whole format, this
matches what we used to do.
2019-03-20 07:24:03 +00:00
nicm
3b959c05ff Bit more logging to show drawing errors. 2019-03-20 07:13:02 +00:00
Thomas Adam
46f642b030 Merge branch 'obsd-master' 2019-03-19 22:02:36 +00:00
nicm
8b74e959ef Include window-status-style and window-status-current style in the format. 2019-03-19 21:09:51 +00:00
Thomas Adam
320237fdb8 Merge branch 'obsd-master' 2019-03-19 20:02:37 +00:00
nicm
161b57869e Add a cursor_character format. 2019-03-19 19:01:50 +00:00
Nicholas Marriott
92732a2540 Add to TODO. 2019-03-19 14:39:55 +00:00
Thomas Adam
b3eebdec48 Merge branch 'obsd-master' 2019-03-19 14:02:36 +00:00
nicm
b24c9e34a9 Rename the first KEY BINDINGS section to DEFAULT KEY BINDINGS. 2019-03-19 13:35:42 +00:00
Thomas Adam
073d439965 Merge branch 'obsd-master' 2019-03-19 00:02:39 +00:00
Thomas Adam
f956c17c09 Merge branch 'obsd-master' 2019-03-18 22:02:36 +00:00
nicm
0406ec0ec2 Free temporary screens when writing format. 2019-03-18 21:55:04 +00:00
nicm
f34ebfed76 The individual -fg, -bg and -attr options have been deprecated (in
favour of -style), undocumented and hidden from show-options since
2014. Remove them, except for status-fg and status-bg.
2019-03-18 21:46:01 +00:00
Thomas Adam
962f255ee8 Merge branch 'obsd-master' 2019-03-18 21:24:49 +00:00
nicm
c62404673e Add some bits to the STATUS LINE section about the new option. 2019-03-18 21:01:04 +00:00
nicm
979313832c Extend the #[] style syntax and use that together with previous format
changes to allow the status line to be entirely configured with a single
option.

Now that it is possible to configure their content, enable the existing
code that lets the status line be multiple lines in height. The status
option can now take a value of 2, 3, 4 or 5 (as well as the previous on
or off) to configure more than one line. The new status-format array
option configures the format of each line, the default just references
the existing status-* options, although some of the more obscure status
options may be eliminated in time.

Additions to the #[] syntax are: "align" to specify alignment (left,
centre, right), "list" for the window list and "range" to configure
ranges of text for the mouse bindings.

The "align" keyword can also be used to specify alignment of entries in
tree mode and the pane status lines.
2019-03-18 20:53:33 +00:00
Thomas Adam
1d6fe43c7b Merge branch 'obsd-master' 2019-03-18 18:02:38 +00:00
Thomas Adam
4cbf596dc5 Merge branch 'obsd-master' 2019-03-18 15:30:55 +00:00
nicm
d738d51688 Mode init needs to be fired with the mode on the list or it will not be
resized correctly.
2019-03-18 15:25:36 +00:00
Thomas Adam
acb2413852 Merge branch 'obsd-master' 2019-03-18 15:07:51 +00:00
nicm
2628af573d Add format variables for the default formats for the various modes
(tree_mode_format and so on) and add a -a flag to display-message to
list variables with values.
2019-03-18 14:10:25 +00:00
Nicholas Marriott
aa2b3472c5 Update TODO. 2019-03-18 12:00:10 +00:00
nicm
ce6be7afd4 Make array options a sparse tree instead of an array of char * and
remove the size limit.
2019-03-18 11:58:40 +00:00
nicm
d2d43987d0 With force, kill previous job before starting new. Fixes problem
reported by Scott Mcdermott in GitHub issue 1627.
2019-03-18 09:46:42 +00:00
nicm
bd3332b211 Break description of styles into its own section. 2019-03-17 19:33:12 +00:00
Nicholas Marriott
3a298454ce Add to TODO. 2019-03-17 08:05:58 +00:00
nicm
b588b1729a Use a pointer for the active screen in the status line instead of
copying them around all the time.
2019-03-16 19:12:13 +00:00
nicm
818fda0363 Give status_save_old the client so it can do the reinit too. 2019-03-16 17:53:55 +00:00
nicm
b4f5b99e4b Tidy and rename some bits of status line code. 2019-03-16 17:14:07 +00:00
nicm
e8b33af780 Add a way to set individual defaults for an array option. 2019-03-15 21:54:47 +00:00
nicm
1d306e926a Add a : to make error messages clearer. 2019-03-15 15:20:00 +00:00
nicm
33595a255f Copy recursion counter into new formats when looping. 2019-03-15 15:02:25 +00:00
nicm
85044a634b Move status line free into its own function. 2019-03-15 14:46:58 +00:00
nicm
2d71bef0ca Remove unused member of struct client. 2019-03-15 10:48:05 +00:00
nicm
f6d34f066c Only print format logging when the flag is set, even if also sending to log_debug. 2019-03-15 10:22:57 +00:00
nicm
672c49d512 The pane and window loops need to pass the window and pane tags when
they build their format.
2019-03-15 10:07:24 +00:00
nicm
27578815da Add a -v flag to display-message to show verbose messages as the format
is parsed, this gives the user a way to debug problems with formats
rather than just being confronted with (for example) a blank status
line.
2019-03-15 10:04:13 +00:00
nicm
25e2e22791 Add a limit on how far format_expand can recurse. 2019-03-14 23:34:41 +00:00
nicm
10d60faba5 Store the time in the format tree rather than passing it around. 2019-03-14 23:14:27 +00:00
Thomas Adam
2c755e3c55 Merge branch 'obsd-master' 2019-03-14 22:02:39 +00:00
nicm
bace79a571 Remove some unnecessary temporary variables and be much less strict
about spacing in style_parse.
2019-03-14 21:46:08 +00:00
nicm
4206bcc10e Add format flags for start and end window. 2019-03-14 21:41:30 +00:00
nicm
38064e7593 Add T format modifier like E but also do strftime(3). 2019-03-14 21:31:43 +00:00
nicm
1416ceb575 Accept 0 time as a shorthand for now to format_expand_time. 2019-03-14 21:27:26 +00:00
Thomas Adam
d58bccfc63 Merge branch 'obsd-master' 2019-03-14 20:02:38 +00:00
nicm
9bd4b96766 Fix ED1 (clear end of screen), reported by Marc Reisner. 2019-03-14 17:58:52 +00:00
Thomas Adam
0b32fa81b6 Merge branch 'obsd-master' 2019-03-14 12:02:41 +00:00
nicm
0425e3178d A little tidying in style_parse. 2019-03-14 10:19:52 +00:00
nicm
13f9a061ac Add a wrapper (struct style) around styles rather than using the
grid_cell directly. There will be some non-cell members soon.
2019-03-14 09:53:52 +00:00
Thomas Adam
f33d2ab29e Merge branch 'obsd-master' 2019-03-14 08:02:37 +00:00
nicm
1e9f8a3523 Missing space in capture-pane usage, from Ben Boeckel.
Also man page fixed from jmc.
2019-03-14 06:36:21 +00:00
nicm
2fbd491ff0 Add actual HPA (\033[`), the existing one is CHA. From Marc Reisner. 2019-03-14 06:33:43 +00:00
Thomas Adam
81b393a493 Merge branch 'obsd-master' 2019-03-14 00:02:37 +00:00
nicm
5755bfc619 Need to set attributes before clearing. 2019-03-13 22:01:22 +00:00
nicm
10f0094be9 The pane's style should be initialized to default before parsing the
argument.
2019-03-13 21:39:21 +00:00
Thomas Adam
786f5e505e Merge branch 'obsd-master' 2019-03-13 20:02:36 +00:00
nicm
6dffbc4849 Tweak format_replace logging. 2019-03-13 18:09:12 +00:00
Thomas Adam
e6fd429d58 Merge branch 'obsd-master' 2019-03-13 16:02:41 +00:00
nicm
95ab1aaaec Add formats to list sessions, windows or panes. 2019-03-13 15:37:28 +00:00
Nicholas Marriott
f628afc850 Update TODO. 2019-03-13 14:51:19 +00:00
nicm
49f04a997a Apply length limits and substitution even to literal formats. 2019-03-13 14:27:17 +00:00
Nicholas Marriott
1aeac384f3 Add to TODO. 2019-03-13 14:20:58 +00:00
nicm
9032ac2a05 Add E: format to expand a format twice (useful to expand the value of an
option).
2019-03-13 14:19:54 +00:00
nicm
71e00c718c Make format parsing build the modifiers into a list, standardize how
arguments are given and allow multiple modifiers on a format (separated
by ;).
2019-03-13 14:10:34 +00:00
Nicholas Marriott
7a6b21de65 Add to TODO. 2019-03-13 09:30:42 +00:00
Nicholas Marriott
4f5cb88c2b Add to TODO. 2019-03-13 09:30:42 +00:00
Thomas Adam
0e6eda02a6 Merge branch 'obsd-master' 2019-03-13 08:02:35 +00:00
nicm
feaa5660a3 Do not use origin for VPA. 2019-03-13 07:34:36 +00:00
Thomas Adam
b03f58b977 Merge branch 'obsd-master' 2019-03-13 00:02:38 +00:00
nicm
b2bc34af12 Set a flag on cells are genuinely empty (cleared and never written to)
and use tty_clear_line (which will choose the best escape sequence) to
clear any batches of cells with that flag when redrawing a line from the
stored screen.
2019-03-12 23:21:45 +00:00
Thomas Adam
400529eea0 Merge branch 'obsd-master' 2019-03-12 22:02:42 +00:00
nicm
938156d73b DECRC and DECSC apparently need to preserve origin mode as well, based
on a fix from Marc Reisner.
2019-03-12 20:02:47 +00:00
Thomas Adam
ba4a884d75 Merge branch 'obsd-master' 2019-03-12 20:02:38 +00:00
nicm
2796ae81d0 Fix HPA in origin mode. 2019-03-12 18:30:08 +00:00
nicm
f3f534a3a0 Tidy up a lot of &ictx->ctx by using a local variable. 2019-03-12 18:26:57 +00:00
Nicholas Marriott
6c3ee42568 Add a couple of things. 2019-03-12 17:59:46 +00:00
Nicholas Marriott
2f0ffe6adb Add to TODO. 2019-03-12 17:52:22 +00:00
Nicholas Marriott
4002dbf0c1 Fix session size regress. 2019-03-12 17:36:58 +00:00
Thomas Adam
162d3cb1f4 Merge branch 'obsd-master' 2019-03-12 16:02:37 +00:00
Thomas Adam
7d2004a8dd Merge branch 'obsd-master' 2019-03-12 14:02:40 +00:00
Nicholas Marriott
860acecc0d Fix up regress test for control client size. 2019-03-12 13:57:06 +00:00
nicm
028f9d1d87 Fix resizing of control clients, should be ignored until SIZECHANGED flag set. 2019-03-12 13:56:30 +00:00
nicm
303d20a758 Fix wrapping after origin mode change. 2019-03-12 13:14:14 +00:00
nicm
3f0efc050a When asked for a window index, return it even if the window exists. 2019-03-12 13:14:04 +00:00
nicm
7804fa1b82 Revert to not clearing history on RIS, apparently some bootloaders send
this and it doesn't really do any harm.
2019-03-12 12:58:40 +00:00
nicm
7b819357ff Tweak target debug logging. 2019-03-12 12:49:46 +00:00
Nicholas Marriott
595b52490e Add to TODO. 2019-03-12 12:12:35 +00:00
Thomas Adam
3ec05e9405 Merge branch 'obsd-master' 2019-03-12 12:02:42 +00:00
Nicholas Marriott
f8a30e1588 Update CHANGES. 2019-03-12 11:20:21 +00:00
nicm
3f6bfbaf2b Allow multiple modes to be open in a pane. A stack of open modes is kept
and the previous restored when the top is exited. If a mode that is
already on the stack is entered, the existing instance is moved to the
top as the active mode rather than being opened new.
2019-03-12 11:16:49 +00:00
Thomas Adam
95d340cc4f Merge branch 'obsd-master' 2019-03-12 10:02:39 +00:00
nicm
ff4c80d53d Add support for origin mode (DECOM, SM/RM ?6), from Marc Reisner. 2019-03-12 07:39:27 +00:00
Thomas Adam
bc72cf2f52 Merge branch 'obsd-master' 2019-03-08 12:02:39 +00:00
nicm
de730f68a4 Make the mode used to view command output (a variant of copy mode) use
its own mode definition struct with a different init function rather
than calling special setup functions.
2019-03-08 10:34:20 +00:00
nicm
9cc04a0f9a Do not use window mode entry after free. 2019-03-08 10:29:25 +00:00
Thomas Adam
349aeb806a Merge branch 'obsd-master' 2019-03-07 22:02:42 +00:00
nicm
f98c66ece8 Add a separate mode struct for the active window mode if any. 2019-03-07 20:24:21 +00:00
Thomas Adam
a1009e7bd3 Merge branch 'obsd-master' 2019-03-07 20:02:41 +00:00
nicm
3c24bc5617 Tidy changing the mode into window_copy_init_for_output. 2019-03-07 19:34:22 +00:00
nicm
7f093fcddc Make adding mode formats a function pointer as well. 2019-03-07 19:01:21 +00:00
Nicholas Marriott
5a564a0c1a Add to TODO. 2019-03-07 15:34:24 +00:00
Thomas Adam
9ebd630675 Merge branch 'obsd-master' 2019-03-04 12:01:28 +00:00
nicm
5cdd578906 Fix sense of aggressive-resize flag. 2019-03-04 09:29:52 +00:00
nicm
a870c255c4 Don't set client offset if client is not a terminal 2019-03-04 09:29:40 +00:00
Thomas Adam
d5c837904b Merge branch 'obsd-master' 2019-02-16 20:02:36 +00:00
nicm
fa33603dc1 Do not look at next key byte if the length is 0, originally from Shingo
NISHIOKA in GitHub issue 1601.
2019-02-16 19:04:34 +00:00
Thomas Adam
fa8294436c Merge branch 'obsd-master' 2019-02-16 14:02:37 +00:00
nicm
82f0c859a2 Use starting client cwd in config file, GitHub issue 1606. 2019-02-16 11:42:08 +00:00
Thomas Adam
9768091ee4 Merge branch 'obsd-master' 2019-02-09 20:02:36 +00:00
nicm
f9c396db41 Completion of command-alias members. 2019-02-09 18:18:36 +00:00
Nicholas Marriott
bdb7e48cba Redirect which stderr. 2019-02-06 14:45:35 +00:00
Thomas Adam
c42e89a598 Merge branch 'obsd-master' 2019-02-06 08:02:37 +00:00
nicm
afd3127d89 Add -b to display-panes like run-shell, GitHub issue 1559. 2019-02-06 07:36:06 +00:00
Thomas Adam
4ab3b18b36 Merge branch 'obsd-master' 2019-01-20 18:02:36 +00:00
nicm
2ea22fce5e Should use DECFRA if not default, not if default. From Karl Beldan. 2019-01-20 15:57:27 +00:00
Thomas Adam
cd8a7fb07b Merge branch 'obsd-master' 2019-01-15 14:02:37 +00:00
nicm
cd39920abd Should save the bg colour when setting it to default, not the fg. 2019-01-15 12:08:53 +00:00
Thomas Adam
469a9e9439 Merge branch 'obsd-master' 2019-01-15 12:02:36 +00:00
nicm
34c0807be6 Do not highlight characters which will not be copied, reported by
Jaroslaw Rzeszotko.
2019-01-15 09:56:31 +00:00
Thomas Adam
c9d482ab48 Merge branch 'obsd-master' 2018-12-18 14:02:40 +00:00
nicm
bde0224deb Pass window into mode functions. 2018-12-18 13:20:44 +00:00
Thomas Adam
b6cdac05c7 Merge branch 'obsd-master' 2018-12-18 00:02:37 +00:00
nicm
4e3d661284 Fix parsing of empty colon-separated fields, reported by Siarhei Siniak. 2018-12-17 21:52:59 +00:00
Thomas Adam
1c5093c1c4 Merge branch 'obsd-master' 2018-11-30 10:02:37 +00:00
nicm
67254ed8df Clear PANE_EXITED flag when starting new child process in case the pane
has been respawned.
2018-11-30 08:44:40 +00:00
Nicholas Marriott
6cf2f74fe9 osdep_get_cwd for NetBSD, from Leonardo Taccari. 2018-11-29 10:37:27 +00:00
Thomas Adam
1ed994a6c8 Merge branch 'obsd-master' 2018-11-28 12:02:36 +00:00
nicm
40d246b29c Handle UTF-8 in word-separators option, GitHub issue 1551. 2018-11-28 11:20:13 +00:00
Thomas Adam
2977317243 Merge branch 'obsd-master' 2018-11-22 12:02:38 +00:00
nicm
3a7b9d5735 Do not use PWD unless it actually matches the real working directory. 2018-11-22 10:36:40 +00:00
Nicholas Marriott
2eca63b98e Add to TODO. 2018-11-21 09:50:53 +00:00
Nicholas Marriott
c4b04b1624 Fixes for GNU/kFreeBSD from James Clarke. 2018-11-21 08:50:22 +00:00
Thomas Adam
efd01f3bfd Merge branch 'obsd-master' 2018-11-19 14:02:41 +00:00
nicm
749f67b7d8 evbuffer_new and bufferevent_new can both fail (when malloc fails) and
return NULL. GitHub issue 1547.
2018-11-19 13:35:40 +00:00
Thomas Adam
a7da2357a5 Merge branch 'obsd-master' 2018-11-15 12:02:37 +00:00
kn
f103927a52 Add [template] to display-panes and choose-{buffer,client,tree} usage
OK nicm
2018-11-15 10:38:53 +00:00
Thomas Adam
0fd73f238d Merge branch 'obsd-master' 2018-11-13 12:02:38 +00:00
nicm
50cdfc06fb Initialize context property in alignment test handler function. 2018-11-13 11:36:37 +00:00
Thomas Adam
3c6cdf0115 Merge branch 'obsd-master' 2018-11-12 16:02:37 +00:00
nicm
86e648d906 Allow style #[] in mode formats. 2018-11-12 14:18:10 +00:00
Thomas Adam
849d06c0df Merge branch 'obsd-master' 2018-11-08 20:02:37 +00:00
nicm
09aee53763 It isn't possible to specify buffer name to copy mode commands now, so
remove the function argument.
2018-11-08 18:49:19 +00:00
Thomas Adam
4efba0bf9f Merge branch 'obsd-master' 2018-11-07 10:02:39 +00:00
nicm
0c7f64458f If a non-repeating key is used when repeating, it should be treated as
an entirely new key press, not checked in root table and ignored if not
found. GitHub issue 1513.
2018-11-07 08:06:28 +00:00
Nicholas Marriott
ff77010f70 Add to TODO. 2018-11-07 08:00:15 +00:00
nicm
7339845c01 There is no reason wait-for has to be restricted to outside tmux. 2018-11-07 07:58:16 +00:00
Thomas Adam
99072e1d81 Merge branch 'obsd-master' 2018-11-06 16:02:37 +00:00
nicm
f9881b3b5d Correctly unzoom and redraw panes in switch-client. 2018-11-06 15:13:13 +00:00
Thomas Adam
b1ad075d11 Merge branch 'obsd-master' 2018-10-31 12:02:38 +00:00
nicm
ef904cfef2 Reset all flags in screen_write_reset, reported by Benjamin Poirier. 2018-10-31 10:05:47 +00:00
Thomas Adam
ddffda4da5 Merge branch 'obsd-master' 2018-10-28 18:02:36 +00:00
nicm
d5b92ac37e Do not printf NULL. 2018-10-28 16:10:02 +00:00
Thomas Adam
0a54a07909 Merge branch 'obsd-master' 2018-10-28 16:02:37 +00:00
nicm
65dd7345e0 Do not check for a key again without an escape if only \033 is present. 2018-10-28 15:34:27 +00:00
Nicholas Marriott
0cefdff1b6 Add to CHANGES. 2018-10-26 11:36:36 +01:00
Thomas Adam
e771f10dc6 Merge branch 'obsd-master' 2018-10-25 17:02:42 +01:00
nicm
fc41bf46ac Add a "terminal" colour which can be used instead of "default" in style
options for the terminal default colour, bypassing any inheritance from
other options. Prompted by a discussion with abieber@.
2018-10-25 15:13:38 +00:00
Nicholas Marriott
2808f8261c Tweak wording. 2018-10-19 08:38:12 +01:00
Nicholas Marriott
c234032fcf Do not use master here. 2018-10-19 08:37:31 +01:00
Nicholas Marriott
bc6211cca5 Instead of "master" for the version number, use "next-X.X". It is more
useful to include the next version number.
2018-10-19 08:35:23 +01:00
Thomas Adam
164590e34c Merge branch 'obsd-master' 2018-10-18 15:02:38 +01:00
nicm
9feb35b7c4 Document new refresh-client flags. 2018-10-18 13:03:45 +00:00
Thomas Adam
f44d02c7f5 Merge branch 'obsd-master' 2018-10-18 11:09:54 +01:00
nicm
646995384d Support for windows larger than visible on the attached client. This has
been a limitation for a long time.

There are two new options, window-size and default-size, and a new
command, resize-window. The force-width and force-height options and the
session_width and session_height formats have been removed.

The new window-size option tells tmux how to work out the size of
windows: largest means it picks the size of the largest session,
smallest the smallest session (similar to the old behaviour) and manual
means that it does not automatically resize windows. The default is
currently largest but this may change. aggressive-resize modifies the
choice of session for largest and smallest as it did before.

If a window is in a session attached to a client that is too small, only
part of the window is shown. tmux attempts to keep the cursor visible,
so the part of the window displayed is changed as the cursor moves (with
a small delay, to try and avoid excess redrawing when applications
redraw status lines or similar that are not currently visible). The
offset of the visible portion of the window is shown in status-right.

Drawing windows which are larger than the client is not as efficient as
those which fit, particularly when the cursor moves, so it is
recommended to avoid using this on slow machines or networks (set
window-size to smallest or manual).

The resize-window command can be used to resize a window manually. If it
is used, the window-size option is automatically set to manual for the
window (undo this with "setw -u window-size"). resize-window works in a
similar way to resize-pane (-U -D -L -R -x -y flags) but also has -a and
-A flags. -a sets the window to the size of the smallest client (what it
would be if window-size was smallest) and -A the largest.

For the same behaviour as force-width or force-height, use resize-window
-x or -y, and "setw -u window-size" to revert to automatic sizing..

If the global window-size option is set to manual, the default-size
option is used for new windows. If -x or -y is used with new-session,
that sets the default-size option for the new session.

The maximum size of a window is 10000x10000. But expect applications to
complain and much higher memory use if making a window excessively
big. The minimum size is the size required for the current layout
including borders.

The refresh-client command can be used to pan around a window, -U -D -L
-R moves up, down, left or right and -c returns to automatic cursor
tracking. The position is reset when the current window is changed.
2018-10-18 08:38:01 +00:00
Nicholas Marriott
2dfdb55ace Handle pan correctly when the terminal is bigger than the window. 2018-10-18 09:24:15 +01:00
nicm
a51668ca06 Support OSC 52 ? to read the top buffer inside tmux (when set-clipboard
is changed to on), also add refresh-client -l to ask tmux to use the
same mechanism to get the clipboard from the terminal outside
tmux. GitHub issue 1477.
2018-10-18 08:04:14 +00:00
nicm
bc0e527f32 Support for extended underline styles on terminals which offer them,
enabled by adding the Smulx capability with terminal-overrides (add
something like ',vte*:Smulx=\E[4\:%p1%dm'). GitHub issue 1492.
2018-10-18 07:57:57 +00:00
Nicholas Marriott
f54f171d5c Merge branch '2.8-rc' 2018-10-17 19:33:43 +01:00
Nicholas Marriott
01918cb017 tmux 2.8. 2018-10-17 19:29:35 +01:00
Nicholas Marriott
a2c52d9f26 Add to TODO. 2018-10-16 14:16:48 +01:00
Nicholas Marriott
407075d8c7 Add to TODO. 2018-10-16 14:12:33 +01:00
Nicholas Marriott
cdd8c93f54 Add to TODO file. 2018-10-15 09:09:40 +01:00
Nicholas Marriott
c88e945bc5 Support OSC 52 ? to read the top buffer inside tmux, also add
refresh-client -l to get the clipboard outside tmux. GitHub issue
1477.
2018-10-11 16:20:14 +01:00
Nicholas Marriott
018f1b8a80 Use :: for escaping : in terminal-overrides not \:. 2018-10-08 19:42:55 +01:00
Nicholas Marriott
4b9e76aaaa Support for extended underline styles, enabled by adding the Smulx
capability with terminal-overrides (add something like
'vte*:Smulx=\E[4\:%p1%dm'). GitHub issue 1492.
2018-10-08 13:21:37 +01:00
Nicholas Marriott
46847e9b2e Update TODO. 2018-10-08 13:06:37 +01:00
Nicholas Marriott
1952b3f746 Update CHANGES. 2018-10-07 15:06:40 +01:00
Nicholas Marriott
546060f340 Update TODO. 2018-10-07 15:01:41 +01:00
Thomas Adam
c44bb2df17 Merge branch 'obsd-master' 2018-10-03 17:02:38 +01:00
nicm
f7c85f3ed8 Do not move the cursor when the mouse wheel is used, GitHub issue 1493. 2018-10-03 15:27:55 +00:00
Nicholas Marriott
5a7cf897f2 Correctly check offset for drawing multiple cells, GitHub issue 1481. 2018-10-01 19:47:58 +01:00
Nicholas Marriott
8053b65f1e Fix select-pane -m, GitHub issuie 1490. 2018-09-28 09:38:22 +01:00
Thomas Adam
a00cdcdfcb Merge branch 'obsd-master' 2018-09-27 11:02:37 +01:00
nicm
228e1a3951 Use same working directory rules for jobs as new windows rather than
always starting in home, GitHub issue 1488.
2018-09-27 07:43:18 +00:00
Nicholas Marriott
ebaf54251a Add formats for when window is larger than client, and show offset in
status-right by default when larger.
2018-09-26 18:41:18 +01:00
Nicholas Marriott
b74b047730 More of revert. 2018-09-26 16:49:03 +01:00
Nicholas Marriott
04c6db2d0f Revert "Add a B flag to mark windows bigger than the client."
This reverts commit b4e74f4310.
2018-09-26 16:42:29 +01:00
Nicholas Marriott
6abb62df1e Change the B into a +. 2018-09-25 21:57:29 +01:00
Thomas Adam
7cf00d6b72 Merge branch 'obsd-master' 2018-09-25 17:02:41 +01:00
nicm
7d59f82cf9 Allow panes to be 1 line or column by redrawing instead of using the
scroll region, from Soeren Tempel in GitHub issue 1487.
2018-09-25 14:27:20 +00:00
Nicholas Marriott
cbf1504fca Do not clamp to needed size since it may be too big. 2018-09-25 15:15:44 +01:00
Nicholas Marriott
83b2d5c513 window_printable_flags needs an additional argument. 2018-09-25 09:55:49 +01:00
Nicholas Marriott
b4e74f4310 Add a B flag to mark windows bigger than the client. 2018-09-25 09:54:47 +01:00
Nicholas Marriott
26d73a7736 Ignore clients that are suspended or exiting. 2018-09-25 09:48:48 +01:00
Nicholas Marriott
57069287a1 Add to TODO. 2018-09-25 08:27:53 +01:00
Thomas Adam
4799f43298 Merge branch 'obsd-master' 2018-09-24 17:02:38 +01:00
nicm
7bc6c105b7 Only include pane status in minimum size if it is turned on, GitHub
issue 1480.
2018-09-24 15:29:56 +00:00
Nicholas Marriott
ad71e7f9d2 Calculate size when trimming RHS correctly. 2018-09-24 12:17:29 +01:00
Nicholas Marriott
71d2ab184b Fix up merge. 2018-09-12 07:15:27 +01:00
Thomas Adam
c067af8e7d Merge branch 'obsd-master' 2018-09-11 20:11:11 +01:00
Nicholas Marriott
3fa538181b Restore window_pane_visible for drawing panes and borders, fixes issue
1458 reported by Felix Rosencrantz, problem located by Avi Halachmi.
2018-09-11 18:34:53 +01:00
nicm
be2201200f The cursor position is limited to the margins for CUF and CUB, so turn
margins off for printing cells (like most everything else already
does). Problem reported by Thomas Sattler.
2018-09-11 17:31:01 +00:00
Nicholas Marriott
84df87011c x,y -> px,py. 2018-09-11 09:39:28 +01:00
Thomas Adam
5e36d52651 Merge branch 'obsd-master' 2018-09-11 09:21:16 +01:00
nicm
bd9133b31d Do not check for mouse events on pane borders when zoomed, based on a
fix from Avi Halachmi.
2018-09-11 06:37:54 +00:00
Thomas Adam
9b32758a0c Merge branch 'obsd-master' 2018-09-10 09:02:39 +01:00
Nicholas Marriott
5aa435a9f0 window_get_active_at needs to check visible also. 2018-09-10 07:51:11 +01:00
nicm
1b92afa799 Do not clear selection when searching. 2018-09-10 06:48:01 +00:00
Nicholas Marriott
a618271e12 Bring back window_pane_visible to stop input going to panes which are
hidden by zoom.
2018-09-10 07:19:17 +01:00
Nicholas Marriott
8598fd1bc5 Add to TODO. 2018-09-09 07:52:28 +01:00
Nicholas Marriott
59df942e09 Fixes to resize-window from Ben Boeckel. 2018-09-05 08:43:58 +01:00
Nicholas Marriott
621bb15b83 Cast parameters to tparm, from Christos Zoulas via Leonardo Taccari. 2018-09-05 08:42:17 +01:00
Thomas Adam
8e24b0bb3e Makefile.am: Add space between -f and argument
Required for some (older) aek instance -- namely NetBSD.
2018-09-04 13:13:32 +01:00
Thomas Adam
0c07b10b30 Merge branch 'obsd-master' 2018-09-03 11:02:39 +01:00
nicm
8f9491ddfe Allow a large line number to go to the end with goto-line, from Mark
Kelly in GitHub issue 1460.
2018-09-03 08:51:43 +00:00
nicm
9bab73f489 Fix selection test, from Takeshi Banse. 2018-09-03 08:47:27 +00:00
Nicholas Marriott
4e867202e8 Add to TODO. 2018-08-31 14:10:51 +01:00
Thomas Adam
e27b588b6c Merge branch 'obsd-master' 2018-08-29 21:02:37 +01:00
Nicholas Marriott
479cac4896 Fix target line for fast path in tty_draw_pane. 2018-08-29 20:52:27 +01:00
nicm
95e3e363ff Reset line flag when clearing selection, GitHub issue 1454. 2018-08-29 18:54:23 +00:00
Nicholas Marriott
7637518b07 Fix refresh-client flags. 2018-08-29 18:59:43 +01:00
Nicholas Marriott
9263a65b5f Update TODO. 2018-08-29 18:52:04 +01:00
Thomas Adam
b29028a914 Merge branch 'obsd-master' 2018-08-29 13:02:38 +01:00
Thomas Adam
1ea17a72f0 Merge branch 'obsd-master' 2018-08-29 11:02:36 +01:00
nicm
f57aa143c1 Keep any text killed in the command prompt with C-w and yank it with
C-y, only use the top buffer if no text has previously been killed. This
and previous change promped by discussion with kn@.
2018-08-29 09:50:32 +00:00
nicm
e53094bc5f Add C-Left and C-Right as aliases for M-b and M-f. 2018-08-29 08:56:51 +00:00
Thomas Adam
18b611bc8d Merge branch 'obsd-master' 2018-08-27 13:02:38 +01:00
nicm
79d2351ce8 Memory leaks, from Gang Fan in GitHub issue 1453. 2018-08-27 11:03:34 +00:00
Nicholas Marriott
79a9472dbc tty_clamp_* need to use wp->xoff and wp->yoff for the check (which
don't include status line, line ox and oy), but ctx->xoff and yp->off
to calculate the position (which do have the statu line).
2018-08-26 14:10:39 +01:00
Thomas Adam
d64daf874f Merge branch 'obsd-master' 2018-08-26 11:02:38 +01:00
nicm
1b4402c823 Add q: format prefix to escape sh(1) special characters. Suggested by
someone ages ago and then more recently in GitHub issue 1449.
2018-08-26 09:28:42 +00:00
Nicholas Marriott
7fbd2f5498 Do not draw cells which have no cell (zoomed and they are not active). 2018-08-25 13:55:50 +01:00
Thomas Adam
45841400f2 Merge branch 'obsd-master' 2018-08-23 21:02:36 +01:00
nicm
f38b5a1b58 all_jobs can be static. 2018-08-23 18:39:12 +00:00
Thomas Adam
da5d563375 Merge branch 'obsd-master' 2018-08-23 19:02:40 +01:00
nicm
bceccc6b63 Move job struct into job.c. 2018-08-23 15:45:05 +00:00
Nicholas Marriott
183193bdbc Update CHANGES. 2018-08-23 08:52:22 +01:00
Thomas Adam
5b93f0fcd3 Merge branch 'obsd-master' 2018-08-22 23:02:43 +01:00
Nicholas Marriott
ee3e0eb183 Update TODO. 2018-08-22 21:29:30 +01:00
nicm
55db3623bf Add StatusLeft and StatusRight mouse key modifiers for the left and
right parts of the status line.
2018-08-22 20:06:14 +00:00
Nicholas Marriott
db8849e7ca Update TODO. 2018-08-21 09:29:12 +01:00
Nicholas Marriott
bfc9fb4b3b Add flags to refresh-client (-U -D -L -R and -c) to pan a window that
is larger than the client manually. Bound to S-Up, S-Down, S-Left,
S-Right and Delete manually.

Also add aliases for keys DC = Delete, IC = Insert, and make
refresh-client -C accept XxY as well as X,Y to match default-size.
2018-08-21 09:27:19 +01:00
Nicholas Marriott
314ee137a9 The stored mouse position should not include the status line offset if any. 2018-08-21 09:10:23 +01:00
Thomas Adam
f36d2ecb07 Merge branch 'obsd-master' 2018-08-20 23:02:38 +01:00
nicm
98a478ceb3 Move offset of window list into status struct. 2018-08-20 20:41:58 +00:00
Nicholas Marriott
9f39470b38 Only screen-redraw.c needs to adjust for message or prompt when the
status line is off, get rid of tty_status_lines and just pass the
client into status_line_size so it can check the CLIENT_STATUSOFF flag
as well.
2018-08-20 20:05:34 +01:00
Nicholas Marriott
458b4b7701 Need to adjust for lines if at top. 2018-08-20 19:45:32 +01:00
Nicholas Marriott
31508228bc Use the y offset from the context which has already been adjusted for
the status line, also make tty_clamp_line adjust the y position.
2018-08-20 19:34:04 +01:00
Nicholas Marriott
1e8c9fb490 Update CHANGES. 2018-08-20 17:52:27 +01:00
Thomas Adam
098967a085 Merge branch 'obsd-master' 2018-08-20 17:02:39 +01:00
nicm
665f046950 Add -Z to find-window as well. 2018-08-20 15:00:42 +00:00
Nicholas Marriott
641191ab20 Support for windows larger than the client.
This adds two new options, window-size and default-size, and a new
command, resize-window.

The force-width and force-height options, and the session_width and
session_height formats have been removed.

The new window-size option tells tmux how to work out the size of
windows: largest means it picks the size of the largest session,
smallest the smallest session (similar to the old behaviour) and
manual means that it does not automatically resize
windows. aggressive-resize modifies the choice of session for largest
and smallest as it did before.

If a window is in a session attached to a client that is too small,
only part of the window is shown. tmux attempts to keep the cursor
visible, so the part of the window displayed is changed as the cursor
moves (with a small delay, to try and avoid excess redrawing when
applications redraw status lines or similar that are not currently
visible).

Drawing windows which are larger than the client is not as efficient
as those which fit, particularly when the cursor moves, so it is
recommended to avoid using this on slow machines or networks (set
window-size to smallest or manual).

The resize-window command can be used to resize a window manually. If
it is used, the window-size option is automatically set to manual for
the window (undo this with "setw -u window-size"). resize-window works
in a similar way to resize-pane (-U -D -L -R -x -y flags) but also has
-a and -A flags. -a sets the window to the size of the smallest client
(what it would be if window-size was smallest) and -A the largest.

For the same behaviour as force-width or force-height, use
resize-width -x or -y.

If the global window-size option is set to manual, the default-size
option is used for new windows. If -x or -y is used with new-session,
that sets the default-size option for the new session.

The maximum size of a window is 10000x10000. But expect applications
to complain and higher memory use if you make a window that big. The
minimum size is the size required for the current layout including
borders.

This change allows some code improvements, most notably that since
windows can now never be cropped, that code can be removed from the
layout code, and since panes can now never be outside the size of the
window, window_pane_visible can be removed.
2018-08-20 15:22:14 +01:00
nicm
9f2db6a0af Fix problems with page scrolling in copy mode, GitHub issue 1440 from
Amos Bird.
2018-08-20 13:51:09 +00:00
Thomas Adam
bf03197e18 Merge branch 'obsd-master' 2018-08-19 21:40:26 +01:00
nicm
cac4eadca0 Add a flag to force redrawing of the status line even if the content
hasn't changed, needed for resizing.
2018-08-19 20:13:07 +00:00
Thomas Adam
3310972d75 Merge branch 'obsd-master' 2018-08-19 21:02:37 +01:00
nicm
d95fad3d5f Expand \u and \U escape sequences in command strings, from Christopher
Hunt in GitHub issue 1443.
2018-08-19 19:03:46 +00:00
Thomas Adam
9ecf657703 Merge branch 'obsd-master' 2018-08-19 19:02:40 +01:00
nicm
88327c7698 Add a client redraw-window flag instead of the redraw-all flag and for
all just use the three flags together (window, borders, status).
2018-08-19 16:45:03 +00:00
Thomas Adam
e811132b05 Merge branch 'obsd-master' 2018-08-18 23:02:40 +01:00
nicm
bd2896b65e SESSION_UNATTACHED flag is no longer necessary now we have an attached
count instead.
2018-08-18 20:08:52 +00:00
Thomas Adam
522d751eee Merge branch 'obsd-master' 2018-08-18 19:02:39 +01:00
nicm
3bc08b0dc0 Some tidying and helper functions. 2018-08-18 16:14:03 +00:00
Nicholas Marriott
af679326b2 Now master again. 2018-08-17 11:46:08 +01:00
Nicholas Marriott
aa6e6fa0f2 2.8. 2018-08-17 11:45:40 +01:00
Nicholas Marriott
9bdbe171b7 2.8-rc. 2018-08-17 11:31:16 +01:00
Thomas Adam
98a3c98c28 Merge branch 'obsd-master' 2018-08-17 11:20:20 +01:00
Nicholas Marriott
de2ddddd60 Add a full stop. 2018-08-17 10:30:04 +01:00
nicm
a9ffb56b65 Add the KEYC_XTERM flag to all function keys that imply a modifier so
that they are correctly translated into xterm(1)-style keys. GitHub
issue 1437.
2018-08-16 14:04:03 +00:00
nicm
14b97fc889 Add size to arguments struct too. 2018-08-14 11:38:05 +00:00
nicm
d0a600ccaa Some tidying, use a struct for arguments (there will be more later) and
add a helper function.
2018-08-14 11:31:34 +00:00
Thomas Adam
a368548645 Merge branch 'obsd-master' 2018-08-10 11:41:07 +01:00
Nicholas Marriott
13fe06a459 Update CHANGES. 2018-08-10 10:21:40 +01:00
nicm
87e87030fe Whoops, didn't mean to commit this. 2018-08-09 09:54:22 +00:00
nicm
a5ef1f2ed6 Bump the UTF-8 character array up to 18 to allow for more combining
characters (some languages use up to five). This size doesn't make as
much difference now that UTF-8 goes into an extended cell. GitHub issue
1430.
2018-08-09 09:53:44 +00:00
nicm
f5d7a80272 calloc the mode data instead of malloc and initialize everything. 2018-08-05 08:59:30 +00:00
Thomas Adam
33f9b316a3 Merge branch 'obsd-master' 2018-08-02 21:02:25 +01:00
nicm
d6ff630498 Log command arguments. 2018-08-02 18:35:21 +00:00
Thomas Adam
eceaa9a493 Merge branch 'obsd-master' 2018-08-02 15:02:25 +01:00
Thomas Adam
2e19a5ecb9 Merge branch 'obsd-master' 2018-08-02 13:02:26 +01:00
nicm
fb1f0fee5a session_groups can be static also. 2018-08-02 11:56:12 +00:00
nicm
6048b0f483 Make key trees and some other bits static. 2018-08-02 11:44:07 +00:00
nicm
f12b857415 Minor tidying. 2018-08-02 11:18:34 +00:00
Thomas Adam
79bdca4638 Merge branch 'obsd-master' 2018-08-02 11:02:25 +01:00
nicm
21f8ac2766 Make display-panes block the client until a pane is chosen or it times out. 2018-08-02 07:55:16 +00:00
Thomas Adam
89b56c3451 Merge branch 'obsd-master' 2018-08-01 17:02:25 +01:00
nicm
fe7486d43b Initialize new lineflag member. 2018-08-01 15:22:40 +00:00
Thomas Adam
400b807d75 Merge branch 'obsd-master' 2018-07-31 17:02:31 +01:00
nicm
5f07da6227 Do not leak path or use it after free. 2018-07-31 13:06:44 +00:00
Thomas Adam
7eb3ef66e5 Merge branch 'obsd-master' 2018-07-31 13:02:25 +01:00
nicm
82776c456e Move struct screen_sel into screen.c and tidy up members that are only
used by copy mode.
2018-07-31 11:49:26 +00:00
nicm
80bdd89856 Clear history on RIS like most other terminals do. 2018-07-31 10:32:19 +00:00
Thomas Adam
ed6327c87b Merge branch 'obsd-master' 2018-07-30 13:02:25 +01:00
nicm
b21a710de7 Remove a leftover unused struct. 2018-07-30 11:24:55 +00:00
Thomas Adam
aa32457772 Merge branch 'obsd-master' 2018-07-23 21:02:25 +01:00
kn
5c78b48cdf Point to glob in section 7 for the actual list of special characters instead
the C API in section 3.

OK millert jmc nicm, "the right idea" deraadt
2018-07-23 19:02:49 +00:00
Thomas Adam
5fc2d4a4fc Merge branch 'obsd-master' 2018-07-17 21:02:24 +01:00
nicm
969af935f3 When a key isn't in the first table, we need to try the same key again
not the any key. Also rename some labels. Fixes GitHub issue 1406
reeported by Mark Kelly.
2018-07-17 18:02:40 +00:00
Thomas Adam
4f04b2094c Merge branch 'obsd-master' 2018-07-16 11:02:25 +01:00
nicm
0d88f8a78b Add an "Any" key to run a command if a key is pressed that is not bound
in the current key table. GitHub issue 1404.
2018-07-16 08:48:22 +00:00
Thomas Adam
6de991d855 Merge branch 'obsd-master' 2018-07-15 11:02:25 +01:00
nicm
ff67ef945d Fix new-window -k, GitHub issue 1403. 2018-07-15 06:57:13 +00:00
Thomas Adam
711b8164bb Merge branch 'obsd-master' 2018-07-11 11:02:26 +01:00
Nicholas Marriott
35985c0add Updates to CHANGES. 2018-07-11 09:51:18 +01:00
nicm
cfc81692e6 Expand formats in load-buffer and save-buffer. 2018-07-11 08:29:21 +00:00
Thomas Adam
a4a7c3c68d Merge branch 'obsd-master' 2018-07-11 09:02:25 +01:00
nicm
e7d53020b4 Helper function to shorten history. 2018-07-11 06:51:39 +00:00
nicm
03519021b9 Add function comments. 2018-07-11 06:43:45 +00:00
Thomas Adam
6f2698004e Merge branch 'obsd-master' 2018-07-06 09:02:24 +01:00
nicm
28a5bc8fae Add a rectangle_toggle format, from Hirokazu Hata. 2018-07-06 07:11:23 +00:00
Thomas Adam
20154f391e Merge branch 'obsd-master' 2018-07-04 15:02:25 +01:00
jmc
98c4291df2 whitespace needed between macro args and punctuation; 2018-07-04 13:27:32 +00:00
Nicholas Marriott
5c0ce192ed +README.ja. 2018-07-04 13:45:40 +01:00
nicm
cc743dc296 Add set-hook -R to run a hook immediately (useful to set multiple hooks
to the same thing).
2018-07-04 12:30:52 +00:00
nicm
d254293a6d Add pane focus hooks. 2018-07-04 12:25:26 +00:00
Thomas Adam
850c26dd46 Merge branch 'obsd-master' 2018-07-04 13:02:25 +01:00
Nicholas Marriott
db07f33886 Japanese README from Kohei Takada. 2018-07-04 10:47:14 +01:00
nicm
2fae6a5761 Add accessors for grid linedata member, for some future work. From Dan
Aloni.
2018-07-04 09:44:07 +00:00
Thomas Adam
2ee0962c96 Merge branch 'obsd-master' 2018-07-02 15:02:25 +01:00
nicm
42935bde71 Fire notify for select-layout, from George Nachman. 2018-07-02 12:39:14 +00:00
Nicholas Marriott
ffebf00585 Solaris 11.4 and later prefers mdoc, from Alan Coopersmith. 2018-07-02 13:36:44 +01:00
Thomas Adam
ead6d652ff Merge branch 'obsd-master' 2018-06-27 19:02:25 +01:00
nicm
a14aa788d4 Allow any punctuation (except :) as separator in s/x/y/, not only
/. From JINNOUCHI Yasushi in GitHub issue 1386.
2018-06-27 17:36:10 +00:00
Thomas Adam
d8721b35a0 Merge branch 'obsd-master' 2018-06-26 15:02:25 +01:00
nicm
97738921cd Style nit. 2018-06-26 13:21:28 +00:00
Thomas Adam
486ce9b098 Merge branch 'obsd-master' 2018-06-26 13:02:25 +01:00
nicm
8fb6666f17 Do not take address of a member of a NULL pointer. GitHub issue 1382
from Kamil Rytarowski.
2018-06-26 11:14:05 +00:00
Thomas Adam
cb77c2aa78 Merge branch 'obsd-master' 2018-06-25 19:02:24 +01:00
nicm
0c94c3fbee If there are only two panes, always use the other pane as the last
pane. Based on a change from Duy Nguyen in GitHub issue 1377.
2018-06-25 17:23:16 +00:00
Thomas Adam
94712a8b0d Merge branch 'obsd-master' 2018-06-24 23:02:25 +01:00
nicm
dec6ec9e29 Minor cosmetic improvement from KOIE Hidetaka. 2018-06-24 21:24:09 +00:00
Thomas Adam
9da78d72de Merge branch 'obsd-master' 2018-06-11 13:02:25 +01:00
nicm
d0c992306d Fix some compiler warnings; from Thomas Adam. 2018-06-11 11:14:10 +00:00
Thomas Adam
54c2d48d7d Merge branch 'obsd-master' 2018-06-09 19:02:29 +01:00
nicm
e6cbeb4f38 Make it possible to resize multiple panes by dragging corners, from Dan
Aloni in GitHub issue 1376.
2018-06-09 17:05:52 +00:00
Thomas Adam
4581240ced Merge branch 'obsd-master' 2018-06-08 23:02:25 +01:00
nicm
f6bad7efd7 Instead of working out which pane to resize with the mouse by walking
the panes list, look through the layout cells for the nearest border and
resize that cell. From Dan Aloni in GitHub issue 1374.
2018-06-08 20:54:22 +00:00
Thomas Adam
713e0ddef3 Merge branch 'obsd-master' 2018-06-08 13:02:25 +01:00
nicm
ff45b2d343 Tweak previous - check for a NULL client and simplify manual text. 2018-06-08 09:43:58 +00:00
Thomas Adam
91280f1fca Linux: include sys/file.h for flock() 2018-06-07 09:17:05 +01:00
Thomas Adam
7de25d86e1 Merge branch 'obsd-master' 2018-06-03 13:02:31 +01:00
nicm
ba31d3a88c Increment the lines counter when skipping a line to avoid an infinite
loop, and fix a check to avoid a potential out-of-bounds access. Problem
reported by Yuxiang Qin and tracked down by Karl Beldan; GitHub issue
1352.

Also a man page fix request by jmc@.
2018-06-03 10:17:30 +00:00
Thomas Adam
54cd292f09 Merge branch 'obsd-master' 2018-05-29 11:02:26 +01:00
nicm
d1f5142dab If foo doesn't exist and can't be expanded in #{?foo,a,b} then assume it
is false.
2018-05-29 09:10:30 +00:00
Thomas Adam
058d2b94dc Merge branch 'obsd-master' 2018-05-28 15:02:31 +01:00
nicm
c177a627d2 When looking for panes, ignore dead ones (with -1 file
descriptor). GitHub issue 1354.
2018-05-28 11:50:47 +00:00
Nicholas Marriott
8f8e0975f1 Add format string test, from "sadie-parayno" in GitHub issue 1358. 2018-05-28 12:48:15 +01:00
nicm
b602c1fb9a Document escaping inside conditionals, from "sadie-parayno" in GitHub
issue 1359.
2018-05-28 11:45:26 +00:00
Thomas Adam
7448b38327 Merge branch 'obsd-master' 2018-05-24 13:02:29 +01:00
Thomas Adam
85d5a377c5 Merge branch 'obsd-master' 2018-05-24 11:02:25 +01:00
nicm
b9a6162d2f Make server_client_get_cwd used (almost) everywhere we need to work out
the cwd, and do not fall back to "." as it is pretty useless. GitHub
issue 1331.
2018-05-24 09:42:49 +00:00
nicm
8f5903d7c3 Improve logging of the environment etc for new panes. 2018-05-24 09:34:54 +00:00
Thomas Adam
f0ac0d6793 Merge branch 'obsd-master' 2018-05-22 11:02:33 +01:00
nicm
2a04665626 Allow escaping , and } with # in #{}; GitHub issue 1332. 2018-05-22 08:49:12 +00:00
Nicholas Marriott
07c0970f54 Sync tmux description with manual. 2018-05-22 08:02:43 +01:00
Thomas Adam
546123f950 Merge branch 'obsd-master' 2018-05-20 15:02:25 +01:00
nicm
f2f9605c63 -T should not actually select the pane. 2018-05-20 11:48:34 +00:00
Thomas Adam
e4e060f2be Merge branch 'obsd-master' 2018-05-15 17:02:31 +01:00
nicm
e3b034fac7 Fix switch-client -l, from Jean-Marc Eurin in GitHub issue 1343. 2018-05-15 14:58:09 +00:00
Thomas Adam
82c0eed36d Merge branch 'obsd-master' 2018-05-09 19:02:35 +01:00
nicm
80994a8de1 Mention allow-rename with \033k. 2018-05-09 16:20:50 +00:00
Thomas Adam
9e1093b7d7 Merge branch 'obsd-master' 2018-05-09 11:02:26 +01:00
nicm
f2029f9d9e Another check for NULL window if looking for index. 2018-05-09 07:50:03 +00:00
Thomas Adam
b2a2c3a1e9 Merge branch 'obsd-master' 2018-05-07 17:02:31 +01:00
nicm
f915a4bf0c Handle terminfo colors > 256 correctly, GitHub issue 1337. 2018-05-07 13:39:09 +00:00
Thomas Adam
91b220525b Merge branch 'obsd-master' 2018-05-04 11:02:31 +01:00
nicm
988c6bc433 Improve logging of sessions. 2018-05-04 08:21:42 +00:00
Thomas Adam
1d858aa89e Merge branch 'obsd-master' 2018-05-03 19:02:26 +01:00
nicm
e24a077752 Use window target if specified and exists even when looking for an
index, fixes neww -a with -t as well.
2018-05-03 16:56:59 +00:00
Thomas Adam
645fe9013f Merge branch 'obsd-master' 2018-04-26 17:02:31 +01:00
guenther
896c1da7da Use <fcntl.h> instead of <sys/file.h> for open() and friends.
Delete a bunch of unnecessary #includes and sort to match style(9)
while doing the above cleanup.

ok deraadt@ krw@
2018-04-26 12:42:51 +00:00
Thomas Adam
d24bd7394d Merge branch 'obsd-master' 2018-04-23 17:02:32 +01:00
Nicholas Marriott
aebb17dc75 Sync imsg from OpenBSD. 2018-04-23 15:40:15 +01:00
nicm
d9d2f84a4b Add KRB5CCNAME to update-environment. 2018-04-23 14:03:06 +00:00
nicm
068d1b97b2 #aabbcc will use RGB if supported so don't say it is closest match. 2018-04-23 13:51:21 +00:00
nicm
4bf6f2706e Check whether cursor is at start or end when copying rectangular
selections, from tb@.
2018-04-23 13:46:34 +00:00
nicm
9f39652d87 Remove unnecessary brackets. 2018-04-23 13:43:08 +00:00
Thomas Adam
c48440fd40 Merge branch 'obsd-master' 2018-04-23 11:02:27 +01:00
nicm
1afe71cc0a rxvt-unicode has some funny behaviour when scrolling with the cursor not
at column 1, so move it back there first if possible. GitHub issue 1318.
2018-04-23 07:41:30 +00:00
Thomas Adam
0e1b339c10 Merge branch 'obsd-master' 2018-04-18 17:02:28 +01:00
nicm
3dceddd70e Change how display-message uses the client. Originally it was only
intended as the target client where the message should be displayed but
at some point (perhaps when -p was added), it was used for format
expansion too. This means it can get a bit weird where you have client
formats expanding for a client with a different current session than the
target session.

However, it is nice that display-message can be used to show information
about a specific client. So change so that the -c client will be used if
the session matches the target session (-t or default), otherwise the
best client will be chosen.
2018-04-18 14:35:37 +00:00
nicm
2595718dd3 Include source function name in grid_check_y logging. 2018-04-18 14:31:42 +00:00
Thomas Adam
6ebd737590 Merge branch 'obsd-master' 2018-04-18 15:02:25 +01:00
nicm
e64d078a4c Fix || example. 2018-04-18 12:50:11 +00:00
Nicholas Marriott
ae0b7c7d72 Some changes. 2018-04-13 17:59:11 +01:00
Nicholas Marriott
a770ef3e2a Merge branch '2.7-rc' 2018-04-13 17:50:40 +01:00
Nicholas Marriott
b95d1de8fd 2.7. 2018-04-13 17:47:36 +01:00
Thomas Adam
7e5262ae9a Merge branch 'obsd-master' 2018-04-11 13:02:28 +01:00
nicm
14ecb5032e Allow no client for rename-session, from Ryan Freeman. 2018-04-11 09:54:45 +00:00
Thomas Adam
1bd66b65a3 Merge branch 'obsd-master' 2018-04-10 13:02:28 +01:00
nicm
051a29ca03 A couple of fixes to the : form of SGR. Apparently there is an extra
argument that nobody knew about, so skip that if it exists. Also there
are a bunch of useless optional arguments at the end, so ignore those.
2018-04-10 11:20:15 +00:00
nicm
c6975b3bb4 Add x and X to choose-tree (with a confirmation prompt) to kill an
item. Suggested by Matt Zagrabelny.
2018-04-10 10:48:44 +00:00
Nicholas Marriott
68bf7c532b Merge branch '2.7-rc' 2018-04-06 12:53:43 +01:00
Thomas Adam
9b73d76ddd Merge branch 'obsd-master' 2018-04-06 11:02:24 +01:00
nicm
5d616f4c72 Fix link, from Eliran Gonen. 2018-04-06 09:09:38 +00:00
Thomas Adam
b5c0b2cae2 Merge branch 'obsd-master' 2018-03-29 10:28:18 +01:00
nicm
803b8815bd Do not crash on empty window, reported by Jamie Macdonald in GitHub
issue 1299. Patch from Thomas Adam.
2018-03-29 08:03:51 +00:00
Thomas Adam
640d97afd0 Merge branch 'obsd-master' 2018-03-23 20:02:31 +00:00
nicm
194e9f611b Fix a regression: do not warn about no client in rename-window. 2018-03-23 19:17:03 +00:00
Thomas Adam
5512de6a61 Merge branch 'obsd-master' 2018-03-23 10:02:30 +00:00
nicm
26792b9035 Fix size calculation when spreading out panes. 2018-03-23 07:44:44 +00:00
Nicholas Marriott
919f55ac4a Merge branch '2.7-rc' 2018-03-22 11:26:32 +00:00
Nicholas Marriott
80283f99fb Revert "2.7-rc."
This reverts commit ced74bd72c.
2018-03-22 11:18:26 +00:00
112 changed files with 8567 additions and 4294 deletions

173
CHANGES
View File

@@ -1,9 +1,170 @@
CHANGES FROM 2.8 to 2.9
* Attempt to preserve horizontal cursor position as well as vertical with
reflow.
* Rewrite main-vertical and horizontal and change layouts to better handle the
case where all panes won't fit into the window size, reduce problems with
pane border status lines and fix other bugs mostly found by Thomas Sattler.
* Add format variables for the default formats in the various modes
(tree_mode_format and so on) and add a -a flag to display-message to list
variables with values.
* Add a -v flag to display-message to show verbose messages as the format is
parsed, this allows formats to be debugged
* Add support for HPA (\033[`).
* Add support for origin mode (\033[?6h).
* No longer clear history on RIS.
* Extend the #[] style syntax and use that together with previous format
changes to allow the status line to be entirely configured with a single
option.
Now that it is possible to configure their content, enable the existing code
that lets the status line be multiple lines in height. The status option can
now take a value of 2, 3, 4 or 5 (as well as the previous on or off) to
configure more than one line. The new status-format array option configures
the format of each line, the default just references the existing status-*
options, although some of the more obscure status options may be eliminated
in time.
Additions to the #[] syntax are: "align" to specify alignment (left, centre,
right), "list" for the window list and "range" to configure ranges of text
for the mouse bindings.
The "align" keyword can also be used to specify alignment of entries in tree
mode and the pane status lines.
* Add E: and T: format modifiers to expand a format twice (useful to expand the
value of an option).
* The individual -fg, -bg and -attr options have been removed; they
were superseded by -style options in tmux 1.9.
* Allow more than one mode to be opened in a pane. Modes are kept on a stack
and retrieved if the same mode is entered again. Exiting the active mode goes
back to the previous one.
* When showing command output in copy mode, call it view mode instead (affects
pane_mode format).
* Add -b to display-panes like run-shell.
* Handle UTF-8 in word-separators option.
* New "terminal" colour allowing options to use the terminal default colour
rather than inheriting the default from a parent option.
* Do not move the cursor in copy mode when the mouse wheel is used.
* Use the same working directory rules for jobs as new windows rather than
always starting in the user's home.
* Allow panes to be one line or column in size.
* Go to last line when goto-line number is out of range in copy mode.
* Yank previously cut text if any with C-y in the command prompt, only use the
buffer if no text has been cut.
* Add q: format modifier to quote shell special characters.
* Add StatusLeft and StatusRight mouse locations (keys such as
MouseDown1StatusLeft) for the status-left and status-right areas of the
status line.
* Add -Z to find-window.
* Support for windows larger than the client. This adds two new options,
window-size and default-size, and a new command, resize-window. The
force-width and force-height options and the session_width and session_height
formats have been removed.
The new window-size option tells tmux how to work out the size of windows:
largest means it picks the size of the largest session, smallest the smallest
session (similar to the old behaviour) and manual means that it does not
automatically resize windows. aggressive-resize modifies the choice of
session for largest and smallest as it did before.
If a window is in a session attached to a client that is too small, only part
of the window is shown. tmux attempts to keep the cursor visible, so the part
of the window displayed is changed as the cursor moves (with a small delay,
to try and avoid excess redrawing when applications redraw status lines or
similar that are not currently visible).
Drawing windows which are larger than the client is not as efficient as those
which fit, particularly when the cursor moves, so it is recommended to avoid
using this on slow machines or networks (set window-size to smallest or
manual).
The resize-window command can be used to resize a window manually. If it is
used, the window-size option is automatically set to manual for the window
(undo this with "setw -u window-size"). resize-window works in a similar way
to resize-pane (-U -D -L -R -x -y flags) but also has -a and -A flags. -a
sets the window to the size of the smallest client (what it would be if
window-size was smallest) and -A the largest.
For the same behaviour as force-width or force-height, use resize-window -x
or -y.
If the global window-size option is set to manual, the default-size option is
used for new windows. If -x or -y is used with new-session, that sets the
default-size option for the new session.
The maximum size of a window is 10000x10000. But expect applications to
complain and higher memory use if making a window that big. The minimum size
is the size required for the current layout including borders.
The refresh-client command can be used to pan around a window, -U -D -L -R
moves up, down, left or right and -c returns to automatic cursor
tracking. The position is reset when the current window is changed.
CHANGES FROM 2.7 to 2.8
* Make display-panes block the client until a pane is chosen or it
times out.
* Clear history on RIS like most other terminals do.
* Add an "Any" key to run a command if a key is pressed that is not
bound in the current key table.
* Expand formats in load-buffer and save-buffer.
* Add a rectangle_toggle format.
* Add set-hook -R to run a hook immediately.
* Add README.ja.
* Add pane focus hooks.
* Allow any punctuation as separator for s/x/y not only /.
* Improve resizing with the mouse (fix resizing the wrong pane in some
layouts, and allow resizing multiple panes at the same time).
* Allow , and } to be escaped in formats as #, and #}.
* Add KRB5CCNAME to update-environment.
* Change meaning of -c to display-message so the client is used if it
matches the session given to -t.
* Fixes to : form of SGR.
* Add x and X to choose-tree to kill sessions, windows or panes.
CHANGES FROM 2.6 TO 2.7
* Remove EVENT_* variables from environment on platforms where tmux uses them
so they do not pass on to panes.
* Fixed for hooks at server exit.
* Fixes for hooks at server exit.
* Remove SGR 10 (was equivalent to SGR 0 but no other terminal seems to do
this).
@@ -255,7 +416,7 @@ CHANGES FROM 2.4 TO 2.5, 09 May 2017
* Do not redraw a client unless we realistically think it can accept the data -
defer redraws until the client has nothing else waiting to write.
CHANGES FROM 2.3 TO 2.4, 20 April 2017
Incompatible Changes
@@ -541,7 +702,7 @@ Normal Changes
==============
* Fix crash due to uninitialized lastwp member of layout_cell
* Fix -fg/-bg/-style with 256 colour terminals.
* Fix -fg/-bg/-style with 256 colour terminals.
CHANGES FROM 1.8 TO 1.9, 20 February 2014
@@ -899,7 +1060,7 @@ CHANGES FROM 1.2 TO 1.3, 18 July 2010
* Run job commands explicitly in the global environment (which can be modified
with setenv -g), rather than with the environment tmux started with.
* Use the machine's hostname as the default title, instead of an empty string.
* Prevent double free if the window option remain-on-exit is set.
* Prevent double free if the window option remain-on-exit is set.
* Key string conversions rewritten.
* Mark zombie windows as dead in the choose-window list.
* Tiled layout added.
@@ -1001,7 +1162,7 @@ CHANGES FROM 1.0 TO 1.1, 05 November 2009
* New lock-client (alias lockc), and lock-session (alias locks) commands to
lock a particular client, or all clients attached to a session.
* Support C-n/C-p/C-v/M-v with emacs keys in choice mode.
* Use : for goto line rather than g in vi mode.
* Use : for goto line rather than g in vi mode.
* Try to guess which client to use when no target client was specified. Finds
the current session, and if only one client is present, use it. Otherwise,
return the most recently used client.
@@ -1132,7 +1293,7 @@ The list of older changes is below.
* main-horizontal layout and main-pane-height option to match vertical.
* New window option main-pane-width to set the width of the large left pane with
main-vertical (was left-vertical) layout.
main-vertical (was left-vertical) layout.
* Lots of layout cleanup. manual layout is now manual-vertical.
16 May 2009

View File

@@ -18,7 +18,7 @@ This should include at least the output of:
$ uname -sp && tmux -V && echo $TERM
Please do not report bugs (crashes, incorrect behaviour) without reproducing on
a tmux built from Git master.
a tmux built from the latest code in Git.
Note that TERM inside tmux must be a variant of screen or tmux (for example:
screen or screen-256color, tmux or tmux-256color). Please ensure this is the

View File

@@ -6,7 +6,7 @@ CLEANFILES = tmux.1.mdoc tmux.1.man
# Distribution tarball options.
EXTRA_DIST = \
CHANGES README COPYING example_tmux.conf compat/*.[ch] \
CHANGES README README.ja COPYING example_tmux.conf compat/*.[ch] \
osdep-*.c mdoc2man.awk tmux.1
# Preprocessor flags.
@@ -100,6 +100,7 @@ dist_tmux_SOURCES = \
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 \
@@ -131,6 +132,7 @@ dist_tmux_SOURCES = \
control.c \
environ.c \
format.c \
format-draw.c \
grid-view.c \
grid.c \
hooks.c \
@@ -195,7 +197,7 @@ install-exec-hook:
>$(srcdir)/tmux.1.mdoc; \
else \
sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1| \
$(AWK) -f$(srcdir)/mdoc2man.awk >$(srcdir)/tmux.1.man; \
$(AWK) -f $(srcdir)/mdoc2man.awk >$(srcdir)/tmux.1.man; \
fi
$(mkdir_p) $(DESTDIR)$(mandir)/man1
$(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \

6
README
View File

@@ -1,8 +1,8 @@
Welcome to tmux!
tmux is a "terminal multiplexer", it enables a number of terminals (or windows)
to be accessed and controlled from a single terminal. tmux is intended to be a
simple, modern, BSD-licensed alternative to programs such as GNU screen.
tmux is a terminal multiplexer: it enables a number of terminals to be created,
accessed, and controlled from a single screen. tmux may be detached from a
screen and continue running in the background, then later reattached.
This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris.

62
README.ja Normal file
View File

@@ -0,0 +1,62 @@
tmuxへようこそ!
tmuxはターミナルマルチプレクサーです。複数のターミナルを一つのスクリーン内に作成し、操作することができます。
バックグラウンドで処理を実行中に一度スクリーンから離れて後から復帰することも可能です。
OpenBSD、FreeBSD、NetBSD、Linux、OS X、Solarisで実行できます。
tmuxはlibevent 2.x.に依存します。 下記からダウンロードしてください。
http://libevent.org
また、ncursesも必要です。こちらからどうぞ。
http://invisible-island.net/ncurses/
tarballでのtmuxのビルドとインストール方法。
$ ./configure && make
$ sudo make install
tmuxはutmp(5)をアップデートするためにutempterを使うことができます。もしインストール済みであればオプション「--enable-utempter」をつけて実行してください。
リポジトリから最新バージョンを手に入れるためには下記を実行。
$ git clone https://github.com/tmux/tmux.git
$ cd tmux
$ sh autogen.sh
$ ./configure && make
(ビルドのためにはlibevent、ncurses libraries、headersに加えて、C compiler、make、autoconf、automake、pkg-configが必要です。)
詳しい情報はhttp://git-scm.comをご覧ください。修正はメール<tmux-users@googlegroups.com>宛、もしくはhttps://github.com/tmux/tmux/issuesにて受け付けています。
tmuxのドキュメントについてはtmux.1マニュアルをご覧ください。こちらのコマンドで参照可能です。
$ nroff -mdoc tmux.1|less
サンプル設定は本リポジトリのexample_tmux.confに
また、bash-completionファイルは下記にあります。
https://github.com/imomaliev/tmux-bash-completion
「-v」や「-vv」を指定することでデバッグモードでの起動が可能です。カレントディレクトリにサーバーやクライアントのログファイルが生成されます。
議論やバグレポート用のメーリングリストにはこちらから参加可能です。
https://groups.google.com/forum/#!forum/tmux-users
gitコミットについての連絡先
https://groups.google.com/forum/#!forum/tmux-git
購読は<tmux-users+subscribe@googlegroups.com>までメールをお願いします。
バグレポートや機能追加(特にコードへの貢献)は大歓迎です。こちらにご連絡ください。
tmux-users@googlegroups.com
本ファイル、CHANGES、 FAQ、SYNCINGそしてTODOはISC licenseで保護されています。
その他のファイルのライセンスや著作権については、ファイルの上部に明記されています。
-- Nicholas Marriott <nicholas.marriott@gmail.com>

38
TODO
View File

@@ -16,13 +16,11 @@
* way to set socket path from config file
- format improvements:
* option to quote format (#{q:session_name})
* some way to pad # stuff with spaces
* formats to show if a window is linked into multiple sessions, into
multiple attached sessions, and is the active window in multiple
attached sessions?
* comparison operators like < and > (for #{version}?)
* %else statement in config file
- improve monitor-*:
* straighten out rules for multiple clients
@@ -31,7 +29,7 @@
* perhaps monitor /all/ panes in the window not just one
- improve mouse support:
* bind commands to mouse in different areas?
* bind commands to mouse in different areas?
* commands executed when clicking on a pattern (URL)
- warts on current naming:
@@ -50,6 +48,11 @@
dragging it should select by word. compare how xterm works. GitHub
issue 682)
* key to search for word under cursor (GitHub issue 1240)
* when entering copy mode, should copy grid so that input does not
need to be suspended
* allow the prefix for automatic buffers to be specified as part of the
key binding to allow session buffers or similar (GitHub issue 1501)
* copy-pipe should be synchronous (GitHub issue 1517)
- layout stuff
* way to tag a layout as a number/name
@@ -61,7 +64,6 @@
not attached to a cell at all. this could be the time to introduce
panelink to replace layout_cell
* way to set hints/limits about pane size for resizing
* panning over window (window larger than visible)
* a mode where one application can cross two panes (ie x|y, width =
COLUMNS/2 but height = ROWS * 2)
* separate active panes for different clients
@@ -109,11 +111,15 @@
* marks in history, automatically add (move?) one when pane is changed
* this doesn't work, need pane reference count probably:
bind -n DoubleClick3Status confirm-before -p "kill-window #I? (y/n)" kill-window
* respawn -c flag same as neww etc
* marker lines in history (GitHub issue 1042)
* tree mode stuff: make command prompt (:) common code so all modes get it,
predefined filters, tag-all key, ...
* drag panes and windows around to move/swap them in choose mode
* flag to specify environment to new-window, split-window,
new-session (issue 1498)
* multiple column panes (issue 1503)
* support for ZERO WIDTH JOINER U+200D
* individual pane synchronize-panes (with pane options?); issue 1638
- hooks
* more hooks for various things
@@ -131,3 +137,25 @@
* finish hooks for notifys
* for session_closed, if no sessions at all, perhaps fake up a
temporary one
- pan
* tty_window_offset should try to keep as much off active pane
visible as possible
* rather than centering cursor it might be better if only
moved offset when it gets close to an edge?
----
TODO soonish maybe:
- Store hooks as options, issue 1619.
- Support buffer prefixes, issue 1501.
- copy-pipe should be synchronous, issue 1517.
- -E flag to pass environment to new-*, issue 1498.
- Copy mode searching is slow when there is a big history, issue 1545.
- Grid "block" stuff, issue 1269. Can be used potentially for compression of
history (bit silly really though), reflow performance (can reflow blocks on
demand). It would possibly be good if history-limit could be global and
collected LRU.
- Command aliases should be able to override builtin commands in order to add
default flags (some mechanism needed to avoid recursion). GitHub issue 1630.

View File

@@ -25,13 +25,13 @@
const char *
attributes_tostring(int attr)
{
static char buf[128];
static char buf[512];
size_t len;
if (attr == 0)
return ("none");
len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s",
len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s",
(attr & GRID_ATTR_BRIGHT) ? "bright," : "",
(attr & GRID_ATTR_DIM) ? "dim," : "",
(attr & GRID_ATTR_UNDERSCORE) ? "underscore," : "",
@@ -39,7 +39,11 @@ attributes_tostring(int attr)
(attr & GRID_ATTR_REVERSE) ? "reverse," : "",
(attr & GRID_ATTR_HIDDEN) ? "hidden," : "",
(attr & GRID_ATTR_ITALICS) ? "italics," : "",
(attr & GRID_ATTR_STRIKETHROUGH) ? "strikethrough," : "");
(attr & GRID_ATTR_STRIKETHROUGH) ? "strikethrough," : "",
(attr & GRID_ATTR_UNDERSCORE_2) ? "double-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "");
if (len > 0)
buf[len - 1] = '\0';
@@ -52,6 +56,25 @@ attributes_fromstring(const char *str)
const char delimiters[] = " ,|";
int attr;
size_t end;
u_int i;
struct {
const char* name;
int attr;
} table[] = {
{ "bright", GRID_ATTR_BRIGHT },
{ "bold", GRID_ATTR_BRIGHT },
{ "dim", GRID_ATTR_DIM },
{ "underscore", GRID_ATTR_UNDERSCORE },
{ "blink", GRID_ATTR_BLINK },
{ "reverse", GRID_ATTR_REVERSE },
{ "hidden", GRID_ATTR_HIDDEN },
{ "italics", GRID_ATTR_ITALICS },
{ "strikethrough", GRID_ATTR_STRIKETHROUGH },
{ "double-underscore", GRID_ATTR_UNDERSCORE_2 },
{ "curly-underscore", GRID_ATTR_UNDERSCORE_3 },
{ "dotted-underscore", GRID_ATTR_UNDERSCORE_4 },
{ "dashed-underscore", GRID_ATTR_UNDERSCORE_5 }
};
if (*str == '\0' || strcspn(str, delimiters) == 0)
return (-1);
@@ -64,24 +87,15 @@ attributes_fromstring(const char *str)
attr = 0;
do {
end = strcspn(str, delimiters);
if ((end == 6 && strncasecmp(str, "bright", end) == 0) ||
(end == 4 && strncasecmp(str, "bold", end) == 0))
attr |= GRID_ATTR_BRIGHT;
else if (end == 3 && strncasecmp(str, "dim", end) == 0)
attr |= GRID_ATTR_DIM;
else if (end == 10 && strncasecmp(str, "underscore", end) == 0)
attr |= GRID_ATTR_UNDERSCORE;
else if (end == 5 && strncasecmp(str, "blink", end) == 0)
attr |= GRID_ATTR_BLINK;
else if (end == 7 && strncasecmp(str, "reverse", end) == 0)
attr |= GRID_ATTR_REVERSE;
else if (end == 6 && strncasecmp(str, "hidden", end) == 0)
attr |= GRID_ATTR_HIDDEN;
else if (end == 7 && strncasecmp(str, "italics", end) == 0)
attr |= GRID_ATTR_ITALICS;
else if (end == 13 && strncasecmp(str, "strikethrough", end) == 0)
attr |= GRID_ATTR_STRIKETHROUGH;
else
for (i = 0; i < nitems(table); i++) {
if (end != strlen(table[i].name))
continue;
if (strncasecmp(str, table[i].name, end) == 0) {
attr |= table[i].attr;
break;
}
}
if (i == nitems(table))
return (-1);
str += end + strspn(str + end, delimiters);
} while (*str != '\0');

13
cfg.c
View File

@@ -37,6 +37,7 @@ struct cfg_cond {
};
TAILQ_HEAD(cfg_conds, cfg_cond);
struct client *cfg_client;
static char *cfg_file;
int cfg_finished;
static char **cfg_causes;
@@ -94,7 +95,7 @@ start_cfg(void)
* command queue is currently empty and our callback will be at the
* front - we need to get in before MSG_COMMAND.
*/
c = TAILQ_FIRST(&clients);
cfg_client = c = TAILQ_FIRST(&clients);
if (c != NULL) {
cfg_item = cmdq_get_callback(cfg_client_done, NULL);
cmdq_append(c, cfg_item);
@@ -339,15 +340,17 @@ cfg_print_causes(struct cmdq_item *item)
void
cfg_show_causes(struct session *s)
{
struct window_pane *wp;
u_int i;
struct window_pane *wp;
struct window_mode_entry *wme;
u_int i;
if (s == NULL || cfg_ncauses == 0)
return;
wp = s->curw->window->active;
window_pane_set_mode(wp, &window_copy_mode, NULL, NULL);
window_copy_init_for_output(wp);
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
for (i = 0; i < cfg_ncauses; i++) {
window_copy_add(wp, "%s", cfg_causes[i]);
free(cfg_causes[i]);

View File

@@ -17,11 +17,10 @@
*/
#include <sys/types.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <errno.h>
#include <event.h>
@@ -223,7 +222,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
const char *ttynam, *cwd;
pid_t ppid;
enum msgtype msg;
char *cause, path[PATH_MAX];
char *cause;
struct termios tio, saved_tio;
size_t size;
@@ -278,9 +277,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL);
/* Save these before pledge(). */
if ((cwd = getenv("PWD")) == NULL &&
(cwd = getcwd(path, sizeof path)) == NULL &&
(cwd = find_home()) == NULL)
if ((cwd = find_cwd()) == NULL && (cwd = find_home()) == NULL)
cwd = "/";
if ((ttynam = ttyname(STDIN_FILENO)) == NULL)
ttynam = "";

View File

@@ -100,6 +100,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
s->cwd = format_single(item, cflag, c, s, wl, wp);
}
c->last_session = c->session;
if (c->session != NULL) {
if (dflag) {
TAILQ_FOREACH(c_loop, &clients, entry) {
@@ -114,6 +115,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
c->session = s;
if (~item->shared->flags & CMDQ_SHARED_REPEAT)
server_client_set_key_table(c, NULL);
tty_update_client_offset(c);
status_timer_start(c);
notify_client("client-session-changed", c);
session_update_activity(s, NULL);
@@ -141,6 +143,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
c->session = s;
server_client_set_key_table(c, NULL);
tty_update_client_offset(c);
status_timer_start(c);
notify_client("client-session-changed", c);
session_update_activity(s, NULL);

View File

@@ -76,7 +76,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
window_lost_pane(w, wp);
layout_close_pane(wp);
w = wp->window = window_create(dst_s->sx, dst_s->sy);
w = wp->window = window_create(w->sx, w->sy);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
w->active = wp;

View File

@@ -41,7 +41,7 @@ const struct cmd_entry cmd_capture_pane_entry = {
.args = { "ab:CeE:JpPqS:t:", 0, 0 },
.usage = "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] "
"[-S start-line]" CMD_TARGET_PANE_USAGE,
"[-S start-line] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -199,8 +199,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
size_t len;
if (self->entry == &cmd_clear_history_entry) {
if (wp->mode == &window_copy_mode)
window_pane_reset_mode(wp);
window_pane_reset_mode_all(wp);
grid_clear_history(wp->base.grid);
return (CMD_RETURN_NORMAL);
}

View File

@@ -32,7 +32,7 @@ const struct cmd_entry cmd_choose_tree_entry = {
.args = { "F:Gf:NO:st:wZ", 0, 1 },
.usage = "[-GNsw] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE,
CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -46,7 +46,7 @@ const struct cmd_entry cmd_choose_client_entry = {
.args = { "F:f:NO:t:Z", 0, 1 },
.usage = "[-N] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE,
CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -60,7 +60,7 @@ const struct cmd_entry cmd_choose_buffer_entry = {
.args = { "F:f:NO:t:Z", 0, 1 },
.usage = "[-N] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE,
CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },

View File

@@ -60,7 +60,6 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
struct client *c = item->client;
struct session *s;
struct window_pane *wp = item->target.wp;
int flag;
if (args_has(args, 'M')) {
if ((wp = cmd_mouse_pane(&shared->mouse, &s, NULL)) == NULL)
@@ -74,18 +73,11 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (wp->mode != &window_copy_mode) {
flag = window_pane_set_mode(wp, &window_copy_mode, NULL, NULL);
if (flag != 0)
return (CMD_RETURN_NORMAL);
window_copy_init_from_pane(wp, args_has(self->args, 'e'));
}
if (args_has(args, 'M')) {
if (wp->mode != NULL && wp->mode != &window_copy_mode)
return (CMD_RETURN_NORMAL);
if (window_pane_set_mode(wp, &window_copy_mode, NULL, args) != 0)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'M'))
window_copy_start_drag(c, &shared->mouse);
}
if (wp->mode == &window_copy_mode && args_has(self->args, 'u'))
if (args_has(self->args, 'u'))
window_copy_pageup(wp, 0);
return (CMD_RETURN_NORMAL);

View File

@@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = {
.name = "display-message",
.alias = "display",
.args = { "c:pt:F:", 0, 1 },
.usage = "[-p] [-c target-client] [-F format] "
.args = { "ac:pt:F:v", 0, 1 },
.usage = "[-apv] [-c target-client] [-F format] "
CMD_TARGET_PANE_USAGE " [message]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -49,23 +49,31 @@ const struct cmd_entry cmd_display_message_entry = {
.exec = cmd_display_message_exec
};
static void
cmd_display_message_each(const char *key, const char *value, void *arg)
{
struct cmdq_item *item = arg;
cmdq_print(item, "%s=%s", key, value);
}
static enum cmd_retval
cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
struct client *c, *target_c;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
const char *template;
char *msg;
struct format_tree *ft;
int flags;
if (args_has(args, 'F') && args->argc != 0) {
cmdq_error(item, "only one of -F or argument must be given");
return (CMD_RETURN_ERROR);
}
c = cmd_find_client(item, args_get(args, 'c'), 1);
template = args_get(args, 'F');
if (args->argc != 0)
@@ -73,10 +81,31 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
if (template == NULL)
template = DISPLAY_MESSAGE_TEMPLATE;
ft = format_create(item->client, item, FORMAT_NONE, 0);
format_defaults(ft, c, s, wl, wp);
/*
* -c is intended to be the client where the message should be
* displayed if -p is not given. But it makes sense to use it for the
* formats too, assuming it matches the session. If it doesn't, use the
* best client for the session.
*/
c = cmd_find_client(item, args_get(args, 'c'), 1);
if (c != NULL && c->session == s)
target_c = c;
else
target_c = cmd_find_best_client(s);
if (args_has(self->args, 'v'))
flags = FORMAT_VERBOSE;
else
flags = 0;
ft = format_create(item->client, item, FORMAT_NONE, flags);
format_defaults(ft, target_c, s, wl, wp);
msg = format_expand_time(ft, template, time(NULL));
if (args_has(args, 'a')) {
if (item != NULL)
format_each(ft, cmd_display_message_each, item);
return (CMD_RETURN_NORMAL);
}
msg = format_expand_time(ft, template);
if (args_has(self->args, 'p'))
cmdq_print(item, "%s", msg);
else if (c != NULL)

View File

@@ -37,8 +37,8 @@ const struct cmd_entry cmd_display_panes_entry = {
.name = "display-panes",
.alias = "displayp",
.args = { "d:t:", 0, 1 },
.usage = "[-d duration] " CMD_TARGET_CLIENT_USAGE,
.args = { "bd:t:", 0, 1 },
.usage = "[-b] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]",
.flags = CMD_AFTERHOOK,
.exec = cmd_display_panes_exec
@@ -55,6 +55,7 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
return (CMD_RETURN_ERROR);
s = c->session;
if (c->identify_callback != NULL)
return (CMD_RETURN_NORMAL);
@@ -64,7 +65,10 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
c->identify_callback_data = xstrdup(args->argv[0]);
else
c->identify_callback_data = xstrdup("select-pane -t '%%'");
s = c->session;
if (args_has(args, 'b'))
c->identify_callback_item = NULL;
else
c->identify_callback_item = item;
if (args_has(args, 'd')) {
delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause);
@@ -77,7 +81,9 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
delay = options_get_number(s->options, "display-panes-time");
server_client_set_identify(c, delay);
return (CMD_RETURN_NORMAL);
if (args_has(args, 'b'))
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
static enum cmd_retval
@@ -96,34 +102,42 @@ cmd_display_panes_callback(struct client *c, struct window_pane *wp)
{
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
char *template, *cmd, *expanded, *cause;
char *cmd, *expanded, *cause;
template = c->identify_callback_data;
if (wp == NULL)
goto out;
xasprintf(&expanded, "%%%u", wp->id);
cmd = cmd_template_replace(template, expanded, 1);
cmd = cmd_template_replace(c->identify_callback_data, expanded, 1);
cmdlist = cmd_string_parse(cmd, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL) {
new_item = cmdq_get_callback(cmd_display_panes_error,
cause);
} else
new_item = NULL;
} else {
if (cmdlist == NULL && cause != NULL)
new_item = cmdq_get_callback(cmd_display_panes_error, cause);
else if (cmdlist == NULL)
new_item = NULL;
else {
new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
cmd_list_free(cmdlist);
}
if (new_item != NULL)
cmdq_append(c, new_item);
if (new_item != NULL) {
if (c->identify_callback_item != NULL)
cmdq_insert_after(c->identify_callback_item, new_item);
else
cmdq_append(c, new_item);
}
free(cmd);
free(expanded);
out:
if (c->identify_callback_item != NULL) {
c->identify_callback_item->flags &= ~CMDQ_WAITING;
c->identify_callback_item = NULL;
}
free(c->identify_callback_data);
c->identify_callback_data = NULL;
c->identify_callback = NULL;
}

View File

@@ -32,8 +32,8 @@ const struct cmd_entry cmd_find_window_entry = {
.name = "find-window",
.alias = "findw",
.args = { "CNt:T", 1, 1 },
.usage = "[-CNT] " CMD_TARGET_PANE_USAGE " match-string",
.args = { "CNt:TZ", 1, 1 },
.usage = "[-CNTZ] " CMD_TARGET_PANE_USAGE " match-string",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -83,6 +83,8 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s);
new_args = args_parse("", 1, &argv);
if (args_has(args, 'Z'))
args_set(new_args, 'Z', NULL);
args_set(new_args, 'f', filter);
window_pane_set_mode(wp, &window_tree_mode, &item->target, new_args);

View File

@@ -34,6 +34,7 @@ static int cmd_find_best_winlink_with_window(struct cmd_find_state *);
static const char *cmd_find_map_table(const char *[][2], const char *);
static void cmd_find_log_state(const char *, struct cmd_find_state *);
static int cmd_find_get_session(struct cmd_find_state *, const char *);
static int cmd_find_get_window(struct cmd_find_state *, const char *, int);
static int cmd_find_get_window_with_session(struct cmd_find_state *,
@@ -82,6 +83,7 @@ cmd_find_try_TMUX(struct client *c)
char tmp[256];
long long pid;
u_int session;
struct session *s;
envent = environ_find(c->environ, "TMUX");
if (envent == NULL)
@@ -91,8 +93,13 @@ cmd_find_try_TMUX(struct client *c)
return (NULL);
if (pid != getpid())
return (NULL);
log_debug("client %p TMUX %s (session @%u)", c, envent->value, session);
return (session_find_by_id(session));
log_debug("%s: client %p TMUX %s (session $%u)", __func__, c,
envent->value, session);
s = session_find_by_id(session);
if (s != NULL)
log_debug("%s: session $%u still exists", __func__, s->id);
return (s);
}
/* Find pane containing client if any. */
@@ -105,9 +112,11 @@ cmd_find_inside_pane(struct client *c)
return (NULL);
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
if (strcmp(wp->tty, c->ttyname) == 0)
if (wp->fd != -1 && strcmp(wp->tty, c->ttyname) == 0)
break;
}
if (wp != NULL)
log_debug("%s: got pane %%%u (%s)", __func__, wp->id, wp->tty);
return (wp);
}
@@ -121,12 +130,12 @@ cmd_find_client_better(struct client *c, struct client *than)
}
/* Find best client for session. */
static struct client *
struct client *
cmd_find_best_client(struct session *s)
{
struct client *c_loop, *c;
if (s->flags & SESSION_UNATTACHED)
if (s->attached == 0)
s = NULL;
c = NULL;
@@ -150,10 +159,10 @@ cmd_find_session_better(struct session *s, struct session *than, int flags)
if (than == NULL)
return (1);
if (flags & CMD_FIND_PREFER_UNATTACHED) {
attached = (~than->flags & SESSION_UNATTACHED);
if (attached && (s->flags & SESSION_UNATTACHED))
attached = (than->attached != 0);
if (attached && s->attached == 0)
return (1);
else if (!attached && (~s->flags & SESSION_UNATTACHED))
else if (!attached && s->attached != 0)
return (0);
}
return (timercmp(&s->activity_time, &than->activity_time, >));
@@ -166,6 +175,8 @@ cmd_find_best_session(struct session **slist, u_int ssize, int flags)
struct session *s_loop, *s;
u_int i;
log_debug("%s: %u sessions to try", __func__, ssize);
s = NULL;
if (slist != NULL) {
for (i = 0; i < ssize; i++) {
@@ -189,6 +200,8 @@ cmd_find_best_session_with_window(struct cmd_find_state *fs)
u_int ssize;
struct session *s;
log_debug("%s: window is @%u", __func__, fs->w->id);
ssize = 0;
RB_FOREACH(s, sessions, &sessions) {
if (!session_has(s, fs->w))
@@ -210,7 +223,7 @@ fail:
}
/*
* Find the best winlink for a window (the current if it contains the pane,
* Find the best winlink for a window (the current if it contains the window,
* otherwise the first).
*/
static int
@@ -218,6 +231,8 @@ cmd_find_best_winlink_with_window(struct cmd_find_state *fs)
{
struct winlink *wl, *wl_loop;
log_debug("%s: window is @%u", __func__, fs->w->id);
wl = NULL;
if (fs->s->curw != NULL && fs->s->curw->window == fs->w)
wl = fs->s->curw;
@@ -436,15 +451,16 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window)
if (window[0] != '+' && window[0] != '-') {
idx = strtonum(window, 0, INT_MAX, &errstr);
if (errstr == NULL) {
if (fs->flags & CMD_FIND_WINDOW_INDEX) {
fs->idx = idx;
return (0);
}
fs->wl = winlink_find_by_index(&fs->s->windows, idx);
if (fs->wl != NULL) {
fs->idx = fs->wl->idx;
fs->w = fs->wl->window;
return (0);
}
if (fs->flags & CMD_FIND_WINDOW_INDEX) {
fs->idx = idx;
return (0);
}
}
}
@@ -701,11 +717,11 @@ cmd_find_copy_state(struct cmd_find_state *dst, struct cmd_find_state *src)
}
/* Log the result. */
void
static void
cmd_find_log_state(const char *prefix, struct cmd_find_state *fs)
{
if (fs->s != NULL)
log_debug("%s: s=$%u", prefix, fs->s->id);
log_debug("%s: s=$%u %s", prefix, fs->s->id, fs->s->name);
else
log_debug("%s: s=none", prefix);
if (fs->wl != NULL) {
@@ -894,6 +910,9 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
break;
}
if (wl != NULL) {
log_debug("%s: session $%u has pane %%%u", __func__,
s->id, wp->id);
fs->s = s;
fs->wl = s->curw; /* use current session */
fs->w = fs->wl->window;
@@ -901,6 +920,9 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
cmd_find_log_state(__func__, fs);
return (0);
} else {
log_debug("%s: session $%u does not have pane %%%u",
__func__, s->id, wp->id);
}
}
@@ -949,7 +971,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
{
struct mouse_event *m;
struct cmd_find_state current;
char *colon, *period, *copy = NULL;
char *colon, *period, *copy = NULL, tmp[256];
const char *session, *window, *pane, *s;
int window_only = 0, pane_only = 0;
@@ -966,11 +988,25 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
s = "session";
else
s = "unknown";
if (target == NULL)
log_debug("%s: target none, type %s", __func__, s);
else
log_debug("%s: target %s, type %s", __func__, target, s);
log_debug("%s: item %p, flags %#x", __func__, item, flags);
*tmp = '\0';
if (flags & CMD_FIND_PREFER_UNATTACHED)
strlcat(tmp, "PREFER_UNATTACHED,", sizeof tmp);
if (flags & CMD_FIND_QUIET)
strlcat(tmp, "QUIET,", sizeof tmp);
if (flags & CMD_FIND_WINDOW_INDEX)
strlcat(tmp, "WINDOW_INDEX,", sizeof tmp);
if (flags & CMD_FIND_DEFAULT_MARKED)
strlcat(tmp, "DEFAULT_MARKED,", sizeof tmp);
if (flags & CMD_FIND_EXACT_SESSION)
strlcat(tmp, "EXACT_SESSION,", sizeof tmp);
if (flags & CMD_FIND_EXACT_WINDOW)
strlcat(tmp, "EXACT_WINDOW,", sizeof tmp);
if (flags & CMD_FIND_CANFAIL)
strlcat(tmp, "CANFAIL,", sizeof tmp);
if (*tmp != '\0')
tmp[strlen(tmp) - 1] = '\0';
log_debug("%s: target %s, type %s, item %p, flags %s", __func__,
target == NULL ? "none" : target, s, item, tmp);
/* Clear new state. */
cmd_find_clear_state(fs, flags);
@@ -1110,9 +1146,16 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
if (pane != NULL)
pane = cmd_find_map_table(cmd_find_pane_table, pane);
log_debug("%s: target %s (flags %#x): session=%s, window=%s, pane=%s",
__func__, target, flags, session == NULL ? "none" : session,
window == NULL ? "none" : window, pane == NULL ? "none" : pane);
if (session != NULL || window != NULL || pane != NULL) {
log_debug("%s: target %s is %s%s%s%s%s%s",
__func__, target,
session == NULL ? "" : "session ",
session == NULL ? "" : session,
window == NULL ? "" : "window ",
window == NULL ? "" : window,
pane == NULL ? "" : "pane ",
pane == NULL ? "" : pane);
}
/* No pane is allowed if want an index. */
if (pane != NULL && (flags & CMD_FIND_WINDOW_INDEX)) {
@@ -1141,7 +1184,8 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
/* This will fill in winlink and window. */
if (cmd_find_get_window_with_session(fs, window) != 0)
goto no_window;
fs->wp = fs->wl->window->active;
if (fs->wl != NULL) /* can be NULL if index only */
fs->wp = fs->wl->window->active;
goto found;
}
@@ -1181,7 +1225,8 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
/* This will fill in session, winlink and window. */
if (cmd_find_get_window(fs, window, window_only) != 0)
goto no_window;
fs->wp = fs->wl->window->active;
if (fs->wl != NULL) /* can be NULL if index only */
fs->wp = fs->wl->window->active;
goto found;
}
@@ -1218,17 +1263,17 @@ found:
no_session:
if (~flags & CMD_FIND_QUIET)
cmdq_error(item, "can't find session %s", session);
cmdq_error(item, "can't find session: %s", session);
goto error;
no_window:
if (~flags & CMD_FIND_QUIET)
cmdq_error(item, "can't find window %s", window);
cmdq_error(item, "can't find window: %s", window);
goto error;
no_pane:
if (~flags & CMD_FIND_QUIET)
cmdq_error(item, "can't find pane %s", pane);
cmdq_error(item, "can't find pane: %s", pane);
goto error;
}
@@ -1298,7 +1343,7 @@ cmd_find_client(struct cmdq_item *item, const char *target, int quiet)
/* If no client found, report an error. */
if (c == NULL && !quiet)
cmdq_error(item, "can't find client %s", copy);
cmdq_error(item, "can't find client: %s", copy);
free(copy);
log_debug("%s: target %s, return %p", __func__, target, c);

View File

@@ -73,14 +73,6 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
const char *cwd;
if (item->client != NULL && item->client->session == NULL)
cwd = item->client->cwd;
else if (s != NULL)
cwd = s->cwd;
else
cwd = NULL;
shellcmd = format_single(item, args->argv[0], c, s, wl, wp);
if (args_has(args, 'F')) {
@@ -128,8 +120,13 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->item = NULL;
memcpy(&cdata->mouse, &shared->mouse, sizeof cdata->mouse);
job_run(shellcmd, s, cwd, NULL, cmd_if_shell_callback,
cmd_if_shell_free, cdata, 0);
if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL,
cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) {
cmdq_error(item, "failed to run command: %s", shellcmd);
free(shellcmd);
free(cdata);
return (CMD_RETURN_ERROR);
}
free(shellcmd);
if (args_has(args, 'b'))
@@ -140,14 +137,16 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
static void
cmd_if_shell_callback(struct job *job)
{
struct cmd_if_shell_data *cdata = job->data;
struct cmd_if_shell_data *cdata = job_get_data(job);
struct client *c = cdata->client;
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
char *cause, *cmd, *file = cdata->file;
u_int line = cdata->line;
int status;
if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0)
status = job_get_status(job);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
cmd = cdata->cmd_else;
else
cmd = cdata->cmd_if;

View File

@@ -47,9 +47,8 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *wl = item->target.wl;
struct window_pane *loopwp, *tmpwp, *wp = item->target.wp;
server_unzoom_window(wl->window);
if (args_has(self->args, 'a')) {
server_unzoom_window(wl->window);
TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) {
if (loopwp == wp)
continue;
@@ -60,13 +59,6 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (window_count_panes(wl->window) == 1) {
server_kill_window(wl->window);
recalculate_sizes();
} else {
layout_close_pane(wp);
window_remove_pane(wl->window, wp);
server_redraw_window(wl->window);
}
server_kill_pane(wp);
return (CMD_RETURN_NORMAL);
}

View File

@@ -75,10 +75,14 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
repeat = 0;
tablewidth = keywidth = 0;
RB_FOREACH(table, key_tables, &key_tables) {
if (tablename != NULL && strcmp(table->name, tablename) != 0)
table = key_bindings_first_table ();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
table = key_bindings_next_table(table);
continue;
RB_FOREACH(bd, key_bindings, &table->key_bindings) {
}
bd = key_bindings_first(table);
while (bd != NULL) {
key = key_string_lookup_key(bd->key);
if (bd->flags & KEY_BINDING_REPEAT)
@@ -90,13 +94,20 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
width = utf8_cstrwidth(key);
if (width > keywidth)
keywidth = width;
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
RB_FOREACH(table, key_tables, &key_tables) {
if (tablename != NULL && strcmp(table->name, tablename) != 0)
table = key_bindings_first_table ();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
table = key_bindings_next_table(table);
continue;
RB_FOREACH(bd, key_bindings, &table->key_bindings) {
}
bd = key_bindings_first(table);
while (bd != NULL) {
key = key_string_lookup_key(bd->key);
if (!repeat)
@@ -122,7 +133,9 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
free(cp);
cmdq_print(item, "bind-key %s", tmp);
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
return (CMD_RETURN_NORMAL);

View File

@@ -30,8 +30,7 @@
#define LIST_SESSIONS_TEMPLATE \
"#{session_name}: #{session_windows} windows " \
"(created #{t:session_created}) " \
"[#{session_width}x#{session_height}]" \
"(created #{t:session_created})" \
"#{?session_grouped, (group ,}" \
"#{session_group}#{?session_grouped,),}" \
"#{?session_attached, (attached),}"

View File

@@ -56,11 +56,14 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_load_buffer_data *cdata;
struct client *c = item->client;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
FILE *f;
const char *path, *bufname;
const char *bufname;
char *pdata = NULL, *new_pdata, *cause;
char *file;
char *path, *file;
size_t psize;
int ch, error;
@@ -68,8 +71,11 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'b'))
bufname = args_get(args, 'b');
path = args->argv[0];
path = format_single(item, args->argv[0], c, s, wl, wp);
if (strcmp(path, "-") == 0) {
free(path);
c = item->client;
cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;
@@ -79,14 +85,17 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
error = server_set_stdin_callback(c, cmd_load_buffer_callback,
cdata, &cause);
if (error != 0) {
cmdq_error(item, "%s: %s", path, cause);
cmdq_error(item, "-: %s", cause);
free(cause);
free(cdata);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_WAIT);
}
file = server_client_get_path(c, path);
free(path);
f = fopen(file, "rb");
if (f == NULL) {
cmdq_error(item, "%s: %s", file, strerror(errno));

View File

@@ -71,14 +71,15 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
struct session *s, *as, *groupwith;
struct window *w;
struct environ *env;
struct options *oo;
struct termios tio, *tiop;
struct session_group *sg;
const char *errstr, *template, *group, *prefix;
const char *path, *cmd, *tmp;
const char *path, *cmd, *tmp, *value;
char **argv, *cause, *cp, *newname, *cwd = NULL;
int detached, already_attached, idx, argc;
int is_control = 0;
u_int sx, sy;
u_int sx, sy, dsx = 80, dsy = 24;
struct environ_entry *envent;
struct cmd_find_state fs;
enum cmd_retval retval;
@@ -156,10 +157,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
/* Get the new session working directory. */
if ((tmp = args_get(args, 'c')) != NULL)
cwd = format_single(item, tmp, c, NULL, NULL, NULL);
else if (c != NULL && c->session == NULL && c->cwd != NULL)
cwd = xstrdup(c->cwd);
else
cwd = xstrdup(".");
cwd = xstrdup(server_client_get_cwd(c, NULL));
/*
* If this is a new client, check for nesting and save the termios
@@ -191,31 +190,50 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
}
}
/* Get default session size. */
if (args_has(args, 'x')) {
tmp = args_get(args, 'x');
if (strcmp(tmp, "-") == 0) {
if (c != NULL)
dsx = c->tty.sx;
} else {
dsx = strtonum(tmp, 1, USHRT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "width %s", errstr);
goto error;
}
}
}
if (args_has(args, 'y')) {
tmp = args_get(args, 'y');
if (strcmp(tmp, "-") == 0) {
if (c != NULL)
dsy = c->tty.sy;
} else {
dsy = strtonum(tmp, 1, USHRT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "height %s", errstr);
goto error;
}
}
}
/* Find new session size. */
if (!detached) {
if (!detached && !is_control) {
sx = c->tty.sx;
sy = c->tty.sy;
if (!is_control &&
sy > 0 &&
options_get_number(global_s_options, "status"))
if (sy > 0 && options_get_number(global_s_options, "status"))
sy--;
} else {
sx = 80;
sy = 24;
}
if ((is_control || detached) && args_has(args, 'x')) {
sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "width %s", errstr);
goto error;
}
}
if ((is_control || detached) && args_has(args, 'y')) {
sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "height %s", errstr);
goto error;
value = options_get_string(global_s_options, "default-size");
if (sscanf(value, "%ux%u", &sx, &sy) != 2) {
sx = 80;
sy = 24;
}
if (args_has(args, 'x'))
sx = dsx;
if (args_has(args, 'y'))
sy = dsy;
}
if (sx == 0)
sx = 1;
@@ -252,10 +270,15 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (c != NULL && !args_has(args, 'E'))
environ_update(global_s_options, c->environ, env);
/* Set up the options. */
oo = options_create(global_s_options);
if (args_has(args, 'x') || args_has(args, 'y'))
options_set_string(oo, "default-size", 0, "%ux%u", dsx, dsy);
/* Create the new session. */
idx = -1 - options_get_number(global_s_options, "base-index");
s = session_create(prefix, newname, argc, argv, path, cwd, env, tiop,
idx, sx, sy, &cause);
s = session_create(prefix, newname, argc, argv, path, cwd, env, oo,
tiop, idx, &cause);
environ_free(env);
if (s == NULL) {
cmdq_error(item, "create session failed: %s", cause);
@@ -303,6 +326,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
c->session = s;
if (~item->shared->flags & CMDQ_SHARED_REPEAT)
server_client_set_key_table(c, NULL);
tty_update_client_offset(c);
status_timer_start(c);
notify_client("client-session-changed", c);
session_update_activity(s, NULL);

View File

@@ -63,7 +63,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
struct environ_entry *envent;
struct cmd_find_state fs;
if (args_has(args, 'a')) {
if (args_has(args, 'a') && wl != NULL) {
if ((idx = winlink_shuffle_up(s, wl)) == -1) {
cmdq_error(item, "no free window indexes");
return (CMD_RETURN_ERROR);
@@ -95,17 +95,14 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
if ((tmp = args_get(args, 'c')) != NULL)
cwd = format_single(item, tmp, c, s, NULL, NULL);
else if (item->client != NULL && item->client->session == NULL)
cwd = xstrdup(item->client->cwd);
else
cwd = xstrdup(s->cwd);
cwd = xstrdup(server_client_get_cwd(item->client, s));
if ((tmp = args_get(args, 'n')) != NULL)
name = format_single(item, tmp, c, s, NULL, NULL);
else
name = NULL;
wl = NULL;
if (idx != -1)
wl = winlink_find_by_index(&s->windows, idx);
if (wl != NULL && args_has(args, 'k')) {

View File

@@ -109,7 +109,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
/* Expand the command. */
ft = format_create(item->client, item, FORMAT_NONE, 0);
format_defaults(ft, c, s, wl, wp);
cmd = format_expand_time(ft, args->argv[0], time(NULL));
cmd = format_expand_time(ft, args->argv[0]);
format_free(ft);
/* Fork the child. */
@@ -157,7 +157,10 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
close(pipe_fd[1]);
wp->pipe_fd = pipe_fd[0];
wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
if (wp->fd != -1)
wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
else
wp->pipe_off = 0;
setblocking(wp->pipe_fd, 0);
wp->pipe_event = bufferevent_new(wp->pipe_fd,
@@ -165,6 +168,8 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
cmd_pipe_pane_write_callback,
cmd_pipe_pane_error_callback,
wp);
if (wp->pipe_event == NULL)
fatalx("out of memory");
if (out)
bufferevent_enable(wp->pipe_event, EV_WRITE);
if (in)

View File

@@ -404,10 +404,11 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
void
cmdq_print(struct cmdq_item *item, const char *fmt, ...)
{
struct client *c = item->client;
struct window *w;
va_list ap;
char *tmp, *msg;
struct client *c = item->client;
struct window_pane *wp;
struct window_mode_entry *wme;
va_list ap;
char *tmp, *msg;
va_start(ap, fmt);
@@ -425,14 +426,11 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
evbuffer_add(c->stdout_data, "\n", 1);
server_client_push_stdout(c);
} else {
w = c->session->curw->window;
if (w->active->mode != &window_copy_mode) {
window_pane_reset_mode(w->active);
window_pane_set_mode(w->active, &window_copy_mode, NULL,
NULL);
window_copy_init_for_output(w->active);
}
window_copy_vadd(w->active, fmt, ap);
wp = c->session->curw->window->active;
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
window_copy_vadd(wp, fmt, ap);
}
va_end(ap);

View File

@@ -18,6 +18,8 @@
#include <sys/types.h>
#include <stdlib.h>
#include "tmux.h"
/*
@@ -31,8 +33,8 @@ const struct cmd_entry cmd_refresh_client_entry = {
.name = "refresh-client",
.alias = "refresh",
.args = { "C:St:", 0, 0 },
.usage = "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE,
.args = { "cC:DlLRSt:U", 0, 1 },
.usage = "[-cDlLRSU] [-C size] " CMD_TARGET_CLIENT_USAGE " [adjustment]",
.flags = CMD_AFTERHOOK,
.exec = cmd_refresh_client_exec
@@ -43,23 +45,80 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
const char *size;
u_int w, h;
struct tty *tty;
struct window *w;
const char *size, *errstr;
u_int x, y, adjust;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
return (CMD_RETURN_ERROR);
tty = &c->tty;
if (args_has(args, 'C')) {
if (args_has(args, 'c') ||
args_has(args, 'L') ||
args_has(args, 'R') ||
args_has(args, 'U') ||
args_has(args, 'D'))
{
if (args->argc == 0)
adjust = 1;
else {
adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "adjustment %s", errstr);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'c'))
c->pan_window = NULL;
else {
w = c->session->curw->window;
if (c->pan_window != w) {
c->pan_window = w;
c->pan_ox = tty->oox;
c->pan_oy = tty->ooy;
}
if (args_has(args, 'L')) {
if (c->pan_ox > adjust)
c->pan_ox -= adjust;
else
c->pan_ox = 0;
} else if (args_has(args, 'R')) {
c->pan_ox += adjust;
if (c->pan_ox > w->sx - tty->osx)
c->pan_ox = w->sx - tty->osx;
} else if (args_has(args, 'U')) {
if (c->pan_oy > adjust)
c->pan_oy -= adjust;
else
c->pan_oy = 0;
} else if (args_has(args, 'D')) {
c->pan_oy += adjust;
if (c->pan_oy > w->sy - tty->osy)
c->pan_oy = w->sy - tty->osy;
}
}
tty_update_client_offset(c);
server_redraw_client(c);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'l')) {
if (c->session != NULL)
tty_putcode_ptr2(&c->tty, TTYC_MS, "", "?");
} else if (args_has(args, 'C')) {
if ((size = args_get(args, 'C')) == NULL) {
cmdq_error(item, "missing size");
return (CMD_RETURN_ERROR);
}
if (sscanf(size, "%u,%u", &w, &h) != 2) {
if (sscanf(size, "%u,%u", &x, &y) != 2 &&
sscanf(size, "%ux%u", &x, &y)) {
cmdq_error(item, "bad size argument");
return (CMD_RETURN_ERROR);
}
if (w < PANE_MINIMUM || w > 5000 ||
h < PANE_MINIMUM || h > 5000) {
if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM ||
y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) {
cmdq_error(item, "size too small or too big");
return (CMD_RETURN_ERROR);
}
@@ -67,16 +126,18 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR);
}
tty_set_size(&c->tty, w, h);
tty_set_size(&c->tty, x, y);
c->flags |= CLIENT_SIZECHANGED;
recalculate_sizes();
} else if (args_has(args, 'S')) {
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'S')) {
c->flags |= CLIENT_STATUSFORCE;
server_status_client(c);
} else {
c->flags |= CLIENT_STATUSFORCE;
server_redraw_client(c);
}
return (CMD_RETURN_NORMAL);
}

View File

@@ -47,7 +47,7 @@ static enum cmd_retval
cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 0);
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
char *newname;

View File

@@ -91,9 +91,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
}
}
if (args_has(self->args, 'x')) {
x = args_strtonum(self->args, 'x', PANE_MINIMUM, INT_MAX,
&cause);
if (args_has(args, 'x')) {
x = args_strtonum(args, 'x', PANE_MINIMUM, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
free(cause);
@@ -101,9 +100,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
}
layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x);
}
if (args_has(self->args, 'y')) {
y = args_strtonum(self->args, 'y', PANE_MINIMUM, INT_MAX,
&cause);
if (args_has(args, 'y')) {
y = args_strtonum(args, 'y', PANE_MINIMUM, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
free(cause);
@@ -112,13 +110,13 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y);
}
if (args_has(self->args, 'L'))
if (args_has(args, 'L'))
layout_resize_pane(wp, LAYOUT_LEFTRIGHT, -adjust, 1);
else if (args_has(self->args, 'R'))
else if (args_has(args, 'R'))
layout_resize_pane(wp, LAYOUT_LEFTRIGHT, adjust, 1);
else if (args_has(self->args, 'U'))
else if (args_has(args, 'U'))
layout_resize_pane(wp, LAYOUT_TOPBOTTOM, -adjust, 1);
else if (args_has(self->args, 'D'))
else if (args_has(args, 'D'))
layout_resize_pane(wp, LAYOUT_TOPBOTTOM, adjust, 1);
server_redraw_window(wl->window);
@@ -129,14 +127,21 @@ static void
cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m)
{
struct winlink *wl;
struct window_pane *loop, *wp_x, *wp_y;
u_int y, ly, x, lx, sx, sy, ex, ey;
struct window *w;
u_int y, ly, x, lx;
static const int offsets[][2] = {
{ 0, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 },
};
struct layout_cell *cells[nitems(offsets)], *lc;
u_int ncells = 0, i, j, resizes = 0;
enum layout_type type;
wl = cmd_mouse_window(m, NULL);
if (wl == NULL) {
c->tty.mouse_drag_update = NULL;
return;
}
w = wl->window;
y = m->y; x = m->x;
if (m->statusat == 0 && y > 0)
@@ -149,37 +154,37 @@ cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m)
else if (m->statusat > 0 && ly >= (u_int)m->statusat)
ly = m->statusat - 1;
wp_x = wp_y = NULL;
TAILQ_FOREACH(loop, &wl->window->panes, entry) {
if (!window_pane_visible(loop))
for (i = 0; i < nitems(cells); i++) {
lc = layout_search_by_border(w->layout_root, lx + offsets[i][0],
ly + offsets[i][1]);
if (lc == NULL)
continue;
sx = loop->xoff;
if (sx != 0)
sx--;
ex = loop->xoff + loop->sx;
for (j = 0; j < ncells; j++) {
if (cells[j] == lc) {
lc = NULL;
break;
}
}
if (lc == NULL)
continue;
sy = loop->yoff;
if (sy != 0)
sy--;
ey = loop->yoff + loop->sy;
if ((lx == sx || lx == ex) &&
(ly >= sy && ly <= ey) &&
(wp_x == NULL || loop->sy > wp_x->sy))
wp_x = loop;
if ((ly == sy || ly == ey) &&
(lx >= sx && lx <= ex) &&
(wp_y == NULL || loop->sx > wp_y->sx))
wp_y = loop;
cells[ncells] = lc;
ncells++;
}
if (wp_x == NULL && wp_y == NULL) {
c->tty.mouse_drag_update = NULL;
if (ncells == 0)
return;
for (i = 0; i < ncells; i++) {
type = cells[i]->parent->type;
if (y != ly && type == LAYOUT_TOPBOTTOM) {
layout_resize_layout(w, cells[i], type, y - ly, 0);
resizes++;
} else if (x != lx && type == LAYOUT_LEFTRIGHT) {
layout_resize_layout(w, cells[i], type, x - lx, 0);
resizes++;
}
}
if (wp_x != NULL)
layout_resize_pane(wp_x, LAYOUT_LEFTRIGHT, x - lx, 0);
if (wp_y != NULL)
layout_resize_pane(wp_y, LAYOUT_TOPBOTTOM, y - ly, 0);
server_redraw_window(wl->window);
if (resizes != 0)
server_redraw_window(w);
}

109
cmd-resize-window.c Normal file
View File

@@ -0,0 +1,109 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2018 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 "tmux.h"
/*
* Increase or decrease window size.
*/
static enum cmd_retval cmd_resize_window_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_resize_window_entry = {
.name = "resize-window",
.alias = "resizew",
.args = { "aADLRt:Ux:y:", 0, 1 },
.usage = "[-aADLRU] [-x width] [-y height] " CMD_TARGET_WINDOW_USAGE " "
"[adjustment]",
.target = { 't', CMD_FIND_WINDOW, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_resize_window_exec
};
static enum cmd_retval
cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct winlink *wl = item->target.wl;
struct window *w = wl->window;
struct session *s = item->target.s;
const char *errstr;
char *cause;
u_int adjust, sx, sy;
if (args->argc == 0)
adjust = 1;
else {
adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "adjustment %s", errstr);
return (CMD_RETURN_ERROR);
}
}
sx = w->sx;
sy = w->sy;
if (args_has(args, 'x')) {
sx = args_strtonum(args, 'x', WINDOW_MINIMUM, WINDOW_MAXIMUM,
&cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'y')) {
sy = args_strtonum(args, 'y', WINDOW_MINIMUM, WINDOW_MAXIMUM,
&cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'L')) {
if (sx >= adjust)
sx -= adjust;
} else if (args_has(args, 'R'))
sx += adjust;
else if (args_has(args, 'U')) {
if (sy >= adjust)
sy -= adjust;
} else if (args_has(args, 'D'))
sy += adjust;
if (args_has(args, 'A'))
default_window_size(s, w, &sx, &sy, WINDOW_SIZE_LARGEST);
else if (args_has(args, 'a'))
default_window_size(s, w, &sx, &sy, WINDOW_SIZE_SMALLEST);
options_set_number(w->options, "window-size", WINDOW_SIZE_MANUAL);
resize_window(w, sx, sy);
return (CMD_RETURN_NORMAL);
}

View File

@@ -67,7 +67,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
window_pane_reset_mode(wp);
window_pane_reset_mode_all(wp);
screen_reinit(&wp->base);
input_init(wp);

View File

@@ -99,7 +99,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
free(cwd);
layout_init(w, wp);
window_pane_reset_mode(wp);
window_pane_reset_mode_all(wp);
screen_reinit(&wp->base);
input_init(wp);
window_set_active_pane(w, wp);

View File

@@ -57,9 +57,10 @@ struct cmd_run_shell_data {
static void
cmd_run_shell_print(struct job *job, const char *msg)
{
struct cmd_run_shell_data *cdata = job->data;
struct cmd_run_shell_data *cdata = job_get_data(job);
struct window_pane *wp = NULL;
struct cmd_find_state fs;
struct window_mode_entry *wme;
if (cdata->wp_id != -1)
wp = window_pane_find_by_id(cdata->wp_id);
@@ -75,10 +76,10 @@ cmd_run_shell_print(struct job *job, const char *msg)
return;
}
if (window_pane_set_mode(wp, &window_copy_mode, NULL, NULL) == 0)
window_copy_init_for_output(wp);
if (wp->mode == &window_copy_mode)
window_copy_add(wp, "%s", msg);
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
window_copy_add(wp, "%s", msg);
}
static enum cmd_retval
@@ -90,14 +91,6 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
const char *cwd;
if (item->client != NULL && item->client->session == NULL)
cwd = item->client->cwd;
else if (s != NULL)
cwd = s->cwd;
else
cwd = NULL;
cdata = xcalloc(1, sizeof *cdata);
cdata->cmd = format_single(item, args->argv[0], c, s, wl, wp);
@@ -110,8 +103,12 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
if (!args_has(args, 'b'))
cdata->item = item;
job_run(cdata->cmd, s, cwd, NULL, cmd_run_shell_callback,
cmd_run_shell_free, cdata, 0);
if (job_run(cdata->cmd, s, server_client_get_cwd(item->client, s), NULL,
cmd_run_shell_callback, cmd_run_shell_free, cdata, 0) == NULL) {
cmdq_error(item, "failed to run command: %s", cdata->cmd);
free(cdata);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'b'))
return (CMD_RETURN_NORMAL);
@@ -121,22 +118,23 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
static void
cmd_run_shell_callback(struct job *job)
{
struct cmd_run_shell_data *cdata = job->data;
char *cmd = cdata->cmd, *msg, *line;
struct cmd_run_shell_data *cdata = job_get_data(job);
struct bufferevent *event = job_get_event(job);
char *cmd = cdata->cmd, *msg = NULL, *line;
size_t size;
int retcode;
int retcode, status;
do {
if ((line = evbuffer_readline(job->event->input)) != NULL) {
if ((line = evbuffer_readline(event->input)) != NULL) {
cmd_run_shell_print(job, line);
free(line);
}
} while (line != NULL);
size = EVBUFFER_LENGTH(job->event->input);
size = EVBUFFER_LENGTH(event->input);
if (size != 0) {
line = xmalloc(size + 1);
memcpy(line, EVBUFFER_DATA(job->event->input), size);
memcpy(line, EVBUFFER_DATA(event->input), size);
line[size] = '\0';
cmd_run_shell_print(job, line);
@@ -144,12 +142,12 @@ cmd_run_shell_callback(struct job *job)
free(line);
}
msg = NULL;
if (WIFEXITED(job->status)) {
if ((retcode = WEXITSTATUS(job->status)) != 0)
status = job_get_status(job);
if (WIFEXITED(status)) {
if ((retcode = WEXITSTATUS(status)) != 0)
xasprintf(&msg, "'%s' returned %d", cmd, retcode);
} else if (WIFSIGNALED(job->status)) {
retcode = WTERMSIG(job->status);
} else if (WIFSIGNALED(status)) {
retcode = WTERMSIG(status);
xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode);
}
if (msg != NULL)

View File

@@ -59,11 +59,13 @@ static enum cmd_retval
cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = item->client;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
struct paste_buffer *pb;
const char *path, *bufname, *bufdata, *start, *end;
const char *flags;
char *msg, *file;
const char *bufname, *bufdata, *start, *end, *flags;
char *msg, *path, *file;
size_t size, used, msglen, bufsize;
FILE *f;
@@ -83,10 +85,12 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
bufdata = paste_buffer_data(pb, &bufsize);
if (self->entry == &cmd_show_buffer_entry)
path = "-";
path = xstrdup("-");
else
path = args->argv[0];
path = format_single(item, args->argv[0], c, s, wl, wp);
if (strcmp(path, "-") == 0) {
free(path);
c = item->client;
if (c == NULL) {
cmdq_error(item, "can't write to stdout");
return (CMD_RETURN_ERROR);
@@ -101,6 +105,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
flags = "ab";
file = server_client_get_path(c, path);
free(path);
f = fopen(file, flags);
if (f == NULL) {
cmdq_error(item, "%s: %s", file, strerror(errno));

View File

@@ -135,7 +135,9 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
changed:
free(oldlayout);
recalculate_sizes();
server_redraw_window(w);
notify_window("window-layout-changed", w);
return (CMD_RETURN_NORMAL);
error:

View File

@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
@@ -54,6 +55,31 @@ const struct cmd_entry cmd_last_pane_entry = {
.exec = cmd_select_pane_exec
};
static void
cmd_select_pane_redraw(struct window *w)
{
struct client *c;
/*
* Redraw entire window if it is bigger than the client (the
* offset may change), otherwise just draw borders.
*/
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL)
continue;
if (c->session->curw->window == w && tty_window_bigger(&c->tty))
server_redraw_client(c);
else {
if (c->session->curw->window == w)
c->flags |= CLIENT_REDRAWBORDERS;
if (session_has(c->session, w))
c->flags |= CLIENT_REDRAWSTATUS;
}
}
}
static enum cmd_retval
cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -64,11 +90,17 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window *w = wl->window;
struct session *s = item->target.s;
struct window_pane *wp = item->target.wp, *lastwp, *markedwp;
struct style *sy = &wp->style;
char *pane_title;
const char *style;
if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) {
lastwp = w->last;
if (lastwp == NULL && window_count_panes(w) == 2) {
lastwp = TAILQ_PREV(w->active, window_panes, entry);
if (lastwp == NULL)
lastwp = TAILQ_NEXT(w->active, entry);
}
if (lastwp == NULL) {
cmdq_error(item, "no last pane");
return (CMD_RETURN_ERROR);
@@ -82,8 +114,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
window_redraw_active_switch(w, lastwp);
if (window_set_active_pane(w, lastwp)) {
cmd_find_from_winlink(current, wl, 0);
server_status_window(w);
server_redraw_window_borders(w);
cmd_select_pane_redraw(w);
}
}
return (CMD_RETURN_NORMAL);
@@ -112,17 +143,16 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
}
if (args_has(self->args, 'P') || args_has(self->args, 'g')) {
if (args_has(args, 'P')) {
style = args_get(args, 'P');
if (style_parse(&grid_default_cell, &wp->colgc,
style) == -1) {
if ((style = args_get(args, 'P')) != NULL) {
style_set(sy, &grid_default_cell);
if (style_parse(sy, &grid_default_cell, style) == -1) {
cmdq_error(item, "bad style: %s", style);
return (CMD_RETURN_ERROR);
}
wp->flags |= PANE_REDRAW;
}
if (args_has(self->args, 'g'))
cmdq_print(item, "%s", style_tostring(&wp->colgc));
cmdq_print(item, "%s", style_tostring(sy));
return (CMD_RETURN_NORMAL);
}
@@ -157,21 +187,17 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
screen_set_title(&wp->base, pane_title);
server_status_window(wp->window);
free(pane_title);
return (CMD_RETURN_NORMAL);
}
if (wp == w->active)
return (CMD_RETURN_NORMAL);
server_unzoom_window(wp->window);
if (!window_pane_visible(wp)) {
cmdq_error(item, "pane not visible");
return (CMD_RETURN_ERROR);
}
window_redraw_active_switch(w, wp);
if (window_set_active_pane(w, wp)) {
cmd_find_from_winlink_pane(current, wl, wp, 0);
hooks_insert(s->hooks, item, current, "after-select-pane");
server_status_window(w);
server_redraw_window_borders(w);
cmd_select_pane_redraw(w);
}
return (CMD_RETURN_NORMAL);

View File

@@ -58,21 +58,23 @@ const struct cmd_entry cmd_send_prefix_entry = {
static void
cmd_send_keys_inject(struct client *c, struct cmdq_item *item, key_code key)
{
struct window_pane *wp = item->target.wp;
struct session *s = item->target.s;
struct key_table *table;
struct key_binding *bd, bd_find;
struct window_pane *wp = item->target.wp;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_mode_entry *wme;
struct key_table *table;
struct key_binding *bd;
if (wp->mode == NULL || wp->mode->key_table == NULL) {
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode->key_table == NULL) {
if (options_get_number(wp->window->options, "xterm-keys"))
key |= KEYC_XTERM;
window_pane_key(wp, NULL, s, key, NULL);
window_pane_key(wp, NULL, s, wl, key, NULL);
return;
}
table = key_bindings_get_table(wp->mode->key_table(wp), 1);
table = key_bindings_get_table(wme->mode->key_table(wme), 1);
bd_find.key = (key & ~KEYC_XTERM);
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
bd = key_bindings_get(table, key & ~KEYC_XTERM);
if (bd != NULL) {
table->references++;
key_bindings_dispatch(bd, item, c, NULL, &item->target);
@@ -83,17 +85,19 @@ cmd_send_keys_inject(struct client *c, struct cmdq_item *item, key_code key)
static enum cmd_retval
cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct window_pane *wp = item->target.wp;
struct session *s = item->target.s;
struct mouse_event *m = &item->shared->mouse;
struct utf8_data *ud, *uc;
wchar_t wc;
int i, literal;
key_code key;
u_int np = 1;
char *cause = NULL;
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct window_pane *wp = item->target.wp;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct mouse_event *m = &item->shared->mouse;
struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
struct utf8_data *ud, *uc;
wchar_t wc;
int i, literal;
key_code key;
u_int np = 1;
char *cause = NULL;
if (args_has(args, 'N')) {
np = args_strtonum(args, 'N', 1, UINT_MAX, &cause);
@@ -102,19 +106,23 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'X') || args->argc == 0)
wp->modeprefix = np;
if (wme != NULL && (args_has(args, 'X') || args->argc == 0)) {
if (wme == NULL || wme->mode->command == NULL) {
cmdq_error(item, "not in a mode");
return (CMD_RETURN_ERROR);
}
wme->prefix = np;
}
}
if (args_has(args, 'X')) {
if (wp->mode == NULL || wp->mode->command == NULL) {
if (wme == NULL || wme->mode->command == NULL) {
cmdq_error(item, "not in a mode");
return (CMD_RETURN_ERROR);
}
if (!m->valid)
wp->mode->command(wp, c, s, args, NULL);
else
wp->mode->command(wp, c, s, args, m);
m = NULL;
wme->mode->command(wme, c, s, wl, args, m);
return (CMD_RETURN_NORMAL);
}
@@ -124,7 +132,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "no mouse target");
return (CMD_RETURN_ERROR);
}
window_pane_key(wp, NULL, s, m->key, m);
window_pane_key(wp, NULL, s, wl, m->key, m);
return (CMD_RETURN_NORMAL);
}

View File

@@ -33,8 +33,8 @@ const struct cmd_entry cmd_set_hook_entry = {
.name = "set-hook",
.alias = NULL,
.args = { "gt:u", 1, 2 },
.usage = "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]",
.args = { "gRt:u", 1, 2 },
.usage = "[-gRu] " CMD_TARGET_SESSION_USAGE " hook-name [command]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
@@ -101,18 +101,21 @@ cmd_set_hook_exec(struct cmd *self, struct cmdq_item *item)
else
cmd = args->argv[1];
if (cmd != NULL && (args_has(args, 'R') || args_has(args, 'u'))) {
cmdq_error(item, "no command allowed");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'R')) {
notify_hook(item, name);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'u')) {
if (cmd != NULL) {
cmdq_error(item, "command passed to unset hook: %s",
name);
return (CMD_RETURN_ERROR);
}
hooks_remove(hooks, name);
return (CMD_RETURN_NORMAL);
}
if (cmd == NULL) {
cmdq_error(item, "no command to set hook: %s", name);
cmdq_error(item, "no command given");
return (CMD_RETURN_ERROR);
}
cmdlist = cmd_string_parse(cmd, NULL, 0, &cause);

View File

@@ -18,6 +18,7 @@
#include <sys/types.h>
#include <fnmatch.h>
#include <stdlib.h>
#include <string.h>
@@ -80,6 +81,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
char *name, *argument, *value = NULL, *cause;
const char *target;
int window, idx, already, error, ambiguous;
struct style *sy;
/* Expand argument. */
c = cmd_find_client(item, NULL, 1);
@@ -162,11 +164,9 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
parent = options_get(oo, name);
/* Check that array options and indexes match up. */
if (idx != -1) {
if (*name == '@' || options_array_size(parent, NULL) == -1) {
cmdq_error(item, "not an array: %s", argument);
goto fail;
}
if (idx != -1 && (*name == '@' || !options_isarray(parent))) {
cmdq_error(item, "not an array: %s", argument);
goto fail;
}
/* With -o, check this option is not already set. */
@@ -208,7 +208,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
goto fail;
}
options_set_string(oo, name, append, "%s", value);
} else if (idx == -1 && options_array_size(parent, NULL) == -1) {
} else if (idx == -1 && !options_isarray(parent)) {
error = cmd_set_option_set(self, item, oo, parent, value);
if (error != 0)
goto fail;
@@ -248,6 +248,16 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
tty_keys_build(&loop->tty);
}
}
if (strcmp(name, "status-fg") == 0 || strcmp(name, "status-bg") == 0) {
sy = options_get_style(oo, "status-style");
sy->gc.fg = options_get_number(oo, "status-fg");
sy->gc.bg = options_get_number(oo, "status-bg");
}
if (strcmp(name, "status-style") == 0) {
sy = options_get_style(oo, "status-style");
options_set_number(oo, "status-fg", sy->gc.fg);
options_set_number(oo, "status-bg", sy->gc.bg);
}
if (strcmp(name, "status") == 0 ||
strcmp(name, "status-interval") == 0)
status_timer_start_all();
@@ -260,10 +270,10 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
}
if (strcmp(name, "pane-border-status") == 0) {
RB_FOREACH(w, windows, &windows)
layout_fix_panes(w, w->sx, w->sy);
layout_fix_panes(w);
}
RB_FOREACH(s, sessions, &sessions)
status_update_saved(s);
status_update_cache(s);
/*
* Update sizes and redraw. May not always be necessary but do it
@@ -297,7 +307,8 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo,
int append = args_has(args, 'a');
struct options_entry *o;
long long number;
const char *errstr;
const char *errstr, *new;
char *old;
key_code key;
oe = options_table_entry(parent);
@@ -310,7 +321,16 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo,
switch (oe->type) {
case OPTIONS_TABLE_STRING:
old = xstrdup(options_get_string(oo, oe->name));
options_set_string(oo, oe->name, append, "%s", value);
new = options_get_string(oo, oe->name);
if (oe->pattern != NULL && fnmatch(oe->pattern, new, 0) != 0) {
options_set_string(oo, oe->name, 0, "%s", old);
free(old);
cmdq_error(item, "value is invalid: %s", value);
return (-1);
}
free(old);
return (0);
case OPTIONS_TABLE_NUMBER:
number = strtonum(value, oe->minimum, oe->maximum, &errstr);
@@ -333,16 +353,7 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo,
cmdq_error(item, "bad colour: %s", value);
return (-1);
}
o = options_set_number(oo, oe->name, number);
options_style_update_new(oo, o);
return (0);
case OPTIONS_TABLE_ATTRIBUTES:
if ((number = attributes_fromstring(value)) == -1) {
cmdq_error(item, "bad attributes: %s", value);
return (-1);
}
o = options_set_number(oo, oe->name, number);
options_style_update_new(oo, o);
options_set_number(oo, oe->name, number);
return (0);
case OPTIONS_TABLE_FLAG:
return (cmd_set_option_flag(item, oe, oo, value));
@@ -354,7 +365,6 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo,
cmdq_error(item, "bad style: %s", value);
return (-1);
}
options_style_update_old(oo, o);
return (0);
case OPTIONS_TABLE_ARRAY:
break;

View File

@@ -43,7 +43,6 @@ const struct cmd_entry cmd_show_messages_entry = {
};
static int cmd_show_messages_terminals(struct cmdq_item *, int);
static int cmd_show_messages_jobs(struct cmdq_item *, int);
static int
cmd_show_messages_terminals(struct cmdq_item *item, int blank)
@@ -66,25 +65,6 @@ cmd_show_messages_terminals(struct cmdq_item *item, int blank)
return (n != 0);
}
static int
cmd_show_messages_jobs(struct cmdq_item *item, int blank)
{
struct job *job;
u_int n;
n = 0;
LIST_FOREACH(job, &all_jobs, entry) {
if (blank) {
cmdq_print(item, "%s", "");
blank = 0;
}
cmdq_print(item, "Job %u: %s [fd=%d, pid=%ld, status=%d]",
n, job->cmd, job->fd, (long)job->pid, job->status);
n++;
}
return (n != 0);
}
static enum cmd_retval
cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -103,7 +83,7 @@ cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item)
done = 1;
}
if (args_has(args, 'J')) {
cmd_show_messages_jobs(item, blank);
job_print_summary(item, blank);
done = 1;
}
if (done)

View File

@@ -88,20 +88,20 @@ static void
cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
struct options_entry *o, int idx)
{
const char *name;
const char *value;
char *tmp, *escaped;
u_int size, i;
struct options_array_item *a;
const char *name, *value;
char *tmp, *escaped;
if (idx != -1) {
xasprintf(&tmp, "%s[%d]", options_name(o), idx);
name = tmp;
} else {
if (options_array_size(o, &size) != -1) {
for (i = 0; i < size; i++) {
if (options_array_get(o, i) == NULL)
continue;
cmd_show_options_print(self, item, o, i);
if (options_isarray(o)) {
a = options_array_first(o);
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx);
a = options_array_next(a);
}
return;
}
@@ -164,24 +164,22 @@ static enum cmd_retval
cmd_show_options_all(struct cmd *self, struct cmdq_item *item,
struct options *oo)
{
struct options_entry *o;
struct options_entry *o;
const struct options_table_entry *oe;
u_int size, idx;
struct options_array_item *a;
u_int idx;
o = options_first(oo);
while (o != NULL) {
oe = options_table_entry(o);
if (oe != NULL && oe->style != NULL) {
o = options_next(o);
continue;
}
if (options_array_size(o, &size) == -1)
if (!options_isarray(o))
cmd_show_options_print(self, item, o, -1);
else {
for (idx = 0; idx < size; idx++) {
if (options_array_get(o, idx) == NULL)
continue;
a = options_array_first(o);
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx);
a = options_array_next(a);
}
}
o = options_next(o);

View File

@@ -60,7 +60,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
if (*path == '/')
pattern = xstrdup(path);
else {
utf8_stravis(&tmp, server_client_get_cwd(c), VIS_GLOB);
utf8_stravis(&tmp, server_client_get_cwd(c, NULL), VIS_GLOB);
xasprintf(&pattern, "%s/%s", tmp, path);
free(tmp);
}

View File

@@ -87,10 +87,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if ((tmp = args_get(args, 'c')) != NULL)
cwd = format_single(item, tmp, c, s, NULL, NULL);
else if (item->client != NULL && item->client->session == NULL)
cwd = xstrdup(item->client->cwd);
else
cwd = xstrdup(s->cwd);
cwd = xstrdup(server_client_get_cwd(item->client, s));
type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'h'))
@@ -149,7 +147,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
}
environ_free(env);
layout_fix_panes(w, w->sx, w->sy);
layout_fix_panes(w);
server_redraw_window(w);
if (!args_has(args, 'd')) {

View File

@@ -54,6 +54,22 @@ cmd_string_ungetc(size_t *p)
(*p)--;
}
static int
cmd_string_unicode(wchar_t *wc, const char *s, size_t *p, char ch)
{
int size = (ch == 'u') ? 4 : 8;
u_int tmp;
if (size == 4 && sscanf(s + *p, "%4x", &tmp) != 1)
return (-1);
if (size == 8 && sscanf(s + *p, "%8x", &tmp) != 1)
return (-1);
*p += size;
*wc = (wchar_t)tmp;
return (0);
}
int
cmd_string_split(const char *s, int *rargc, char ***rargv)
{
@@ -191,12 +207,11 @@ cmd_string_copy(char **dst, char *src, size_t *len)
static char *
cmd_string_string(const char *s, size_t *p, char endch, int esc)
{
int ch;
char *buf, *t;
size_t len;
buf = NULL;
len = 0;
int ch;
wchar_t wc;
struct utf8_data ud;
char *buf = NULL, *t;
size_t len = 0;
while ((ch = cmd_string_getc(s, p)) != endch) {
switch (ch) {
@@ -220,6 +235,18 @@ cmd_string_string(const char *s, size_t *p, char endch, int esc)
case 't':
ch = '\t';
break;
case 'u':
case 'U':
if (cmd_string_unicode(&wc, s, p, ch) != 0)
goto error;
if (utf8_split(wc, &ud) != UTF8_DONE)
goto error;
if (len >= SIZE_MAX - ud.size - 1)
goto error;
buf = xrealloc(buf, len + ud.size);
memcpy(buf + len, ud.data, ud.size);
len += ud.size;
continue;
}
break;
case '$':

View File

@@ -105,8 +105,6 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
window_set_active_pane(dst_w, src_wp);
} else {
tmp_wp = dst_wp;
if (!window_pane_visible(tmp_wp))
tmp_wp = src_wp;
window_set_active_pane(src_w, tmp_wp);
}
} else {

View File

@@ -113,8 +113,11 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
if (item->client == NULL)
return (CMD_RETURN_NORMAL);
if (wl != NULL) {
if (wp != NULL)
server_unzoom_window(wl->window);
if (wp != NULL) {
window_redraw_active_switch(wp->window, wp);
window_set_active_pane(wp->window, wp);
}
session_set_current(s, wl);
cmd_find_from_session(&item->shared->current, s, 0);
}
@@ -128,6 +131,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
c->session = s;
if (~item->shared->flags & CMDQ_SHARED_REPEAT)
server_client_set_key_table(c, NULL);
tty_update_client_offset(c);
status_timer_start(c);
notify_client("client-session-changed", c);
session_update_activity(s, NULL);

View File

@@ -170,7 +170,7 @@ cmd_wait_for_wait(struct cmdq_item *item, const char *name,
struct client *c = item->client;
struct wait_item *wi;
if (c == NULL || c->session != NULL) {
if (c == NULL) {
cmdq_error(item, "not able to wait");
return (CMD_RETURN_ERROR);
}
@@ -198,7 +198,7 @@ cmd_wait_for_lock(struct cmdq_item *item, const char *name,
{
struct wait_item *wi;
if (item->client == NULL || item->client->session != NULL) {
if (item->client == NULL) {
cmdq_error(item, "not able to lock");
return (CMD_RETURN_ERROR);
}

53
cmd.c
View File

@@ -80,6 +80,7 @@ extern const struct cmd_entry cmd_refresh_client_entry;
extern const struct cmd_entry cmd_rename_session_entry;
extern const struct cmd_entry cmd_rename_window_entry;
extern const struct cmd_entry cmd_resize_pane_entry;
extern const struct cmd_entry cmd_resize_window_entry;
extern const struct cmd_entry cmd_respawn_pane_entry;
extern const struct cmd_entry cmd_respawn_window_entry;
extern const struct cmd_entry cmd_rotate_window_entry;
@@ -166,6 +167,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_rename_session_entry,
&cmd_rename_window_entry,
&cmd_resize_pane_entry,
&cmd_resize_window_entry,
&cmd_respawn_pane_entry,
&cmd_respawn_window_entry,
&cmd_rotate_window_entry,
@@ -200,6 +202,15 @@ const struct cmd_entry *cmd_table[] = {
NULL
};
void
cmd_log_argv(int argc, char **argv, const char *prefix)
{
int i;
for (i = 0; i < argc; i++)
log_debug("%s: argv[%d]=%s", prefix, i, argv[i]);
}
int
cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
{
@@ -208,6 +219,7 @@ cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
if (argc == 0)
return (0);
cmd_log_argv(argc, argv, __func__);
*buf = '\0';
for (i = 0; i < argc; i++) {
@@ -240,9 +252,11 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
arglen = strlen(buf) + 1;
(*argv)[i] = xstrdup(buf);
buf += arglen;
len -= arglen;
}
cmd_log_argv(argc, *argv, __func__);
return (0);
}
@@ -304,31 +318,31 @@ cmd_stringify_argv(int argc, char **argv)
static int
cmd_try_alias(int *argc, char ***argv)
{
struct options_entry *o;
int old_argc = *argc, new_argc;
char **old_argv = *argv, **new_argv;
u_int size, idx;
int i;
size_t wanted;
const char *s, *cp = NULL;
struct options_entry *o;
struct options_array_item *a;
int old_argc = *argc, new_argc, i;
char **old_argv = *argv, **new_argv;
size_t wanted;
const char *s, *cp = NULL;
o = options_get_only(global_options, "command-alias");
if (o == NULL || options_array_size(o, &size) == -1 || size == 0)
if (o == NULL)
return (-1);
wanted = strlen(old_argv[0]);
for (idx = 0; idx < size; idx++) {
s = options_array_get(o, idx);
if (s == NULL)
continue;
cp = strchr(s, '=');
if (cp == NULL || (size_t)(cp - s) != wanted)
continue;
if (strncmp(old_argv[0], s, wanted) == 0)
break;
a = options_array_first(o);
while (a != NULL) {
s = options_array_item_value(a);
if (s != NULL) {
cp = strchr(s, '=');
if (cp != NULL &&
(size_t)(cp - s) == wanted &&
strncmp(old_argv[0], s, wanted) == 0)
break;
}
a = options_array_next(a);
}
if (idx == size)
if (a == NULL)
return (-1);
if (cmd_string_split(cp + 1, &new_argc, &new_argv) != 0)
@@ -401,6 +415,7 @@ retry:
xasprintf(cause, "unknown command: %s", name);
return (NULL);
}
cmd_log_argv(argc, argv, entry->name);
args = args_parse(entry->args.template, argc, argv);
if (args == NULL)

View File

@@ -141,6 +141,8 @@ colour_tostring(int c)
return ("white");
case 8:
return ("default");
case 9:
return ("terminal");
case 90:
return ("brightblack");
case 91:
@@ -158,7 +160,7 @@ colour_tostring(int c)
case 97:
return ("brightwhite");
}
return (NULL);
return ("invalid");
}
/* Convert colour from string. */
@@ -188,6 +190,11 @@ colour_fromstring(const char *s)
return (n | COLOUR_FLAG_256);
}
if (strcasecmp(s, "default") == 0)
return (8);
if (strcasecmp(s, "terminal") == 0)
return (9);
if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0)
return (0);
if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0)
@@ -204,8 +211,6 @@ colour_fromstring(const char *s)
return (6);
if (strcasecmp(s, "white") == 0 || strcmp(s, "7") == 0)
return (7);
if (strcasecmp(s, "default") == 0 || strcmp(s, "8") == 0)
return (8);
if (strcasecmp(s, "brightblack") == 0 || strcmp(s, "90") == 0)
return (90);
if (strcasecmp(s, "brightred") == 0 || strcmp(s, "91") == 0)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: imsg-buffer.c,v 1.10 2017/04/11 09:57:19 reyk Exp $ */
/* $OpenBSD: imsg-buffer.c,v 1.11 2017/12/14 09:27:44 kettenis Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -29,9 +29,9 @@
#include "compat.h"
#include "imsg.h"
int ibuf_realloc(struct ibuf *, size_t);
void ibuf_enqueue(struct msgbuf *, struct ibuf *);
void ibuf_dequeue(struct msgbuf *, struct ibuf *);
static int ibuf_realloc(struct ibuf *, size_t);
static void ibuf_enqueue(struct msgbuf *, struct ibuf *);
static void ibuf_dequeue(struct msgbuf *, struct ibuf *);
struct ibuf *
ibuf_open(size_t len)
@@ -67,7 +67,7 @@ ibuf_dynamic(size_t len, size_t max)
return (buf);
}
int
static int
ibuf_realloc(struct ibuf *buf, size_t len)
{
u_char *b;
@@ -289,14 +289,14 @@ again:
return (1);
}
void
static void
ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
{
TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
msgbuf->queued++;
}
void
static void
ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
{
TAILQ_REMOVE(&msgbuf->bufs, buf, entry);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: imsg.c,v 1.15 2017/04/11 09:57:19 reyk Exp $ */
/* $OpenBSD: imsg.c,v 1.16 2017/12/14 09:27:44 kettenis Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -30,7 +30,7 @@
int imsg_fd_overhead = 0;
int imsg_get_fd(struct imsgbuf *);
static int imsg_get_fd(struct imsgbuf *);
void
imsg_init(struct imsgbuf *ibuf, int fd)
@@ -266,7 +266,7 @@ imsg_free(struct imsg *imsg)
freezero(imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE);
}
int
static int
imsg_get_fd(struct imsgbuf *ibuf)
{
int fd;

View File

@@ -1,6 +1,6 @@
# configure.ac
AC_INIT(tmux, 2.7-rc)
AC_INIT([tmux], 2.9)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@@ -35,7 +35,7 @@ AC_USE_SYSTEM_EXTENSIONS
test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc
# Is this --enable-debug?
test "x$VERSION" = xmaster && enable_debug=yes
case "x$VERSION" in xnext*) enable_debug=yes;; esac
AC_ARG_ENABLE(
debug,
AC_HELP_STRING(--enable-debug, enable debug build flags),
@@ -478,6 +478,9 @@ if test "x$found_forkpty" = xyes; then
fi
AM_CONDITIONAL(NEED_FORKPTY, test "x$found_forkpty" = xno)
# Look for kinfo_getfile in libutil.
AC_SEARCH_LIBS(kinfo_getfile, [util util-freebsd])
# Look for a suitable queue.h.
AC_CHECK_DECL(
TAILQ_CONCAT,
@@ -610,7 +613,16 @@ case "$host_os" in
*solaris*)
AC_MSG_RESULT(sunos)
PLATFORM=sunos
MANFORMAT=man
case `/usr/bin/nroff --version 2>&1` in
*GNU*)
# Solaris 11.4 and later use GNU groff.
MANFORMAT=mdoc
;;
*)
# Solaris 2.0 to 11.3 use AT&T nroff.
MANFORMAT=man
;;
esac
;;
*hpux*)
AC_MSG_RESULT(hpux)

View File

@@ -47,6 +47,8 @@ control_notify_input(struct client *c, struct window_pane *wp,
*/
if (winlink_find_by_window(&c->session->windows, wp->window) != NULL) {
message = evbuffer_new();
if (message == NULL)
fatalx("out of memory");
evbuffer_add_printf(message, "%%output %%%u ", wp->id);
for (i = 0; i < len; i++) {
if (buf[i] < ' ' || buf[i] == '\\')

View File

@@ -174,22 +174,26 @@ environ_unset(struct environ *env, const char *name)
void
environ_update(struct options *oo, struct environ *src, struct environ *dst)
{
struct environ_entry *envent;
struct options_entry *o;
u_int size, idx;
const char *value;
struct environ_entry *envent;
struct options_entry *o;
struct options_array_item *a;
const char *value;
o = options_get(oo, "update-environment");
if (o == NULL || options_array_size(o, &size) == -1)
if (o == NULL)
return;
for (idx = 0; idx < size; idx++) {
value = options_array_get(o, idx);
if (value == NULL)
a = options_array_first(o);
while (a != NULL) {
value = options_array_item_value(a);
if (value == NULL) {
a = options_array_next(a);
continue;
}
if ((envent = environ_find(src, value)) == NULL)
environ_clear(dst, value);
else
environ_set(dst, envent->name, "%s", envent->value);
a = options_array_next(a);
}
}

View File

@@ -49,11 +49,15 @@ bind F10 selectw -t:19
bind F11 selectw -t:20
bind F12 selectw -t:21
# Keys to toggle monitoring activity in a window, and synchronize-panes
# A key to toggle between smallest and largest sizes if a window is visible in
# multiple places
bind F set -w window-size
# Keys to toggle monitoring activity in a window and the synchronize-panes option
bind m set monitor-activity
bind y set synchronize-panes\; display 'synchronize-panes #{?synchronize-panes,on,off}'
# Create a single default session, because a session is created here, tmux
# Create a single default session - because a session is created here, tmux
# should be started with "tmux attach" rather than "tmux new"
new -d -s0 -nirssi 'exec irssi'
set -t0:0 monitor-activity on

892
format-draw.c Normal file
View File

@@ -0,0 +1,892 @@
/* $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"
/* Format range. */
struct format_range {
u_int index;
struct screen *s;
u_int start;
u_int end;
enum style_range_type type;
u_int argument;
TAILQ_ENTRY(format_range) entry;
};
TAILQ_HEAD(format_ranges, format_range);
/* Does this range match this style? */
static int
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);
return (1);
}
/* Free a range. */
static void
format_free_range(struct format_ranges *frs, struct format_range *fr)
{
TAILQ_REMOVE(frs, fr, entry);
free(fr);
}
/* Fix range positions. */
static void
format_update_ranges(struct format_ranges *frs, struct screen *s, u_int offset,
u_int start, u_int width)
{
struct format_range *fr, *fr1;
if (frs == NULL)
return;
TAILQ_FOREACH_SAFE(fr, frs, entry, fr1) {
if (fr->s != s)
continue;
if (fr->end <= start || fr->start >= start + width) {
format_free_range(frs, fr);
continue;
}
if (fr->start < start)
fr->start = start;
if (fr->end > start + width)
fr->end = start + width;
if (fr->start == fr->end) {
format_free_range(frs, fr);
continue;
}
fr->start -= start;
fr->end -= start;
fr->start += offset;
fr->end += offset;
}
}
/* Draw a part of the format. */
static void
format_draw_put(struct screen_write_ctx *octx, u_int ocx, u_int ocy,
struct screen *s, struct format_ranges *frs, u_int offset, u_int start,
u_int width)
{
/*
* The offset is how far from the cursor on the target screen; start
* and width how much to copy from the source screen.
*/
screen_write_cursormove(octx, ocx + offset, ocy, 0);
screen_write_fast_copy(octx, s, start, 0, width, 1);
format_update_ranges(frs, s, offset, start, width);
}
/* Draw list part of format. */
static void
format_draw_put_list(struct screen_write_ctx *octx,
u_int ocx, u_int ocy, u_int offset, u_int width, struct screen *list,
struct screen *list_left, struct screen *list_right, int focus_start,
int focus_end, struct format_ranges *frs)
{
u_int start, focus_centre;
/* If there is enough space for the list, draw it entirely. */
if (width >= list->cx) {
format_draw_put(octx, ocx, ocy, list, frs, offset, 0, width);
return;
}
/* The list needs to be trimmed. Try to keep the focus visible. */
focus_centre = focus_start + (focus_end - focus_start) / 2;
if (focus_centre < width / 2)
start = 0;
else
start = focus_centre - width / 2;
if (start + width > list->cx)
start = list->cx - width;
/* Draw <> markers at either side if needed. */
if (start != 0 && width > list_left->cx) {
screen_write_cursormove(octx, ocx + offset, ocy, 0);
screen_write_fast_copy(octx, list_left, 0, 0, list_left->cx, 1);
offset += list_left->cx;
start += list_left->cx;
width -= list_left->cx;
}
if (start + width < list->cx && width > list_right->cx) {
screen_write_cursormove(octx, ocx + offset + width - 1, ocy, 0);
screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx,
1);
width -= list_right->cx;
}
/* Draw the list screen itself. */
format_draw_put(octx, ocx, ocy, list, frs, offset, start, width);
}
/* Draw format with no list. */
static void
format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
struct format_ranges *frs)
{
u_int width_left, width_centre, width_right;
width_left = left->cx;
width_centre = centre->cx;
width_right = right->cx;
/*
* Try to keep as much of the left and right as possible at the expense
* of the centre.
*/
while (width_left + width_centre + width_right > available) {
if (width_centre > 0)
width_centre--;
else if (width_right > 0)
width_right--;
else
width_left--;
}
/* Write left. */
format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
/* Write right at available - width_right. */
format_draw_put(octx, ocx, ocy, right, frs,
available - width_right,
right->cx - width_right,
width_right);
/*
* Write centre halfway between
* width_left
* and
* available - width_right.
*/
format_draw_put(octx, ocx, ocy, centre, frs,
width_left
+ ((available - width_right) - width_left) / 2
- width_centre / 2,
centre->cx / 2 - width_centre / 2,
width_centre);
}
/* Draw format with list on the left. */
static void
format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
struct screen *list, struct screen *list_left, struct screen *list_right,
struct screen *after, int focus_start, int focus_end,
struct format_ranges *frs)
{
u_int width_left, width_centre, width_right;
u_int width_list, width_after;
struct screen_write_ctx ctx;
width_left = left->cx;
width_centre = centre->cx;
width_right = right->cx;
width_list = list->cx;
width_after = after->cx;
/*
* Trim first the centre, then the list, then the right, then after the
* list, then the left.
*/
while (width_left +
width_centre +
width_right +
width_list +
width_after > available) {
if (width_centre > 0)
width_centre--;
else if (width_list > 0)
width_list--;
else if (width_right > 0)
width_right--;
else if (width_after > 0)
width_after--;
else
width_left--;
}
/* If there is no list left, pass off to the no list function. */
if (width_list == 0) {
screen_write_start(&ctx, NULL, left);
screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
screen_write_stop(&ctx);
format_draw_none(octx, available, ocx, ocy, left, centre,
right, frs);
return;
}
/* Write left at 0. */
format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
/* Write right at available - width_right. */
format_draw_put(octx, ocx, ocy, right, frs,
available - width_right,
right->cx - width_right,
width_right);
/* Write after at width_left + width_list. */
format_draw_put(octx, ocx, ocy, after, frs,
width_left + width_list,
0,
width_after);
/*
* Write centre halfway between
* width_left + width_list + width_after
* and
* available - width_right.
*/
format_draw_put(octx, ocx, ocy, centre, frs,
(width_left + width_list + width_after)
+ ((available - width_right)
- (width_left + width_list + width_after)) / 2
- width_centre / 2,
centre->cx / 2 - width_centre / 2,
width_centre);
/*
* The list now goes from
* width_left
* to
* width_left + width_list.
* If there is no focus given, keep the left in focus.
*/
if (focus_start == -1 || focus_end == -1)
focus_start = focus_end = 0;
format_draw_put_list(octx, ocx, ocy, width_left, width_list, list,
list_left, list_right, focus_start, focus_end, frs);
}
/* Draw format with list in the centre. */
static void
format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
struct screen *list, struct screen *list_left, struct screen *list_right,
struct screen *after, int focus_start, int focus_end,
struct format_ranges *frs)
{
u_int width_left, width_centre, width_right;
u_int width_list, width_after, middle;
struct screen_write_ctx ctx;
width_left = left->cx;
width_centre = centre->cx;
width_right = right->cx;
width_list = list->cx;
width_after = after->cx;
/*
* Trim first the list, then after the list, then the centre, then the
* right, then the left.
*/
while (width_left +
width_centre +
width_right +
width_list +
width_after > available) {
if (width_list > 0)
width_list--;
else if (width_after > 0)
width_after--;
else if (width_centre > 0)
width_centre--;
else if (width_right > 0)
width_right--;
else
width_left--;
}
/* If there is no list left, pass off to the no list function. */
if (width_list == 0) {
screen_write_start(&ctx, NULL, centre);
screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
screen_write_stop(&ctx);
format_draw_none(octx, available, ocx, ocy, left, centre,
right, frs);
return;
}
/* Write left at 0. */
format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
/* Write after at available - width_after. */
format_draw_put(octx, ocx, ocy, after, frs,
available - width_after,
after->cx - width_after,
width_after);
/* Write right at available - width_right. */
format_draw_put(octx, ocx, ocy, right, frs,
available - width_right,
right->cx - width_right,
width_right);
/*
* All three centre sections are offset from the middle of the
* available space.
*/
middle = (width_left + ((available - width_right) - width_left) / 2);
/*
* Write centre at
* middle - width_list / 2 - width_centre.
*/
format_draw_put(octx, ocx, ocy, centre, frs,
middle - width_list / 2 - width_centre,
0,
width_centre);
/*
* Write after at
* middle + width_list / 2 - width_centre.
*/
format_draw_put(octx, ocx, ocy, after, frs,
middle + width_list / 2,
0,
width_after);
/*
* The list now goes from
* middle - width_list / 2
* to
* middle + width_list / 2
* If there is no focus given, keep the centre in focus.
*/
if (focus_start == -1 || focus_end == -1)
focus_start = focus_end = list->cx / 2;
format_draw_put_list(octx, ocx, ocy, middle - width_list / 2,
width_list, list, list_left, list_right, focus_start, focus_end,
frs);
}
/* Draw format with list on the right. */
static void
format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
struct screen *list, struct screen *list_left, struct screen *list_right,
struct screen *after, int focus_start, int focus_end,
struct format_ranges *frs)
{
u_int width_left, width_centre, width_right;
u_int width_list, width_after;
struct screen_write_ctx ctx;
width_left = left->cx;
width_centre = centre->cx;
width_right = right->cx;
width_list = list->cx;
width_after = after->cx;
/*
* Trim first the centre, then the list, then the right, then
* after the list, then the left.
*/
while (width_left +
width_centre +
width_right +
width_list +
width_after > available) {
if (width_centre > 0)
width_centre--;
else if (width_list > 0)
width_list--;
else if (width_right > 0)
width_right--;
else if (width_after > 0)
width_after--;
else
width_left--;
}
/* If there is no list left, pass off to the no list function. */
if (width_list == 0) {
screen_write_start(&ctx, NULL, right);
screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
screen_write_stop(&ctx);
format_draw_none(octx, available, ocx, ocy, left, centre,
right, frs);
return;
}
/* Write left at 0. */
format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
/* Write after at available - width_after. */
format_draw_put(octx, ocx, ocy, after, frs,
available - width_after,
after->cx - width_after,
width_after);
/*
* Write right at
* available - width_right - width_list - width_after.
*/
format_draw_put(octx, ocx, ocy, right, frs,
available - width_right - width_list - width_after,
0,
width_right);
/*
* Write centre halfway between
* width_left
* and
* available - width_right - width_list - width_after.
*/
format_draw_put(octx, ocx, ocy, centre, frs,
width_left
+ ((available - width_right - width_list - width_after)
- width_left) / 2
- width_centre / 2,
centre->cx / 2 - width_centre / 2,
width_centre);
/*
* The list now goes from
* available - width_list - width_after
* to
* available - width_after
* If there is no focus given, keep the right in focus.
*/
if (focus_start == -1 || focus_end == -1)
focus_start = focus_end = 0;
format_draw_put_list(octx, ocx, ocy, available - width_list -
width_after, width_list, list, list_left, list_right, focus_start,
focus_end, frs);
}
/* Draw a format to a screen. */
void
format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
u_int available, const char *expanded, struct style_ranges *srs)
{
enum { LEFT,
CENTRE,
RIGHT,
LIST,
LIST_LEFT,
LIST_RIGHT,
AFTER,
TOTAL } current = LEFT, last = LEFT;
const char *names[] = { "LEFT",
"CENTRE",
"RIGHT",
"LIST",
"LIST_LEFT",
"LIST_RIGHT",
"AFTER" };
size_t size = strlen(expanded);
struct screen *os = octx->s, s[TOTAL];
struct screen_write_ctx ctx[TOTAL];
u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL];
u_int map[] = { LEFT, LEFT, CENTRE, RIGHT };
int focus_start = -1, focus_end = -1;
int list_state = -1;
enum style_align list_align = STYLE_ALIGN_DEFAULT;
struct style sy;
struct utf8_data *ud = &sy.gc.data;
const char *cp, *end;
enum utf8_state more;
char *tmp;
struct format_range *fr = NULL, *fr1;
struct format_ranges frs;
struct style_range *sr;
style_set(&sy, base);
TAILQ_INIT(&frs);
log_debug("%s: %s", __func__, expanded);
/*
* We build three screens for left, right, centre alignment, one for
* the list, one for anything after the list and two for the list left
* and right markers.
*/
for (i = 0; i < TOTAL; i++) {
screen_init(&s[i], size, 1, 0);
screen_write_start(&ctx[i], NULL, &s[i]);
screen_write_clearendofline(&ctx[i], base->bg);
width[i] = 0;
}
/*
* Walk the string and add to the corresponding screens,
* parsing styles as we go.
*/
cp = expanded;
while (*cp != '\0') {
if (cp[0] != '#' || cp[1] != '[') {
/* See if this is a UTF-8 character. */
if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
while (*++cp != '\0' && more == UTF8_MORE)
more = utf8_append(ud, *cp);
if (more != UTF8_DONE)
cp -= ud->have;
}
/* Not a UTF-8 character - ASCII or not valid. */
if (more != UTF8_DONE) {
if (*cp < 0x20 || *cp > 0x7e) {
/* Ignore nonprintable characters. */
cp++;
continue;
}
utf8_set(ud, *cp);
cp++;
}
/* Draw the cell to th current screen. */
screen_write_cell(&ctx[current], &sy.gc);
width[current] += ud->width;
continue;
}
/* This is a style. Work out where the end is and parse it. */
end = format_skip(cp + 2, "]");
if (end == NULL) {
log_debug("%s: no terminating ] at '%s'", __func__,
cp + 2);
TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1)
format_free_range(&frs, fr);
goto out;
}
tmp = xstrndup(cp + 2, end - (cp + 2));
if (style_parse(&sy, base, tmp) != 0) {
log_debug("%s: invalid style '%s'", __func__, tmp);
free(tmp);
cp = end + 1;
continue;
}
log_debug("%s: style '%s' -> '%s'", __func__, tmp,
style_tostring(&sy));
free(tmp);
/* Check the list state. */
switch (sy.list) {
case STYLE_LIST_ON:
/*
* Entering the list, exiting a marker, or exiting the
* focus.
*/
if (list_state != 0) {
if (fr != NULL) { /* abort any region */
free(fr);
fr = NULL;
}
list_state = 0;
list_align = sy.align;
}
/* End the focus if started. */
if (focus_start != -1 && focus_end == -1)
focus_end = s[LIST].cx;
current = LIST;
break;
case STYLE_LIST_FOCUS:
/* Entering the focus. */
if (list_state != 0) /* not inside the list */
break;
if (focus_start == -1) /* focus already started */
focus_start = s[LIST].cx;
break;
case STYLE_LIST_OFF:
/* Exiting or outside the list. */
if (list_state == 0) {
if (fr != NULL) { /* abort any region */
free(fr);
fr = NULL;
}
if (focus_start != -1 && focus_end == -1)
focus_end = s[LIST].cx;
map[list_align] = AFTER;
if (list_align == STYLE_ALIGN_LEFT)
map[STYLE_ALIGN_DEFAULT] = AFTER;
list_state = 1;
}
current = map[sy.align];
break;
case STYLE_LIST_LEFT_MARKER:
/* Entering left marker. */
if (list_state != 0) /* not inside the list */
break;
if (s[LIST_LEFT].cx != 0) /* already have marker */
break;
if (fr != NULL) { /* abort any region */
free(fr);
fr = NULL;
}
if (focus_start != -1 && focus_end == -1)
focus_start = focus_end = -1;
current = LIST_LEFT;
break;
case STYLE_LIST_RIGHT_MARKER:
/* Entering right marker. */
if (list_state != 0) /* not inside the list */
break;
if (s[LIST_RIGHT].cx != 0) /* already have marker */
break;
if (fr != NULL) { /* abort any region */
free(fr);
fr = NULL;
}
if (focus_start != -1 && focus_end == -1)
focus_start = focus_end = -1;
current = LIST_RIGHT;
break;
}
if (current != last) {
log_debug("%s: change %s -> %s", __func__,
names[last], names[current]);
last = current;
}
/*
* Check if the range style has changed and if so end the
* current range and start a new one if needed.
*/
if (srs != NULL) {
if (fr != NULL && !format_is_type(fr, &sy)) {
if (s[current].cx != fr->start) {
fr->end = s[current].cx + 1;
TAILQ_INSERT_TAIL(&frs, fr, entry);
} else
free(fr);
fr = NULL;
}
if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) {
fr = xcalloc(1, sizeof *fr);
fr->index = current;
fr->s = &s[current];
fr->start = s[current].cx;
fr->type = sy.range_type;
fr->argument = sy.range_argument;
}
}
cp = end + 1;
}
free(fr);
for (i = 0; i < TOTAL; i++) {
screen_write_stop(&ctx[i]);
log_debug("%s: width %s is %u", __func__, names[i], width[i]);
}
if (focus_start != -1 && focus_end != -1)
log_debug("%s: focus %d-%d", __func__, focus_start, focus_end);
TAILQ_FOREACH(fr, &frs, entry) {
log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type,
fr->argument, names[fr->index], fr->start, fr->end);
}
/*
* Draw the screens. How they are arranged depends on where the list
* appearsq.
*/
switch (list_align) {
case STYLE_ALIGN_DEFAULT:
/* No list. */
format_draw_none(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &frs);
break;
case STYLE_ALIGN_LEFT:
/* List is part of the left. */
format_draw_left(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
&s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
break;
case STYLE_ALIGN_CENTRE:
/* List is part of the centre. */
format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
&s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
break;
case STYLE_ALIGN_RIGHT:
/* List is part of the right. */
format_draw_right(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
&s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
break;
}
/* Create ranges to return. */
TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) {
sr = xcalloc(1, sizeof *sr);
sr->type = fr->type;
sr->argument = fr->argument;
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);
format_free_range(&frs, fr);
}
out:
/* Free the screens. */
for (i = 0; i < TOTAL; i++)
screen_free(&s[i]);
/* Restore the original cursor position. */
screen_write_cursormove(octx, ocx, ocy, 0);
}
/* Get width, taking #[] into account. */
u_int
format_width(const char *expanded)
{
const char *cp, *end;
u_int width = 0;
struct utf8_data ud;
enum utf8_state more;
cp = expanded;
while (*cp != '\0') {
if (cp[0] == '#' && cp[1] == '[') {
end = format_skip(cp + 2, "]");
if (end == NULL)
return 0;
cp = end + 1;
} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
while (*++cp != '\0' && more == UTF8_MORE)
more = utf8_append(&ud, *cp);
if (more == UTF8_DONE)
width += ud.width;
else
cp -= ud.have;
} else if (*cp > 0x1f && *cp < 0x7f) {
width++;
cp++;
}
}
return (width);
}
/* Trim on the left, taking #[] into account. */
char *
format_trim_left(const char *expanded, u_int limit)
{
char *copy, *out;
const char *cp = expanded, *end;
u_int width = 0;
struct utf8_data ud;
enum utf8_state more;
out = copy = xmalloc(strlen(expanded) + 1);
while (*cp != '\0') {
if (cp[0] == '#' && cp[1] == '[') {
end = format_skip(cp + 2, "]");
if (end == NULL)
break;
memcpy(out, cp, end + 1 - cp);
out += (end + 1 - cp);
cp = end + 1;
} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
while (*++cp != '\0' && more == UTF8_MORE)
more = utf8_append(&ud, *cp);
if (more == UTF8_DONE) {
if (width + ud.width <= limit) {
memcpy(out, ud.data, ud.size);
out += ud.size;
}
width += ud.width;
} else
cp -= ud.have;
} else if (*cp > 0x1f && *cp < 0x7f) {
if (width + 1 <= limit)
*out++ = *cp;
width++;
cp++;
} else
cp++;
}
*out = '\0';
return (copy);
}
/* Trim on the right, taking #[] into account. */
char *
format_trim_right(const char *expanded, u_int limit)
{
char *copy, *out;
const char *cp = expanded, *end;
u_int width = 0, total_width, skip;
struct utf8_data ud;
enum utf8_state more;
total_width = format_width(expanded);
if (total_width <= limit)
return (xstrdup(expanded));
skip = total_width - limit;
out = copy = xmalloc(strlen(expanded) + 1);
while (*cp != '\0') {
if (cp[0] == '#' && cp[1] == '[') {
end = format_skip(cp + 2, "]");
if (end == NULL)
break;
memcpy(out, cp, end + 1 - cp);
out += (end + 1 - cp);
cp = end + 1;
} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
while (*++cp != '\0' && more == UTF8_MORE)
more = utf8_append(&ud, *cp);
if (more == UTF8_DONE) {
if (width >= skip) {
memcpy(out, ud.data, ud.size);
out += ud.size;
}
width += ud.width;
} else
cp -= ud.have;
} else if (*cp > 0x1f && *cp < 0x7f) {
if (width >= skip)
*out++ = *cp;
width++;
cp++;
} else
cp++;
}
*out = '\0';
return (copy);
}

1067
format.c

File diff suppressed because it is too large Load Diff

View File

@@ -64,7 +64,7 @@ grid_view_clear_history(struct grid *gd, u_int bg)
/* Find the last used line. */
last = 0;
for (yy = 0; yy < gd->sy; yy++) {
gl = &gd->linedata[grid_view_y(gd, yy)];
gl = grid_get_line(gd, grid_view_y(gd, yy));
if (gl->cellused != 0)
last = yy + 1;
}

278
grid.c
View File

@@ -39,8 +39,13 @@
const struct grid_cell grid_default_cell = {
0, 0, 8, 8, { { ' ' }, 0, 1, 1 }
};
static const struct grid_cell_entry grid_default_entry = {
0, { .data = { 0, 8, 8, ' ' } }
/* Cleared grid cell data. */
const struct grid_cell grid_cleared_cell = {
GRID_FLAG_CLEARED, 0, 8, 8, { { ' ' }, 0, 1, 1 }
};
static const struct grid_cell_entry grid_cleared_entry = {
GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } }
};
static void grid_empty_line(struct grid *, u_int, u_int);
@@ -50,7 +55,7 @@ static void
grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
u_char c)
{
gce->flags = gc->flags;
gce->flags = (gc->flags & ~GRID_FLAG_CLEARED);
gce->data.fg = gc->fg & 0xff;
if (gc->fg & COLOUR_FLAG_256)
@@ -64,7 +69,7 @@ grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
gce->data.data = c;
}
/* Check if a cell should be extended. */
/* Check if a cell should be an extended cell. */
static int
grid_need_extended_cell(const struct grid_cell_entry *gce,
const struct grid_cell *gc)
@@ -80,6 +85,40 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (0);
}
/* Get an extended cell. */
static void
grid_get_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
int flags)
{
u_int at = gl->extdsize + 1;
gl->extddata = xreallocarray(gl->extddata, at, sizeof *gl->extddata);
gl->extdsize = at;
gce->offset = at - 1;
gce->flags = (flags | GRID_FLAG_EXTENDED);
}
/* Set cell as extended. */
static struct grid_cell *
grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
const struct grid_cell *gc)
{
struct grid_cell *gcp;
int flags = (gc->flags & ~GRID_FLAG_CLEARED);
if (~gce->flags & GRID_FLAG_EXTENDED)
grid_get_extended_cell(gl, gce, flags);
else if (gce->offset >= gl->extdsize)
fatalx("offset too big");
gl->flags |= GRID_LINE_EXTENDED;
gcp = &gl->extddata[gce->offset];
memcpy(gcp, gc, sizeof *gcp);
gcp->flags = flags;
return (gcp);
}
/* Free up unused extended cells. */
static void
grid_compact_line(struct grid_line *gl)
@@ -122,27 +161,18 @@ grid_compact_line(struct grid_line *gl)
gl->extdsize = new_extdsize;
}
/* Set cell as extended. */
static struct grid_cell *
grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
const struct grid_cell *gc)
/* Get line data. */
struct grid_line *
grid_get_line(struct grid *gd, u_int line)
{
struct grid_cell *gcp;
return (&gd->linedata[line]);
}
gl->flags |= GRID_LINE_EXTENDED;
if (~gce->flags & GRID_FLAG_EXTENDED) {
gl->extddata = xreallocarray(gl->extddata, gl->extdsize + 1,
sizeof *gl->extddata);
gce->offset = gl->extdsize++;
gce->flags = gc->flags | GRID_FLAG_EXTENDED;
}
if (gce->offset >= gl->extdsize)
fatalx("offset too big");
gcp = &gl->extddata[gce->offset];
memcpy(gcp, gc, sizeof *gcp);
return (gcp);
/* Adjust number of lines. */
void
grid_adjust_lines(struct grid *gd, u_int lines)
{
gd->linedata = xreallocarray(gd->linedata, lines, sizeof *gd->linedata);
}
/* Copy default into a cell. */
@@ -153,9 +183,13 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
struct grid_cell_entry *gce = &gl->celldata[px];
struct grid_cell *gc;
memcpy(gce, &grid_default_entry, sizeof *gce);
memcpy(gce, &grid_cleared_entry, sizeof *gce);
if (bg & COLOUR_FLAG_RGB) {
gc = grid_extended_cell(gl, gce, &grid_default_cell);
grid_get_extended_cell(gl, gce, gce->flags);
gl->flags |= GRID_LINE_EXTENDED;
gc = &gl->extddata[gce->offset];
memcpy(gc, &grid_cleared_cell, sizeof *gc);
gc->bg = bg;
} else {
if (bg & COLOUR_FLAG_256)
@@ -166,10 +200,10 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
/* Check grid y position. */
static int
grid_check_y(struct grid *gd, u_int py)
grid_check_y(struct grid *gd, const char* from, u_int py)
{
if (py >= gd->hsize + gd->sy) {
log_debug("y out of range: %u", py);
log_debug("%s: y out of range: %u", from, py);
return (-1);
}
return (0);
@@ -272,6 +306,15 @@ grid_compare(struct grid *ga, struct grid *gb)
return (0);
}
/* Trim lines from the history. */
static void
grid_trim_history(struct grid *gd, u_int ny)
{
grid_free_lines(gd, 0, ny);
memmove(&gd->linedata[0], &gd->linedata[ny],
(gd->hsize + gd->sy - ny) * (sizeof *gd->linedata));
}
/*
* Collect lines from the history if at the limit. Free the top (oldest) 10%
* and shift up.
@@ -294,9 +337,7 @@ grid_collect_history(struct grid *gd)
* Free the lines from 0 to ny then move the remaining lines over
* them.
*/
grid_free_lines(gd, 0, ny);
memmove(&gd->linedata[0], &gd->linedata[ny],
(gd->hsize + gd->sy - ny) * (sizeof *gd->linedata));
grid_trim_history(gd, ny);
gd->hsize -= ny;
if (gd->hscrolled > gd->hsize)
@@ -326,9 +367,7 @@ grid_scroll_history(struct grid *gd, u_int bg)
void
grid_clear_history(struct grid *gd)
{
grid_free_lines(gd, 0, gd->hsize);
memmove(&gd->linedata[0], &gd->linedata[gd->hsize],
gd->sy * (sizeof *gd->linedata));
grid_trim_history(gd, gd->hsize);
gd->hscrolled = 0;
gd->hsize = 0;
@@ -399,7 +438,7 @@ static void
grid_empty_line(struct grid *gd, u_int py, u_int bg)
{
memset(&gd->linedata[py], 0, sizeof gd->linedata[py]);
if (bg != 8)
if (!COLOUR_DEFAULT(bg))
grid_expand_line(gd, py, gd->sx, bg);
}
@@ -407,7 +446,7 @@ grid_empty_line(struct grid *gd, u_int py, u_int bg)
const struct grid_line *
grid_peek_line(struct grid *gd, u_int py)
{
if (grid_check_y(gd, py) != 0)
if (grid_check_y(gd, __func__, py) != 0)
return (NULL);
return (&gd->linedata[py]);
}
@@ -441,11 +480,11 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
void
grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc)
{
if (grid_check_y(gd, py) != 0 || px >= gd->linedata[py].cellsize) {
if (grid_check_y(gd, __func__, py) != 0 ||
px >= gd->linedata[py].cellsize)
memcpy(gc, &grid_default_cell, sizeof *gc);
return;
}
return (grid_get_cell1(&gd->linedata[py], px, gc));
else
grid_get_cell1(&gd->linedata[py], px, gc);
}
/* Set cell at relative position. */
@@ -455,7 +494,7 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
struct grid_line *gl;
struct grid_cell_entry *gce;
if (grid_check_y(gd, py) != 0)
if (grid_check_y(gd, __func__, py) != 0)
return;
grid_expand_line(gd, py, px + 1, 8);
@@ -481,7 +520,7 @@ grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
struct grid_cell *gcp;
u_int i;
if (grid_check_y(gd, py) != 0)
if (grid_check_y(gd, __func__, py) != 0)
return;
grid_expand_line(gd, py, px + slen, 8);
@@ -504,7 +543,8 @@ grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
void
grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
{
u_int xx, yy;
struct grid_line *gl;
u_int xx, yy;
if (nx == 0 || ny == 0)
return;
@@ -514,18 +554,19 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
return;
}
if (grid_check_y(gd, py) != 0)
if (grid_check_y(gd, __func__, py) != 0)
return;
if (grid_check_y(gd, py + ny - 1) != 0)
if (grid_check_y(gd, __func__, py + ny - 1) != 0)
return;
for (yy = py; yy < py + ny; yy++) {
if (px + nx >= gd->sx && px < gd->linedata[yy].cellused)
gd->linedata[yy].cellused = px;
if (px > gd->linedata[yy].cellsize && bg == 8)
gl = &gd->linedata[yy];
if (px + nx >= gd->sx && px < gl->cellused)
gl->cellused = px;
if (px > gl->cellsize && COLOUR_DEFAULT(bg))
continue;
if (px + nx >= gd->linedata[yy].cellsize && bg == 8) {
gd->linedata[yy].cellsize = px;
if (px + nx >= gl->cellsize && COLOUR_DEFAULT(bg)) {
gl->cellsize = px;
continue;
}
grid_expand_line(gd, yy, px + nx, 8); /* default bg first */
@@ -543,9 +584,9 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
if (ny == 0)
return;
if (grid_check_y(gd, py) != 0)
if (grid_check_y(gd, __func__, py) != 0)
return;
if (grid_check_y(gd, py + ny - 1) != 0)
if (grid_check_y(gd, __func__, py + ny - 1) != 0)
return;
for (yy = py; yy < py + ny; yy++) {
@@ -563,13 +604,13 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
if (ny == 0 || py == dy)
return;
if (grid_check_y(gd, py) != 0)
if (grid_check_y(gd, __func__, py) != 0)
return;
if (grid_check_y(gd, py + ny - 1) != 0)
if (grid_check_y(gd, __func__, py + ny - 1) != 0)
return;
if (grid_check_y(gd, dy) != 0)
if (grid_check_y(gd, __func__, dy) != 0)
return;
if (grid_check_y(gd, dy + ny - 1) != 0)
if (grid_check_y(gd, __func__, dy + ny - 1) != 0)
return;
/* Free any lines which are being replaced. */
@@ -603,7 +644,7 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
if (nx == 0 || px == dx)
return;
if (grid_check_y(gd, py) != 0)
if (grid_check_y(gd, __func__, py) != 0)
return;
gl = &gd->linedata[py];
@@ -744,7 +785,11 @@ grid_string_cells_code(const struct grid_cell *lastgc,
{ GRID_ATTR_BLINK, 5 },
{ GRID_ATTR_REVERSE, 7 },
{ GRID_ATTR_HIDDEN, 8 },
{ GRID_ATTR_STRIKETHROUGH, 9 }
{ GRID_ATTR_STRIKETHROUGH, 9 },
{ GRID_ATTR_UNDERSCORE_2, 42 },
{ GRID_ATTR_UNDERSCORE_3, 43 },
{ GRID_ATTR_UNDERSCORE_4, 44 },
{ GRID_ATTR_UNDERSCORE_5, 45 },
};
n = 0;
@@ -770,11 +815,15 @@ grid_string_cells_code(const struct grid_cell *lastgc,
else
strlcat(buf, "\033[", len);
for (i = 0; i < n; i++) {
if (i + 1 < n)
xsnprintf(tmp, sizeof tmp, "%d;", s[i]);
else
if (s[i] < 10)
xsnprintf(tmp, sizeof tmp, "%d", s[i]);
else {
xsnprintf(tmp, sizeof tmp, "%d:%d", s[i] / 10,
s[i] % 10);
}
strlcat(buf, tmp, len);
if (i + 1 < n)
strlcat(buf, ";", len);
}
strlcat(buf, "m", len);
}
@@ -981,11 +1030,11 @@ grid_reflow_move(struct grid *gd, struct grid_line *from)
/* Join line below onto this one. */
static void
grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
u_int width, u_int *cy, int already)
u_int width, int already)
{
struct grid_line *gl, *from;
struct grid_line *gl, *from = NULL;
struct grid_cell gc;
u_int lines, want, left, i, to, line;
u_int lines, left, i, to, line, want = 0;
u_int at;
int wrapped = 1;
@@ -1010,7 +1059,7 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
* If this is now the last line, there is nothing more to be
* done.
*/
if (yy + lines == gd->hsize + gd->sy)
if (yy + 1 + lines == gd->hsize + gd->sy)
break;
line = yy + 1 + lines;
@@ -1020,6 +1069,7 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
if (gd->linedata[line].cellused == 0) {
if (!wrapped)
break;
lines++;
continue;
}
@@ -1078,11 +1128,7 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
grid_reflow_dead(&gd->linedata[i]);
}
/* Adjust cursor and scroll positions. */
if (*cy > to + lines)
*cy -= lines;
else if (*cy > to)
*cy = to;
/* Adjust scroll position. */
if (gd->hscrolled > to + lines)
gd->hscrolled -= lines;
else if (gd->hscrolled > to)
@@ -1092,7 +1138,7 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
/* Split this line into several new ones */
static void
grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
u_int at, u_int *cy)
u_int at)
{
struct grid_line *gl = &gd->linedata[yy], *first;
struct grid_cell gc;
@@ -1145,9 +1191,7 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
memcpy(first, gl, sizeof *first);
grid_reflow_dead(gl);
/* Adjust the cursor and scroll positions. */
if (yy <= *cy)
(*cy) += lines - 1;
/* Adjust the scroll position. */
if (yy <= gd->hscrolled)
gd->hscrolled += lines - 1;
@@ -1156,24 +1200,17 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
* in the last new line, try to join with the next lines.
*/
if (width < sx && (flags & GRID_LINE_WRAPPED))
grid_reflow_join(target, gd, sx, yy, width, cy, 1);
grid_reflow_join(target, gd, sx, yy, width, 1);
}
/* Reflow lines on grid to new width. */
void
grid_reflow(struct grid *gd, u_int sx, u_int *cursor)
grid_reflow(struct grid *gd, u_int sx)
{
struct grid *target;
struct grid_line *gl;
struct grid_cell gc;
u_int yy, cy, width, i, at, first;
struct timeval start, tv;
gettimeofday(&start, NULL);
log_debug("%s: %u lines, new width %u", __func__, gd->hsize + gd->sy,
sx);
cy = gd->hsize + (*cursor);
u_int yy, width, i, at, first;
/*
* Create a destination grid. This is just used as a container for the
@@ -1227,7 +1264,7 @@ grid_reflow(struct grid *gd, u_int sx, u_int *cursor)
* it was previously wrapped.
*/
if (width > sx) {
grid_reflow_split(target, gd, sx, yy, at, &cy);
grid_reflow_split(target, gd, sx, yy, at);
continue;
}
@@ -1236,7 +1273,7 @@ grid_reflow(struct grid *gd, u_int sx, u_int *cursor)
* of the next line.
*/
if (gl->flags & GRID_LINE_WRAPPED)
grid_reflow_join(target, gd, sx, yy, width, &cy, 0);
grid_reflow_join(target, gd, sx, yy, width, 0);
else
grid_reflow_move(target, gl);
}
@@ -1247,23 +1284,68 @@ grid_reflow(struct grid *gd, u_int sx, u_int *cursor)
if (target->sy < gd->sy)
grid_reflow_add(target, gd->sy - target->sy);
gd->hsize = target->sy - gd->sy;
if (gd->hscrolled > gd->hsize)
gd->hscrolled = gd->hsize;
free(gd->linedata);
gd->linedata = target->linedata;
free(target);
}
/* Convert to position based on wrapped lines. */
void
grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy)
{
u_int ax = 0, ay = 0, yy;
for (yy = 0; yy < py; yy++) {
if (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
ax += gd->linedata[yy].cellused;
else {
ax = 0;
ay++;
}
}
if (px >= gd->linedata[yy].cellused)
ax = UINT_MAX;
else
ax += px;
*wx = ax;
*wy = ay;
}
/* Convert position based on wrapped lines back. */
void
grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
{
u_int yy, ax = 0, ay = 0;
for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) {
if (ay == wy)
break;
if (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
ax += gd->linedata[yy].cellused;
else {
ax = 0;
ay++;
}
}
/*
* Update scrolled and cursor positions.
* yy is now 0 on the unwrapped line which contains wx. Walk forwards
* until we find the end or the line now containing wx.
*/
if (gd->hscrolled > gd->hsize)
gd->hscrolled = gd->hsize;
if (cy < gd->hsize)
*cursor = 0;
else
*cursor = cy - gd->hsize;
gettimeofday(&tv, NULL);
timersub(&tv, &start, &tv);
log_debug("%s: now %u lines (in %llu.%06u seconds)", __func__,
gd->hsize + gd->sy, (unsigned long long)tv.tv_sec,
(u_int)tv.tv_usec);
if (wx == UINT_MAX) {
while (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
yy++;
wx = gd->linedata[yy].cellused;
} else {
while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) {
if (wx < gd->linedata[yy].cellused)
break;
wx -= gd->linedata[yy].cellused;
yy++;
}
}
*px = wx;
*py = yy;
}

View File

@@ -247,10 +247,10 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m)
if ((mode & ALL_MOUSE_MODES) == 0)
return;
if (!window_pane_visible(wp))
return;
if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
return;
if (!window_pane_visible(wp))
return;
/* If this pane is not in button or all mode, discard motion events. */
if (MOUSE_DRAG(m->b) &&

401
input.c
View File

@@ -30,7 +30,7 @@
/*
* Based on the description by Paul Williams at:
*
* http://vt100.net/emu/dec_ansi_parser
* https://vt100.net/emu/dec_ansi_parser
*
* With the following changes:
*
@@ -81,6 +81,7 @@ struct input_ctx {
struct input_cell old_cell;
u_int old_cx;
u_int old_cy;
int old_mode;
u_char interm_buf[4];
size_t interm_len;
@@ -93,6 +94,10 @@ struct input_ctx {
u_char *input_buf;
size_t input_len;
size_t input_space;
enum {
INPUT_END_ST,
INPUT_END_BEL
} input_end;
struct input_param param_list[24];
u_int param_list_len;
@@ -126,11 +131,11 @@ static void input_set_state(struct window_pane *,
const struct input_transition *);
static void input_reset_cell(struct input_ctx *);
static void input_osc_4(struct window_pane *, const char *);
static void input_osc_10(struct window_pane *, const char *);
static void input_osc_11(struct window_pane *, const char *);
static void input_osc_52(struct window_pane *, const char *);
static void input_osc_104(struct window_pane *, const char *);
static void input_osc_4(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_52(struct input_ctx *, const char *);
static void input_osc_104(struct input_ctx *, const char *);
/* Transition entry/exit handlers. */
static void input_clear(struct input_ctx *);
@@ -161,6 +166,7 @@ static void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *);
static void input_csi_dispatch_sgr(struct input_ctx *);
static int input_dcs_dispatch(struct input_ctx *);
static int input_top_bit_set(struct input_ctx *);
static int input_end_bel(struct input_ctx *);
/* Command table comparison function. */
static int input_table_compare(const void *, const void *);
@@ -266,6 +272,7 @@ static const struct input_table_entry input_csi_table[] = {
{ 'S', "", INPUT_CSI_SU },
{ 'X', "", INPUT_CSI_ECH },
{ 'Z', "", INPUT_CSI_CBT },
{ '`', "", INPUT_CSI_HPA },
{ 'b', "", INPUT_CSI_REP },
{ 'c', "", INPUT_CSI_DA },
{ 'c', ">", INPUT_CSI_DA_TWO },
@@ -487,7 +494,7 @@ static const struct input_transition input_state_esc_enter_table[] = {
{ -1, -1, NULL, NULL }
};
/* esc_interm state table. */
/* esc_intermediate state table. */
static const struct input_transition input_state_esc_intermediate_table[] = {
INPUT_STATE_ANYWHERE,
@@ -602,7 +609,7 @@ static const struct input_transition input_state_dcs_parameter_table[] = {
{ -1, -1, NULL, NULL }
};
/* dcs_interm state table. */
/* dcs_intermediate state table. */
static const struct input_transition input_state_dcs_intermediate_table[] = {
INPUT_STATE_ANYWHERE,
@@ -655,12 +662,12 @@ static const struct input_transition input_state_dcs_ignore_table[] = {
static const struct input_transition input_state_osc_string_table[] = {
INPUT_STATE_ANYWHERE,
{ 0x00, 0x06, NULL, NULL },
{ 0x07, 0x07, NULL, &input_state_ground },
{ 0x08, 0x17, NULL, NULL },
{ 0x19, 0x19, NULL, NULL },
{ 0x1c, 0x1f, NULL, NULL },
{ 0x20, 0xff, input_input, NULL },
{ 0x00, 0x06, NULL, NULL },
{ 0x07, 0x07, input_end_bel, &input_state_ground },
{ 0x08, 0x17, NULL, NULL },
{ 0x19, 0x19, NULL, NULL },
{ 0x1c, 0x1f, NULL, NULL },
{ 0x20, 0xff, input_input, NULL },
{ -1, -1, NULL, NULL }
};
@@ -750,6 +757,32 @@ input_reset_cell(struct input_ctx *ictx)
ictx->old_cy = 0;
}
/* Save screen state. */
static void
input_save_state(struct input_ctx *ictx)
{
struct screen_write_ctx *sctx = &ictx->ctx;
struct screen *s = sctx->s;
memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell);
ictx->old_cx = s->cx;
ictx->old_cy = s->cy;
ictx->old_mode = s->mode;
}
static void
input_restore_state(struct input_ctx *ictx)
{
struct screen_write_ctx *sctx = &ictx->ctx;
memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell);
if (ictx->old_mode & MODE_ORIGIN)
screen_write_mode_set(sctx, MODE_ORIGIN);
else
screen_write_mode_clear(sctx, MODE_ORIGIN);
screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy, 0);
}
/* Initialise input parser. */
void
input_init(struct window_pane *wp)
@@ -762,6 +795,8 @@ input_init(struct window_pane *wp)
ictx->input_buf = xmalloc(INPUT_BUF_START);
ictx->since_ground = evbuffer_new();
if (ictx->since_ground == NULL)
fatalx("out of memory");
evtimer_set(&ictx->timer, input_timer_callback, ictx);
@@ -794,16 +829,17 @@ void
input_reset(struct window_pane *wp, int clear)
{
struct input_ctx *ictx = wp->ictx;
struct screen_write_ctx *sctx = &ictx->ctx;
input_reset_cell(ictx);
if (clear) {
if (wp->mode == NULL)
screen_write_start(&ictx->ctx, wp, &wp->base);
if (TAILQ_EMPTY(&wp->modes))
screen_write_start(sctx, wp, &wp->base);
else
screen_write_start(&ictx->ctx, NULL, &wp->base);
screen_write_reset(&ictx->ctx);
screen_write_stop(&ictx->ctx);
screen_write_start(sctx, NULL, &wp->base);
screen_write_reset(sctx);
screen_write_stop(sctx);
}
input_clear(ictx);
@@ -839,6 +875,7 @@ void
input_parse(struct window_pane *wp)
{
struct input_ctx *ictx = wp->ictx;
struct screen_write_ctx *sctx = &ictx->ctx;
const struct input_transition *itr;
struct evbuffer *evb = wp->event->input;
u_char *buf;
@@ -854,10 +891,10 @@ input_parse(struct window_pane *wp)
* Open the screen. Use NULL wp if there is a mode set as don't want to
* update the tty.
*/
if (wp->mode == NULL)
screen_write_start(&ictx->ctx, wp, &wp->base);
if (TAILQ_EMPTY(&wp->modes))
screen_write_start(sctx, wp, &wp->base);
else
screen_write_start(&ictx->ctx, NULL, &wp->base);
screen_write_start(sctx, NULL, &wp->base);
ictx->wp = wp;
buf = EVBUFFER_DATA(evb);
@@ -893,7 +930,7 @@ input_parse(struct window_pane *wp)
* be the minority.
*/
if (itr->handler != input_print)
screen_write_collect_end(&ictx->ctx);
screen_write_collect_end(sctx);
/*
* Execute the handler, if any. Don't switch state if it
@@ -912,7 +949,7 @@ input_parse(struct window_pane *wp)
}
/* Close the screen. */
screen_write_stop(&ictx->ctx);
screen_write_stop(sctx);
evbuffer_drain(evb, len);
}
@@ -993,8 +1030,8 @@ input_get(struct input_ctx *ictx, u_int validx, int minval, int defval)
static void
input_reply(struct input_ctx *ictx, const char *fmt, ...)
{
va_list ap;
char *reply;
va_list ap;
char *reply;
va_start(ap, fmt);
xvasprintf(&reply, fmt, ap);
@@ -1019,6 +1056,8 @@ input_clear(struct input_ctx *ictx)
*ictx->input_buf = '\0';
ictx->input_len = 0;
ictx->input_end = INPUT_END_ST;
ictx->flags &= ~INPUT_DISCARD;
}
@@ -1039,7 +1078,8 @@ input_ground(struct input_ctx *ictx)
static int
input_print(struct input_ctx *ictx)
{
int set;
struct screen_write_ctx *sctx = &ictx->ctx;
int set;
ictx->utf8started = 0; /* can't be valid UTF-8 */
@@ -1050,7 +1090,7 @@ input_print(struct input_ctx *ictx)
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
utf8_set(&ictx->cell.cell.data, ictx->ch);
screen_write_collect_add(&ictx->ctx, &ictx->cell.cell);
screen_write_collect_add(sctx, &ictx->cell.cell);
ictx->last = ictx->ch;
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
@@ -1210,13 +1250,10 @@ input_esc_dispatch(struct input_ctx *ictx)
screen_write_mode_clear(sctx, MODE_KKEYPAD);
break;
case INPUT_ESC_DECSC:
memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell);
ictx->old_cx = s->cx;
ictx->old_cy = s->cy;
input_save_state(ictx);
break;
case INPUT_ESC_DECRC:
memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell);
screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy);
input_restore_state(ictx);
break;
case INPUT_ESC_DECALN:
screen_write_alignmenttest(sctx);
@@ -1303,7 +1340,7 @@ input_csi_dispatch(struct input_ctx *ictx)
n = input_get(ictx, 0, 1, 1);
m = input_get(ictx, 1, 1, 1);
if (n != -1 && m != -1)
screen_write_cursormove(sctx, m - 1, n - 1);
screen_write_cursormove(sctx, m - 1, n - 1, 1);
break;
case INPUT_CSI_WINOPS:
input_csi_dispatch_winops(ictx);
@@ -1435,7 +1472,7 @@ input_csi_dispatch(struct input_ctx *ictx)
case INPUT_CSI_HPA:
n = input_get(ictx, 0, 1, 1);
if (n != -1)
screen_write_cursormove(sctx, n - 1, s->cy);
screen_write_cursormove(sctx, n - 1, -1, 1);
break;
case INPUT_CSI_ICH:
n = input_get(ictx, 0, 1, 1);
@@ -1460,8 +1497,7 @@ input_csi_dispatch(struct input_ctx *ictx)
input_print(ictx);
break;
case INPUT_CSI_RCP:
memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell);
screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy);
input_restore_state(ictx);
break;
case INPUT_CSI_RM:
input_csi_dispatch_rm(ictx);
@@ -1470,9 +1506,7 @@ input_csi_dispatch(struct input_ctx *ictx)
input_csi_dispatch_rm_private(ictx);
break;
case INPUT_CSI_SCP:
memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell);
ictx->old_cx = s->cx;
ictx->old_cy = s->cy;
input_save_state(ictx);
break;
case INPUT_CSI_SGR:
input_csi_dispatch_sgr(ictx);
@@ -1507,7 +1541,7 @@ input_csi_dispatch(struct input_ctx *ictx)
case INPUT_CSI_VPA:
n = input_get(ictx, 0, 1, 1);
if (n != -1)
screen_write_cursormove(sctx, s->cx, n - 1);
screen_write_cursormove(sctx, -1, n - 1, 1);
break;
case INPUT_CSI_DECSCUSR:
n = input_get(ictx, 0, 0, 0);
@@ -1524,17 +1558,18 @@ input_csi_dispatch(struct input_ctx *ictx)
static void
input_csi_dispatch_rm(struct input_ctx *ictx)
{
u_int i;
struct screen_write_ctx *sctx = &ictx->ctx;
u_int i;
for (i = 0; i < ictx->param_list_len; i++) {
switch (input_get(ictx, i, 0, -1)) {
case -1:
break;
case 4: /* IRM */
screen_write_mode_clear(&ictx->ctx, MODE_INSERT);
screen_write_mode_clear(sctx, MODE_INSERT);
break;
case 34:
screen_write_mode_set(&ictx->ctx, MODE_BLINKING);
screen_write_mode_set(sctx, MODE_BLINKING);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -1547,6 +1582,7 @@ input_csi_dispatch_rm(struct input_ctx *ictx)
static void
input_csi_dispatch_rm_private(struct input_ctx *ictx)
{
struct screen_write_ctx *sctx = &ictx->ctx;
struct window_pane *wp = ictx->wp;
u_int i;
@@ -1555,36 +1591,39 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
case -1:
break;
case 1: /* DECCKM */
screen_write_mode_clear(&ictx->ctx, MODE_KCURSOR);
screen_write_mode_clear(sctx, MODE_KCURSOR);
break;
case 3: /* DECCOLM */
screen_write_cursormove(&ictx->ctx, 0, 0);
screen_write_clearscreen(&ictx->ctx,
ictx->cell.cell.bg);
screen_write_cursormove(sctx, 0, 0, 1);
screen_write_clearscreen(sctx, ictx->cell.cell.bg);
break;
case 6: /* DECOM */
screen_write_mode_clear(sctx, MODE_ORIGIN);
screen_write_cursormove(sctx, 0, 0, 1);
break;
case 7: /* DECAWM */
screen_write_mode_clear(&ictx->ctx, MODE_WRAP);
screen_write_mode_clear(sctx, MODE_WRAP);
break;
case 12:
screen_write_mode_clear(&ictx->ctx, MODE_BLINKING);
screen_write_mode_clear(sctx, MODE_BLINKING);
break;
case 25: /* TCEM */
screen_write_mode_clear(&ictx->ctx, MODE_CURSOR);
screen_write_mode_clear(sctx, MODE_CURSOR);
break;
case 1000:
case 1001:
case 1002:
case 1003:
screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES);
screen_write_mode_clear(sctx, ALL_MOUSE_MODES);
break;
case 1004:
screen_write_mode_clear(&ictx->ctx, MODE_FOCUSON);
screen_write_mode_clear(sctx, MODE_FOCUSON);
break;
case 1005:
screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_UTF8);
screen_write_mode_clear(sctx, MODE_MOUSE_UTF8);
break;
case 1006:
screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_SGR);
screen_write_mode_clear(sctx, MODE_MOUSE_SGR);
break;
case 47:
case 1047:
@@ -1594,7 +1633,7 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
window_pane_alternate_off(wp, &ictx->cell.cell, 1);
break;
case 2004:
screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE);
screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -1607,17 +1646,18 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
static void
input_csi_dispatch_sm(struct input_ctx *ictx)
{
u_int i;
struct screen_write_ctx *sctx = &ictx->ctx;
u_int i;
for (i = 0; i < ictx->param_list_len; i++) {
switch (input_get(ictx, i, 0, -1)) {
case -1:
break;
case 4: /* IRM */
screen_write_mode_set(&ictx->ctx, MODE_INSERT);
screen_write_mode_set(sctx, MODE_INSERT);
break;
case 34:
screen_write_mode_clear(&ictx->ctx, MODE_BLINKING);
screen_write_mode_clear(sctx, MODE_BLINKING);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -1630,6 +1670,7 @@ input_csi_dispatch_sm(struct input_ctx *ictx)
static void
input_csi_dispatch_sm_private(struct input_ctx *ictx)
{
struct screen_write_ctx *sctx = &ictx->ctx;
struct window_pane *wp = ictx->wp;
u_int i;
@@ -1638,45 +1679,48 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
case -1:
break;
case 1: /* DECCKM */
screen_write_mode_set(&ictx->ctx, MODE_KCURSOR);
screen_write_mode_set(sctx, MODE_KCURSOR);
break;
case 3: /* DECCOLM */
screen_write_cursormove(&ictx->ctx, 0, 0);
screen_write_clearscreen(&ictx->ctx,
ictx->cell.cell.bg);
screen_write_cursormove(sctx, 0, 0, 1);
screen_write_clearscreen(sctx, ictx->cell.cell.bg);
break;
case 6: /* DECOM */
screen_write_mode_set(sctx, MODE_ORIGIN);
screen_write_cursormove(sctx, 0, 0, 1);
break;
case 7: /* DECAWM */
screen_write_mode_set(&ictx->ctx, MODE_WRAP);
screen_write_mode_set(sctx, MODE_WRAP);
break;
case 12:
screen_write_mode_set(&ictx->ctx, MODE_BLINKING);
screen_write_mode_set(sctx, MODE_BLINKING);
break;
case 25: /* TCEM */
screen_write_mode_set(&ictx->ctx, MODE_CURSOR);
screen_write_mode_set(sctx, MODE_CURSOR);
break;
case 1000:
screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES);
screen_write_mode_set(&ictx->ctx, MODE_MOUSE_STANDARD);
screen_write_mode_clear(sctx, ALL_MOUSE_MODES);
screen_write_mode_set(sctx, MODE_MOUSE_STANDARD);
break;
case 1002:
screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES);
screen_write_mode_set(&ictx->ctx, MODE_MOUSE_BUTTON);
screen_write_mode_clear(sctx, ALL_MOUSE_MODES);
screen_write_mode_set(sctx, MODE_MOUSE_BUTTON);
break;
case 1003:
screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES);
screen_write_mode_set(&ictx->ctx, MODE_MOUSE_ALL);
screen_write_mode_clear(sctx, ALL_MOUSE_MODES);
screen_write_mode_set(sctx, MODE_MOUSE_ALL);
break;
case 1004:
if (ictx->ctx.s->mode & MODE_FOCUSON)
if (sctx->s->mode & MODE_FOCUSON)
break;
screen_write_mode_set(&ictx->ctx, MODE_FOCUSON);
screen_write_mode_set(sctx, MODE_FOCUSON);
wp->flags |= PANE_FOCUSPUSH; /* force update */
break;
case 1005:
screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8);
screen_write_mode_set(sctx, MODE_MOUSE_UTF8);
break;
case 1006:
screen_write_mode_set(&ictx->ctx, MODE_MOUSE_SGR);
screen_write_mode_set(sctx, MODE_MOUSE_SGR);
break;
case 47:
case 1047:
@@ -1686,7 +1730,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
window_pane_alternate_on(wp, &ictx->cell.cell, 1);
break;
case 2004:
screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE);
screen_write_mode_set(sctx, MODE_BRACKETPASTE);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -1699,6 +1743,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
static void
input_csi_dispatch_winops(struct input_ctx *ictx)
{
struct screen_write_ctx *sctx = &ictx->ctx;
struct window_pane *wp = ictx->wp;
int n, m;
@@ -1738,7 +1783,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
return;
case 0:
case 2:
screen_push_title(ictx->ctx.s);
screen_push_title(sctx->s);
break;
}
break;
@@ -1749,7 +1794,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
return;
case 0:
case 2:
screen_pop_title(ictx->ctx.s);
screen_pop_title(sctx->s);
server_status_window(ictx->wp->window);
break;
}
@@ -1834,10 +1879,11 @@ input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i)
static void
input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i)
{
char *s = ictx->param_list[i].str, *copy, *ptr, *out;
int p[8];
u_int n;
const char *errstr;
struct grid_cell *gc = &ictx->cell.cell;
char *s = ictx->param_list[i].str, *copy, *ptr, *out;
int p[8];
u_int n;
const char *errstr;
for (n = 0; n < nitems(p); n++)
p[n] = -1;
@@ -1845,27 +1891,67 @@ input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i)
ptr = copy = xstrdup(s);
while ((out = strsep(&ptr, ":")) != NULL) {
p[n++] = strtonum(out, 0, INT_MAX, &errstr);
if (errstr != NULL || n == nitems(p)) {
free(copy);
return;
}
if (*out != '\0') {
p[n++] = strtonum(out, 0, INT_MAX, &errstr);
if (errstr != NULL || n == nitems(p)) {
free(copy);
return;
}
} else
n++;
log_debug("%s: %u = %d", __func__, n - 1, p[n - 1]);
}
free(copy);
if (n == 0 || (p[0] != 38 && p[0] != 48))
if (n == 0)
return;
switch (p[1]) {
case 2:
if (n != 5)
if (p[0] == 4) {
if (n != 2)
return;
switch (p[1]) {
case 0:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
break;
input_csi_dispatch_sgr_rgb_do(ictx, p[0], p[2], p[3], p[4]);
case 1:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
gc->attr |= GRID_ATTR_UNDERSCORE;
break;
case 2:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
gc->attr |= GRID_ATTR_UNDERSCORE_2;
break;
case 3:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
gc->attr |= GRID_ATTR_UNDERSCORE_3;
break;
case 4:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
gc->attr |= GRID_ATTR_UNDERSCORE_4;
break;
case 5:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
gc->attr |= GRID_ATTR_UNDERSCORE_5;
break;
}
return;
}
if (p[0] != 38 && p[0] != 48)
return;
if (p[1] == -1)
i = 2;
else
i = 1;
switch (p[i]) {
case 2:
if (n < i + 4)
break;
input_csi_dispatch_sgr_rgb_do(ictx, p[0], p[i + 1], p[i + 2],
p[i + 3]);
break;
case 5:
if (n != 3)
if (n < i + 2)
break;
input_csi_dispatch_sgr_256_do(ictx, p[0], p[2]);
input_csi_dispatch_sgr_256_do(ictx, p[0], p[i + 1]);
break;
}
}
@@ -1919,6 +2005,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
gc->attr |= GRID_ATTR_ITALICS;
break;
case 4:
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
gc->attr |= GRID_ATTR_UNDERSCORE;
break;
case 5:
@@ -1940,7 +2027,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
gc->attr &= ~GRID_ATTR_ITALICS;
break;
case 24:
gc->attr &= ~GRID_ATTR_UNDERSCORE;
gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
break;
case 25:
gc->attr &= ~GRID_ATTR_BLINK;
@@ -2004,6 +2091,17 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
}
}
/* End of input with BEL. */
static int
input_end_bel(struct input_ctx *ictx)
{
log_debug("%s", __func__);
ictx->input_end = INPUT_END_BEL;
return (0);
}
/* DCS string started. */
static void
input_enter_dcs(struct input_ctx *ictx)
@@ -2019,20 +2117,19 @@ input_enter_dcs(struct input_ctx *ictx)
static int
input_dcs_dispatch(struct input_ctx *ictx)
{
const char prefix[] = "tmux;";
const u_int prefix_len = (sizeof prefix) - 1;
struct screen_write_ctx *sctx = &ictx->ctx;
u_char *buf = ictx->input_buf;
size_t len = ictx->input_len;
const char prefix[] = "tmux;";
const u_int prefixlen = (sizeof prefix) - 1;
if (ictx->flags & INPUT_DISCARD)
return (0);
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
log_debug("%s: \"%s\"", __func__, buf);
/* Check for tmux prefix. */
if (ictx->input_len >= prefix_len &&
strncmp(ictx->input_buf, prefix, prefix_len) == 0) {
screen_write_rawstring(&ictx->ctx,
ictx->input_buf + prefix_len, ictx->input_len - prefix_len);
}
if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0)
screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen);
return (0);
}
@@ -2052,15 +2149,17 @@ input_enter_osc(struct input_ctx *ictx)
static void
input_exit_osc(struct input_ctx *ictx)
{
u_char *p = ictx->input_buf;
u_int option;
struct screen_write_ctx *sctx = &ictx->ctx;
u_char *p = ictx->input_buf;
u_int option;
if (ictx->flags & INPUT_DISCARD)
return;
if (ictx->input_len < 1 || *p < '0' || *p > '9')
return;
log_debug("%s: \"%s\"", __func__, p);
log_debug("%s: \"%s\" (end %s)", __func__, p,
ictx->input_end == INPUT_END_ST ? "ST" : "BEL");
option = 0;
while (*p >= '0' && *p <= '9')
@@ -2072,32 +2171,32 @@ input_exit_osc(struct input_ctx *ictx)
case 0:
case 2:
if (utf8_isvalid(p)) {
screen_set_title(ictx->ctx.s, p);
screen_set_title(sctx->s, p);
server_status_window(ictx->wp->window);
}
break;
case 4:
input_osc_4(ictx->wp, p);
input_osc_4(ictx, p);
break;
case 10:
input_osc_10(ictx->wp, p);
input_osc_10(ictx, p);
break;
case 11:
input_osc_11(ictx->wp, p);
input_osc_11(ictx, p);
break;
case 12:
if (utf8_isvalid(p) && *p != '?') /* ? is colour request */
screen_set_cursor_colour(ictx->ctx.s, p);
screen_set_cursor_colour(sctx->s, p);
break;
case 52:
input_osc_52(ictx->wp, p);
input_osc_52(ictx, p);
break;
case 104:
input_osc_104(ictx->wp, p);
input_osc_104(ictx, p);
break;
case 112:
if (*p == '\0') /* no arguments allowed */
screen_set_cursor_colour(ictx->ctx.s, "");
screen_set_cursor_colour(sctx->s, "");
break;
default:
log_debug("%s: unknown '%u'", __func__, option);
@@ -2120,13 +2219,15 @@ input_enter_apc(struct input_ctx *ictx)
static void
input_exit_apc(struct input_ctx *ictx)
{
struct screen_write_ctx *sctx = &ictx->ctx;
if (ictx->flags & INPUT_DISCARD)
return;
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
if (!utf8_isvalid(ictx->input_buf))
return;
screen_set_title(ictx->ctx.s, ictx->input_buf);
screen_set_title(sctx->s, ictx->input_buf);
server_status_window(ictx->wp->window);
}
@@ -2162,6 +2263,7 @@ input_exit_rename(struct input_ctx *ictx)
static int
input_top_bit_set(struct input_ctx *ictx)
{
struct screen_write_ctx *sctx = &ictx->ctx;
struct utf8_data *ud = &ictx->utf8data;
ictx->last = -1;
@@ -2188,18 +2290,19 @@ input_top_bit_set(struct input_ctx *ictx)
(int)ud->size, ud->data, ud->width);
utf8_copy(&ictx->cell.cell.data, ud);
screen_write_collect_add(&ictx->ctx, &ictx->cell.cell);
screen_write_collect_add(sctx, &ictx->cell.cell);
return (0);
}
/* Handle the OSC 4 sequence for setting (multiple) palette entries. */
static void
input_osc_4(struct window_pane *wp, const char *p)
input_osc_4(struct input_ctx *ictx, const char *p)
{
char *copy, *s, *next = NULL;
long idx;
u_int r, g, b;
struct window_pane *wp = ictx->wp;
char *copy, *s, *next = NULL;
long idx;
u_int r, g, b;
copy = s = xstrdup(p);
while (s != NULL && *s != '\0') {
@@ -2227,16 +2330,17 @@ bad:
free(copy);
}
/* Handle the OSC 10 sequence for setting background colour. */
/* Handle the OSC 10 sequence for setting foreground colour. */
static void
input_osc_10(struct window_pane *wp, const char *p)
input_osc_10(struct input_ctx *ictx, const char *p)
{
u_int r, g, b;
struct window_pane *wp = ictx->wp;
u_int r, g, b;
if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3)
goto bad;
wp->colgc.fg = colour_join_rgb(r, g, b);
wp->style.gc.fg = colour_join_rgb(r, g, b);
wp->flags |= PANE_REDRAW;
return;
@@ -2247,14 +2351,15 @@ bad:
/* Handle the OSC 11 sequence for setting background colour. */
static void
input_osc_11(struct window_pane *wp, const char *p)
input_osc_11(struct input_ctx *ictx, const char *p)
{
u_int r, g, b;
struct window_pane *wp = ictx->wp;
u_int r, g, b;
if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3)
goto bad;
wp->colgc.bg = colour_join_rgb(r, g, b);
wp->style.gc.bg = colour_join_rgb(r, g, b);
wp->flags |= PANE_REDRAW;
return;
@@ -2265,13 +2370,16 @@ bad:
/* Handle the OSC 52 sequence for setting the clipboard. */
static void
input_osc_52(struct window_pane *wp, const char *p)
input_osc_52(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
char *end;
const char *buf;
size_t len;
u_char *out;
int outlen, state;
struct screen_write_ctx ctx;
struct paste_buffer *pb;
state = options_get_number(global_options, "set-clipboard");
if (state != 2)
@@ -2282,6 +2390,32 @@ input_osc_52(struct window_pane *wp, const char *p)
end++;
if (*end == '\0')
return;
log_debug("%s: %s", __func__, end);
if (strcmp(end, "?") == 0) {
if ((pb = paste_get_top(NULL)) != NULL) {
buf = paste_buffer_data(pb, &len);
outlen = 4 * ((len + 2) / 3) + 1;
out = xmalloc(outlen);
if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) {
abort();
free(out);
return;
}
} else {
outlen = 0;
out = NULL;
}
bufferevent_write(wp->event, "\033]52;;", 6);
if (outlen != 0)
bufferevent_write(wp->event, out, outlen);
if (ictx->input_end == INPUT_END_BEL)
bufferevent_write(wp->event, "\007", 1);
else
bufferevent_write(wp->event, "\033\\", 2);
free(out);
return;
}
len = (strlen(end) / 4) * 3;
if (len == 0)
@@ -2303,10 +2437,11 @@ input_osc_52(struct window_pane *wp, const char *p)
/* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */
static void
input_osc_104(struct window_pane *wp, const char *p)
input_osc_104(struct input_ctx *ictx, const char *p)
{
char *copy, *s;
long idx;
struct window_pane *wp = ictx->wp;
char *copy, *s;
long idx;
if (*p == '\0') {
window_pane_reset_palette(wp);

104
job.c
View File

@@ -36,8 +36,33 @@ static void job_read_callback(struct bufferevent *, void *);
static void job_write_callback(struct bufferevent *, void *);
static void job_error_callback(struct bufferevent *, short, void *);
/* A single job. */
struct job {
enum {
JOB_RUNNING,
JOB_DEAD,
JOB_CLOSED
} state;
int flags;
char *cmd;
pid_t pid;
int status;
int fd;
struct bufferevent *event;
job_update_cb updatecb;
job_complete_cb completecb;
job_free_cb freecb;
void *data;
LIST_ENTRY(job) entry;
};
/* All jobs list. */
struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
/* Start a job running, if it isn't already. */
struct job *
@@ -54,6 +79,7 @@ job_run(const char *cmd, struct session *s, const char *cwd,
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0)
return (NULL);
log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd == NULL ? "" : cwd);
/*
* Do not set TERM during .tmux.conf, it is nice to be able to use
@@ -128,6 +154,8 @@ job_run(const char *cmd, struct session *s, const char *cwd,
job->event = bufferevent_new(job->fd, job_read_callback,
job_write_callback, job_error_callback, job);
if (job->event == NULL)
fatalx("out of memory");
bufferevent_enable(job->event, EV_READ|EV_WRITE);
log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid);
@@ -207,8 +235,16 @@ job_error_callback(__unused struct bufferevent *bufev, __unused short events,
/* Job died (waitpid() returned its pid). */
void
job_died(struct job *job, int status)
job_check_died(pid_t pid, int status)
{
struct job *job;
LIST_FOREACH(job, &all_jobs, entry) {
if (pid == job->pid)
break;
}
if (job == NULL)
return;
log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid);
job->status = status;
@@ -222,3 +258,67 @@ job_died(struct job *job, int status)
job->state = JOB_DEAD;
}
}
/* Get job status. */
int
job_get_status(struct job *job)
{
return (job->status);
}
/* Get job data. */
void *
job_get_data(struct job *job)
{
return (job->data);
}
/* Get job event. */
struct bufferevent *
job_get_event(struct job *job)
{
return (job->event);
}
/* Kill all jobs. */
void
job_kill_all(void)
{
struct job *job;
LIST_FOREACH(job, &all_jobs, entry) {
if (job->pid != -1)
kill(job->pid, SIGTERM);
}
}
/* Are any jobs still running? */
int
job_still_running(void)
{
struct job *job;
LIST_FOREACH(job, &all_jobs, entry) {
if ((~job->flags & JOB_NOWAIT) && job->state == JOB_RUNNING)
return (1);
}
return (0);
}
/* Print job summary. */
void
job_print_summary(struct cmdq_item *item, int blank)
{
struct job *job;
u_int n = 0;
LIST_FOREACH(job, &all_jobs, entry) {
if (blank) {
cmdq_print(item, "%s", "");
blank = 0;
}
cmdq_print(item, "Job %u: %s [fd=%d, pid=%ld, status=%d]",
n, job->cmd, job->fd, (long)job->pid, job->status);
n++;
}
}

View File

@@ -24,17 +24,19 @@
#include "tmux.h"
RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp);
RB_GENERATE(key_tables, key_table, entry, key_table_cmp);
struct key_tables key_tables = RB_INITIALIZER(&key_tables);
static int key_bindings_cmp(struct key_binding *, struct key_binding *);
RB_GENERATE_STATIC(key_bindings, key_binding, entry, key_bindings_cmp);
static int key_table_cmp(struct key_table *, struct key_table *);
RB_GENERATE_STATIC(key_tables, key_table, entry, key_table_cmp);
static struct key_tables key_tables = RB_INITIALIZER(&key_tables);
int
key_table_cmp(struct key_table *e1, struct key_table *e2)
static int
key_table_cmp(struct key_table *table1, struct key_table *table2)
{
return (strcmp(e1->name, e2->name));
return (strcmp(table1->name, table2->name));
}
int
static int
key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
{
if (bd1->key < bd2->key)
@@ -64,6 +66,18 @@ key_bindings_get_table(const char *name, int create)
return (table);
}
struct key_table *
key_bindings_first_table(void)
{
return (RB_MIN(key_tables, &key_tables));
}
struct key_table *
key_bindings_next_table(struct key_table *table)
{
return (RB_NEXT(key_tables, &key_tables, table));
}
void
key_bindings_unref_table(struct key_table *table)
{
@@ -83,6 +97,27 @@ key_bindings_unref_table(struct key_table *table)
free(table);
}
struct key_binding *
key_bindings_get(struct key_table *table, key_code key)
{
struct key_binding bd;
bd.key = key;
return (RB_FIND(key_bindings, &table->key_bindings, &bd));
}
struct key_binding *
key_bindings_first(struct key_table *table)
{
return (RB_MIN(key_bindings, &table->key_bindings));
}
struct key_binding *
key_bindings_next(__unused struct key_table *table, struct key_binding *bd)
{
return (RB_NEXT(key_bindings, &table->key_bindings, bd));
}
void
key_bindings_add(const char *name, key_code key, int repeat,
struct cmd_list *cmdlist)
@@ -193,7 +228,7 @@ key_bindings_init(void)
"bind ] paste-buffer",
"bind c new-window",
"bind d detach-client",
"bind f command-prompt \"find-window -- '%%'\"",
"bind f command-prompt \"find-window -Z -- '%%'\"",
"bind i display-message",
"bind l last-window",
"bind m select-pane -m",
@@ -223,6 +258,11 @@ key_bindings_init(void)
"bind M-n next-window -a",
"bind M-o rotate-window -D",
"bind M-p previous-window -a",
"bind -r S-Up refresh-client -U 10",
"bind -r S-Down refresh-client -D 10",
"bind -r S-Left refresh-client -L 10",
"bind -r S-Right refresh-client -R 10",
"bind -r DC refresh-client -c",
"bind -r M-Up resize-pane -U 5",
"bind -r M-Down resize-pane -D 5",
"bind -r M-Left resize-pane -L 5",

View File

@@ -43,7 +43,9 @@ static const struct {
{ "F11", KEYC_F11 },
{ "F12", KEYC_F12 },
{ "IC", KEYC_IC },
{ "Insert", KEYC_IC },
{ "DC", KEYC_DC },
{ "Delete", KEYC_DC },
{ "Home", KEYC_HOME },
{ "End", KEYC_END },
{ "NPage", KEYC_NPAGE },
@@ -166,9 +168,11 @@ key_string_lookup_string(const char *string)
enum utf8_state more;
wchar_t wc;
/* Is this no key? */
/* Is this no key or any key? */
if (strcasecmp(string, "None") == 0)
return (KEYC_NONE);
if (strcasecmp(string, "Any") == 0)
return (KEYC_ANY);
/* Is this a hexadecimal value? */
if (string[0] == '0' && string[1] == 'x') {
@@ -251,6 +255,8 @@ key_string_lookup_key(key_code key)
/* Handle special keys. */
if (key == KEYC_UNKNOWN)
return ("Unknown");
if (key == KEYC_ANY)
return ("Any");
if (key == KEYC_FOCUS_IN)
return ("FocusIn");
if (key == KEYC_FOCUS_OUT)
@@ -267,6 +273,10 @@ key_string_lookup_key(key_code key)
return ("MouseMovePane");
if (key == KEYC_MOUSEMOVE_STATUS)
return ("MouseMoveStatus");
if (key == KEYC_MOUSEMOVE_STATUS_LEFT)
return ("MouseMoveStatusLeft");
if (key == KEYC_MOUSEMOVE_STATUS_RIGHT)
return ("MouseMoveStatusRight");
if (key == KEYC_MOUSEMOVE_BORDER)
return ("MouseMoveBorder");
if (key >= KEYC_USER && key < KEYC_USER + KEYC_NUSER) {

View File

@@ -167,7 +167,7 @@ layout_parse(struct window *w, const char *layout)
/* Update pane offsets and sizes. */
layout_fix_offsets(lc);
layout_fix_panes(w, lc->sx, lc->sy);
layout_fix_panes(w);
/* Then resize the layout back to the original window size. */
layout_resize(w, sx, sy);

View File

@@ -119,7 +119,7 @@ layout_set_even(struct window *w, enum layout_type type)
{
struct window_pane *wp;
struct layout_cell *lc, *lcnew;
u_int n;
u_int n, sx, sy;
layout_print_cell(w->layout_root, __func__, 1);
@@ -131,7 +131,18 @@ layout_set_even(struct window *w, enum layout_type type)
/* Free the old root and construct a new. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, w->sx, w->sy, 0, 0);
if (type == LAYOUT_LEFTRIGHT) {
sx = (n * (PANE_MINIMUM + 1)) - 1;
if (sx < w->sx)
sx = w->sx;
sy = w->sy;
} else {
sy = (n * (PANE_MINIMUM + 1)) - 1;
if (sy < w->sy)
sy = w->sy;
sx = w->sx;
}
layout_set_size(lc, sx, sy, 0, 0);
layout_make_node(lc, type);
/* Build new leaf cells. */
@@ -148,10 +159,11 @@ layout_set_even(struct window *w, enum layout_type type)
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_panes(w, w->sx, w->sy);
layout_fix_panes(w);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -172,9 +184,8 @@ static void
layout_set_main_h(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lcrow, *lcchild;
u_int n, mainheight, otherheight, width, height;
u_int used, i, j, columns, rows, totalrows;
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
u_int n, mainh, otherh, sx;
layout_print_cell(w->layout_root, __func__, 1);
@@ -184,110 +195,65 @@ layout_set_main_h(struct window *w)
return;
n--; /* take off main pane */
/* How many rows and columns will be needed, not counting main? */
columns = (w->sx + 1) / (PANE_MINIMUM + 1); /* maximum columns */
if (columns == 0)
columns = 1;
rows = 1 + (n - 1) / columns;
columns = 1 + (n - 1) / rows;
width = (w->sx - (n - 1)) / columns;
/* Get the main pane height and add one for separator line. */
mainheight = options_get_number(w->options, "main-pane-height") + 1;
/* Get the optional other pane height and add one for separator line. */
otherheight = options_get_number(w->options, "other-pane-height") + 1;
/*
* If an other pane height was specified, honour it so long as it
* doesn't shrink the main height to less than the main-pane-height
*/
if (otherheight > 1 && w->sy - otherheight > mainheight)
mainheight = w->sy - otherheight;
if (mainheight < PANE_MINIMUM + 1)
mainheight = PANE_MINIMUM + 1;
/* Try and make everything fit. */
totalrows = rows * (PANE_MINIMUM + 1) - 1;
if (mainheight + totalrows > w->sy) {
if (totalrows + PANE_MINIMUM + 1 > w->sy)
mainheight = PANE_MINIMUM + 2;
/* Get the main pane height and work out the other pane height. */
mainh = options_get_number(w->options, "main-pane-height");
if (mainh + PANE_MINIMUM + 1 >= w->sy) {
if (w->sy <= PANE_MINIMUM + 1 + PANE_MINIMUM)
mainh = PANE_MINIMUM;
else
mainheight = w->sy - totalrows;
height = PANE_MINIMUM;
} else
height = (w->sy - mainheight - (rows - 1)) / rows;
mainh = w->sy - (PANE_MINIMUM + 1);
otherh = PANE_MINIMUM;
} else {
otherh = options_get_number(w->options, "other-pane-height");
if (otherh == 0)
otherh = w->sy - mainh;
else if (otherh > w->sy || w->sy - otherh < mainh)
otherh = w->sy - mainh;
else
mainh = w->sy - otherh;
}
/* Work out what height is needed. */
sx = (n * (PANE_MINIMUM + 1)) - 1;
if (sx < w->sx)
sx = w->sx;
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, w->sx, mainheight + rows * (height + 1) - 1, 0, 0);
layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM);
/* Create the main pane. */
lcmain = layout_create_cell(lc);
layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0);
layout_set_size(lcmain, sx, mainh, 0, 0);
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
/* Create a grid of the remaining cells. */
wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
for (j = 0; j < rows; j++) {
/* If this is the last cell, all done. */
if (wp == NULL)
break;
/* Create the other pane. */
lcother = layout_create_cell(lc);
layout_set_size(lcother, sx, otherh, 0, 0);
layout_make_node(lcother, LAYOUT_LEFTRIGHT);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
/* Create the new row. */
lcrow = layout_create_cell(lc);
layout_set_size(lcrow, w->sx, height, 0, 0);
TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
/* If only one column, just use the row directly. */
if (columns == 1) {
layout_make_leaf(lcrow, wp);
wp = TAILQ_NEXT(wp, entry);
/* Add the remaining panes as children. */
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == TAILQ_FIRST(&w->panes))
continue;
}
/* Add in the columns. */
layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
for (i = 0; i < columns; i++) {
/* Create and add a pane cell. */
lcchild = layout_create_cell(lcrow);
layout_set_size(lcchild, width, height, 0, 0);
layout_make_leaf(lcchild, wp);
TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
/* Move to the next cell. */
if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
break;
}
/* Adjust the row to fit the full width if necessary. */
if (i == columns)
i--;
used = ((i + 1) * (width + 1)) - 1;
if (w->sx <= used)
continue;
lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT,
w->sx - used);
}
/* Adjust the last row height to fit if necessary. */
used = mainheight + (rows * height) + rows - 1;
if (w->sy > used) {
lcrow = TAILQ_LAST(&lc->cells, layout_cells);
layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
w->sy - used);
lcchild = layout_create_cell(lc);
layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
layout_make_leaf(lcchild, wp);
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
}
layout_spread_cell(w, lcother);
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_panes(w, w->sx, w->sy);
layout_fix_panes(w);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -296,9 +262,8 @@ static void
layout_set_main_v(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lccolumn, *lcchild;
u_int n, mainwidth, otherwidth, width, height;
u_int used, i, j, columns, rows, totalcolumns;
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
u_int n, mainw, otherw, sy;
layout_print_cell(w->layout_root, __func__, 1);
@@ -308,110 +273,65 @@ layout_set_main_v(struct window *w)
return;
n--; /* take off main pane */
/* How many rows and columns will be needed, not counting main? */
rows = (w->sy + 1) / (PANE_MINIMUM + 1); /* maximum rows */
if (rows == 0)
rows = 1;
columns = 1 + (n - 1) / rows;
rows = 1 + (n - 1) / columns;
height = (w->sy - (n - 1)) / rows;
/* Get the main pane width and add one for separator line. */
mainwidth = options_get_number(w->options, "main-pane-width") + 1;
/* Get the optional other pane width and add one for separator line. */
otherwidth = options_get_number(w->options, "other-pane-width") + 1;
/*
* If an other pane width was specified, honour it so long as it
* doesn't shrink the main width to less than the main-pane-width
*/
if (otherwidth > 1 && w->sx - otherwidth > mainwidth)
mainwidth = w->sx - otherwidth;
if (mainwidth < PANE_MINIMUM + 1)
mainwidth = PANE_MINIMUM + 1;
/* Try and make everything fit. */
totalcolumns = columns * (PANE_MINIMUM + 1) - 1;
if (mainwidth + totalcolumns > w->sx) {
if (totalcolumns + PANE_MINIMUM + 1 > w->sx)
mainwidth = PANE_MINIMUM + 2;
/* Get the main pane width and work out the other pane width. */
mainw = options_get_number(w->options, "main-pane-width");
if (mainw + PANE_MINIMUM + 1 >= w->sx) {
if (w->sx <= PANE_MINIMUM + 1 + PANE_MINIMUM)
mainw = PANE_MINIMUM;
else
mainwidth = w->sx - totalcolumns;
width = PANE_MINIMUM;
} else
width = (w->sx - mainwidth - (columns - 1)) / columns;
mainw = w->sx - (PANE_MINIMUM + 1);
otherw = PANE_MINIMUM;
} else {
otherw = options_get_number(w->options, "other-pane-width");
if (otherw == 0)
otherw = w->sx - mainw;
else if (otherw > w->sx || w->sx - otherw < mainw)
otherw = w->sx - mainw;
else
mainw = w->sx - otherw;
}
/* Work out what height is needed. */
sy = (n * (PANE_MINIMUM + 1)) - 1;
if (sy < w->sy)
sy = w->sy;
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, mainwidth + columns * (width + 1) - 1, w->sy, 0, 0);
layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
layout_make_node(lc, LAYOUT_LEFTRIGHT);
/* Create the main pane. */
lcmain = layout_create_cell(lc);
layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0);
layout_set_size(lcmain, mainw, sy, 0, 0);
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
/* Create a grid of the remaining cells. */
wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
for (j = 0; j < columns; j++) {
/* If this is the last cell, all done. */
if (wp == NULL)
break;
/* Create the other pane. */
lcother = layout_create_cell(lc);
layout_set_size(lcother, otherw, sy, 0, 0);
layout_make_node(lcother, LAYOUT_TOPBOTTOM);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
/* Create the new column. */
lccolumn = layout_create_cell(lc);
layout_set_size(lccolumn, width, w->sy, 0, 0);
TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry);
/* If only one row, just use the row directly. */
if (rows == 1) {
layout_make_leaf(lccolumn, wp);
wp = TAILQ_NEXT(wp, entry);
/* Add the remaining panes as children. */
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == TAILQ_FIRST(&w->panes))
continue;
}
/* Add in the rows. */
layout_make_node(lccolumn, LAYOUT_TOPBOTTOM);
for (i = 0; i < rows; i++) {
/* Create and add a pane cell. */
lcchild = layout_create_cell(lccolumn);
layout_set_size(lcchild, width, height, 0, 0);
layout_make_leaf(lcchild, wp);
TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry);
/* Move to the next cell. */
if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
break;
}
/* Adjust the column to fit the full height if necessary. */
if (i == rows)
i--;
used = ((i + 1) * (height + 1)) - 1;
if (w->sy <= used)
continue;
lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells);
layout_resize_adjust(w, lcchild, LAYOUT_TOPBOTTOM,
w->sy - used);
}
/* Adjust the last column width to fit if necessary. */
used = mainwidth + (columns * width) + columns - 1;
if (w->sx > used) {
lccolumn = TAILQ_LAST(&lc->cells, layout_cells);
layout_resize_adjust(w, lccolumn, LAYOUT_LEFTRIGHT,
w->sx - used);
lcchild = layout_create_cell(lc);
layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
layout_make_leaf(lcchild, wp);
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
}
layout_spread_cell(w, lcother);
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_panes(w, w->sx, w->sy);
layout_fix_panes(w);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -421,7 +341,7 @@ layout_set_tiled(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcrow, *lcchild;
u_int n, width, height, used;
u_int n, width, height, used, sx, sy;
u_int i, j, columns, rows;
layout_print_cell(w->layout_root, __func__, 1);
@@ -450,8 +370,13 @@ layout_set_tiled(struct window *w)
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, (width + 1) * columns - 1,
(height + 1) * rows - 1, 0, 0);
sx = ((width + 1) * columns) - 1;
if (sx < w->sx)
sx = w->sx;
sy = ((height + 1) * rows) - 1;
if (sy < w->sy)
sy = w->sy;
layout_set_size(lc, sx, sy, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM);
/* Create a grid of the cells. */
@@ -511,10 +436,11 @@ layout_set_tiled(struct window *w)
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_panes(w, w->sx, w->sy);
layout_fix_panes(w);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}

215
layout.c
View File

@@ -127,6 +127,42 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
}
}
struct layout_cell *
layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
{
struct layout_cell *lcchild, *last = NULL;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (x >= lcchild->xoff && x < lcchild->xoff + lcchild->sx &&
y >= lcchild->yoff && y < lcchild->yoff + lcchild->sy) {
/* Inside the cell - recurse. */
return (layout_search_by_border(lcchild, x, y));
}
if (last == NULL) {
last = lcchild;
continue;
}
switch (lc->type) {
case LAYOUT_LEFTRIGHT:
if (x < lcchild->xoff && x >= last->xoff + last->sx)
return (last);
break;
case LAYOUT_TOPBOTTOM:
if (y < lcchild->yoff && y >= last->yoff + last->sy)
return (last);
break;
case LAYOUT_WINDOWPANE:
break;
}
last = lcchild;
}
return (NULL);
}
void
layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff,
u_int yoff)
@@ -200,7 +236,7 @@ layout_need_status(struct layout_cell *lc, int at_top)
{
struct layout_cell *first_lc;
if (lc->parent) {
if (lc->parent != NULL) {
if (lc->parent->type == LAYOUT_LEFTRIGHT)
return (layout_need_status(lc->parent, at_top));
@@ -217,71 +253,29 @@ layout_need_status(struct layout_cell *lc, int at_top)
/* Update pane offsets and sizes based on their cells. */
void
layout_fix_panes(struct window *w, u_int wsx, u_int wsy)
layout_fix_panes(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc;
u_int sx, sy;
int shift, status, at_top;
int shift, status;
status = options_get_number(w->options, "pane-border-status");
at_top = (status == 1);
TAILQ_FOREACH(wp, &w->panes, entry) {
if ((lc = wp->layout_cell) == NULL)
continue;
if (status != 0)
shift = layout_need_status(lc, at_top);
shift = layout_need_status(lc, status == 1);
else
shift = 0;
wp->xoff = lc->xoff;
wp->yoff = lc->yoff;
if (shift && at_top)
if (shift && status == 1)
wp->yoff += 1;
/*
* Layout cells are limited by the smallest size of other cells
* within the same row or column; if this isn't the case
* resizing becomes difficult.
*
* However, panes do not have to take up their entire cell, so
* they can be cropped to the window edge if the layout
* overflows and they are partly visible.
*
* This stops cells being hidden unnecessarily.
*/
/*
* Work out the horizontal size. If the pane is actually
* outside the window or the entire pane is already visible,
* don't crop.
*/
if (lc->xoff >= wsx || lc->xoff + lc->sx < wsx)
sx = lc->sx;
else {
sx = wsx - lc->xoff;
if (sx < 1)
sx = lc->sx;
}
/*
* Similarly for the vertical size; the minimum vertical size
* is two because scroll regions cannot be one line.
*/
if (lc->yoff >= wsy || lc->yoff + lc->sy < wsy)
sy = lc->sy;
else {
sy = wsy - lc->yoff;
if (sy < 2)
sy = lc->sy;
}
if (shift)
sy -= 1;
window_pane_resize(wp, sx, sy);
window_pane_resize(wp, lc->sx, lc->sy - shift);
}
}
@@ -313,7 +307,9 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
{
struct layout_cell *lcchild;
u_int available, minimum;
int status;
status = options_get_number(w->options, "pane-border-status");
if (lc->type == LAYOUT_WINDOWPANE) {
/* Space available in this cell only. */
minimum = PANE_MINIMUM;
@@ -321,9 +317,8 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
available = lc->sx;
else {
available = lc->sy;
minimum += layout_need_status(lc,
options_get_number(w->options,
"pane-border-status") == 1);
if (status != 0)
minimum += layout_need_status(lc, status == 1);
}
if (available > minimum)
available -= minimum;
@@ -455,8 +450,7 @@ layout_init(struct window *w, struct window_pane *wp)
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, w->sx, w->sy, 0, 0);
layout_make_leaf(lc, wp);
layout_fix_panes(w, w->sx, w->sy);
layout_fix_panes(w);
}
void
@@ -485,7 +479,7 @@ layout_resize(struct window *w, u_int sx, u_int sy)
* out proportionately - this should leave the layout fitting the new
* window size.
*/
xchange = sx - w->sx;
xchange = sx - lc->sx;
xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
if (xchange < 0 && xchange < -xlimit)
xchange = -xlimit;
@@ -499,7 +493,7 @@ layout_resize(struct window *w, u_int sx, u_int sy)
layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, xchange);
/* Adjust vertically in a similar fashion. */
ychange = sy - w->sy;
ychange = sy - lc->sy;
ylimit = layout_resize_check(w, lc, LAYOUT_TOPBOTTOM);
if (ychange < 0 && ychange < -ylimit)
ychange = -ylimit;
@@ -514,7 +508,7 @@ layout_resize(struct window *w, u_int sx, u_int sy)
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_panes(w, sx, sy);
layout_fix_panes(w);
}
/* Resize a pane to an absolute size. */
@@ -550,29 +544,11 @@ layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
layout_resize_pane(wp, type, change, 1);
}
/* Resize a single pane within the layout. */
void
layout_resize_pane(struct window_pane *wp, enum layout_type type, int change,
int opposite)
layout_resize_layout(struct window *w, struct layout_cell *lc,
enum layout_type type, int change, int opposite)
{
struct window *w = wp->window;
struct layout_cell *lc, *lcparent;
int needed, size;
lc = wp->layout_cell;
/* Find next parent of the same type. */
lcparent = lc->parent;
while (lcparent != NULL && lcparent->type != type) {
lc = lcparent;
lcparent = lc->parent;
}
if (lcparent == NULL)
return;
/* If this is the last cell, move back one. */
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
lc = TAILQ_PREV(lc, layout_cells, entry);
int needed, size;
/* Grow or shrink the cell. */
needed = change;
@@ -591,9 +567,34 @@ layout_resize_pane(struct window_pane *wp, enum layout_type type, int change,
}
/* Fix cell offsets. */
layout_fix_offsets(wp->window->layout_root);
layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
notify_window("window-layout-changed", wp->window);
layout_fix_offsets(w->layout_root);
layout_fix_panes(w);
notify_window("window-layout-changed", w);
}
/* Resize a single pane within the layout. */
void
layout_resize_pane(struct window_pane *wp, enum layout_type type, int change,
int opposite)
{
struct layout_cell *lc, *lcparent;
lc = wp->layout_cell;
/* Find next parent of the same type. */
lcparent = lc->parent;
while (lcparent != NULL && lcparent->type != type) {
lc = lcparent;
lcparent = lc->parent;
}
if (lcparent == NULL)
return;
/* If this is the last cell, move back one. */
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
lc = TAILQ_PREV(lc, layout_cells, entry);
layout_resize_layout(wp->window, lc, type, change, opposite);
}
/* Helper function to grow pane. */
@@ -674,7 +675,7 @@ void
layout_assign_pane(struct layout_cell *lc, struct window_pane *wp)
{
layout_make_leaf(lc, wp);
layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
layout_fix_panes(wp->window);
}
/* Calculate the new pane size for resized parent. */
@@ -721,7 +722,7 @@ layout_set_size_check(struct window *w, struct layout_cell *lc,
enum layout_type type, int size)
{
struct layout_cell *lcchild;
u_int new_size, available, previous, count, idx;
u_int new_size, available, previous, count, idx;
/* Cells with no children must just be bigger than minimum. */
if (lc->type == LAYOUT_WINDOWPANE)
@@ -735,6 +736,9 @@ layout_set_size_check(struct window *w, struct layout_cell *lc,
/* Check new size will work for each child. */
if (lc->type == type) {
if (available < (count * 2) - 1)
return (0);
if (type == LAYOUT_LEFTRIGHT)
previous = lc->sx;
else
@@ -744,13 +748,17 @@ layout_set_size_check(struct window *w, struct layout_cell *lc,
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
new_size = layout_new_pane_size(w, previous, lcchild,
type, size, count - idx, available);
if (new_size > available)
return (0);
available -= (new_size + 1);
if (idx == count - 1) {
if (new_size > available)
return (0);
available -= new_size;
} else {
if (new_size + 1 > available)
return (0);
available -= new_size + 1;
}
if (!layout_set_size_check(w, lcchild, type, new_size))
return (0);
idx++;
}
} else {
@@ -826,8 +834,9 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
int insert_before, int full_size)
{
struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
u_int sx, sy, xoff, yoff, size1, size2;
u_int sx, sy, xoff, yoff, size1, size2, minimum;
u_int new_size, saved_size, resize_first = 0;
int status;
/*
* If full_size is specified, add a new cell at the top of the window
@@ -837,6 +846,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
lc = wp->window->layout_root;
else
lc = wp->layout_cell;
status = options_get_number(wp->window->options, "pane-border-status");
/* Copy the old cell size. */
sx = lc->sx;
@@ -851,7 +861,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
return (NULL);
break;
case LAYOUT_TOPBOTTOM:
if (sy < PANE_MINIMUM * 2 + 1)
minimum = PANE_MINIMUM * 2 + 1;
if (status != 0)
minimum += layout_need_status(lc, status == 1);
if (sy < minimum)
return (NULL);
break;
default:
@@ -994,7 +1007,7 @@ layout_close_pane(struct window_pane *wp)
/* Fix pane offsets and sizes. */
if (w->layout_root != NULL) {
layout_fix_offsets(w->layout_root);
layout_fix_panes(w, w->sx, w->sy);
layout_fix_panes(w);
}
notify_window("window-layout-changed", w);
}
@@ -1003,22 +1016,29 @@ int
layout_spread_cell(struct window *w, struct layout_cell *parent)
{
struct layout_cell *lc;
u_int number, each, size;
int change, changed;
u_int number, each, size, this;
int change, changed, status;
number = 0;
TAILQ_FOREACH (lc, &parent->cells, entry)
number++;
if (number <= 1)
return (0);
status = options_get_number(w->options, "pane-border-status");
if (parent->type == LAYOUT_LEFTRIGHT)
size = parent->sx;
else if (parent->type == LAYOUT_TOPBOTTOM)
else if (parent->type == LAYOUT_TOPBOTTOM) {
size = parent->sy;
else
if (status != 0)
size -= layout_need_status(parent, status == 1);
} else
return (0);
if (size < number - 1)
return (0);
each = (size - (number - 1)) / number;
if (each == 0)
return (0);
changed = 0;
TAILQ_FOREACH (lc, &parent->cells, entry) {
@@ -1029,7 +1049,10 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
change = each - (int)lc->sx;
layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
} else if (parent->type == LAYOUT_TOPBOTTOM) {
change = each - (int)lc->sy;
this = each;
if (status != 0)
this += layout_need_status(lc, status == 1);
change = this - (int)lc->sy;
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
}
if (change != 0)
@@ -1051,7 +1074,7 @@ layout_spread_out(struct window_pane *wp)
do {
if (layout_spread_cell(w, parent)) {
layout_fix_offsets(parent);
layout_fix_panes(w, w->sx, w->sy);
layout_fix_panes(w);
break;
}
} while ((parent = parent->parent) != NULL);

View File

@@ -192,7 +192,7 @@ mode_tree_clear_tagged(struct mode_tree_list *mtl)
}
}
void
static void
mode_tree_up(struct mode_tree_data *mtd, int wrap)
{
if (mtd->current == 0) {
@@ -497,7 +497,7 @@ mode_tree_draw(struct mode_tree_data *mtd)
struct options *oo = wp->window->options;
struct screen_write_ctx ctx;
struct grid_cell gc0, gc;
u_int w, h, i, j, sy, box_x, box_y;
u_int w, h, i, j, sy, box_x, box_y, width;
char *text, *start, key[7];
const char *tag, *symbol;
size_t size, n;
@@ -530,7 +530,7 @@ mode_tree_draw(struct mode_tree_data *mtd)
line = &mtd->line_list[i];
mti = line->item;
screen_write_cursormove(&ctx, 0, i - mtd->offset);
screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
if (i < 10)
snprintf(key, sizeof key, "(%c) ", '0' + i);
@@ -572,8 +572,9 @@ mode_tree_draw(struct mode_tree_data *mtd)
tag = "*";
else
tag = "";
xasprintf(&text, "%-*s%s%s%s: %s", keylen, key, start,
mti->name, tag, mti->text);
xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name,
tag);
width = utf8_cstrwidth(text);
free(start);
if (mti->tagged) {
@@ -582,11 +583,13 @@ mode_tree_draw(struct mode_tree_data *mtd)
}
if (i != mtd->current) {
screen_write_nputs(&ctx, w, &gc0, "%s", text);
screen_write_clearendofline(&ctx, 8);
screen_write_puts(&ctx, &gc0, "%s", text);
format_draw(&ctx, &gc0, w - width, mti->text, NULL);
} else {
screen_write_nputs(&ctx, w, &gc, "%s", text);
screen_write_clearendofline(&ctx, gc.bg);
screen_write_puts(&ctx, &gc, "%s", text);
format_draw(&ctx, &gc, w - width, mti->text, NULL);
}
free(text);
@@ -605,13 +608,13 @@ mode_tree_draw(struct mode_tree_data *mtd)
line = &mtd->line_list[mtd->current];
mti = line->item;
screen_write_cursormove(&ctx, 0, h);
screen_write_cursormove(&ctx, 0, h, 0);
screen_write_box(&ctx, w, sy - h);
xasprintf(&text, " %s (sort: %s)", mti->name,
mtd->sort_list[mtd->sort_type]);
if (w - 2 >= strlen(text)) {
screen_write_cursormove(&ctx, 1, h);
screen_write_cursormove(&ctx, 1, h, 0);
screen_write_puts(&ctx, &gc0, "%s", text);
if (mtd->no_matches)
@@ -633,7 +636,7 @@ mode_tree_draw(struct mode_tree_data *mtd)
box_y = sy - h - 2;
if (box_x != 0 && box_y != 0) {
screen_write_cursormove(&ctx, 2, h + 1);
screen_write_cursormove(&ctx, 2, h + 1, 0);
mtd->drawcb(mtd->modedata, mti->itemdata, &ctx, box_x, box_y);
}

View File

@@ -35,7 +35,7 @@ struct notify_entry {
};
static void
notify_hook(struct cmdq_item *item, struct notify_entry *ne)
notify_hook1(struct cmdq_item *item, struct notify_entry *ne)
{
struct cmd_find_state fs;
struct hook *hook;
@@ -101,7 +101,7 @@ notify_callback(struct cmdq_item *item, void *data)
if (strcmp(ne->name, "session-window-changed") == 0)
control_notify_session_window_changed(ne->session);
notify_hook(item, ne);
notify_hook1(item, ne);
if (ne->client != NULL)
server_client_unref(ne->client);
@@ -153,6 +153,24 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
cmdq_append(NULL, new_item);
}
void
notify_hook(struct cmdq_item *item, const char *name)
{
struct notify_entry ne;
memset(&ne, 0, sizeof ne);
ne.name = name;
cmd_find_copy_state(&ne.fs, &item->target);
ne.client = item->client;
ne.session = item->target.s;
ne.window = item->target.w;
ne.pane = item->target.wp->id;
notify_hook1(item, &ne);
}
void
notify_input(struct window_pane *wp, struct evbuffer *input)
{

View File

@@ -38,6 +38,9 @@ static const char *options_table_mode_keys_list[] = {
static const char *options_table_clock_mode_style_list[] = {
"12", "24", NULL
};
static const char *options_table_status_list[] = {
"off", "on", "2", "3", "4", "5", NULL
};
static const char *options_table_status_keys_list[] = {
"emacs", "vi", NULL
};
@@ -59,6 +62,72 @@ static const char *options_table_pane_status_list[] = {
static const char *options_table_set_clipboard_list[] = {
"off", "external", "on", NULL
};
static const char *options_table_window_size_list[] = {
"largest", "smallest", "manual", NULL
};
/* Status line format. */
#define OPTIONS_TABLE_STATUS_FORMAT1 \
"#[align=left range=left #{status-left-style}]" \
"#{T;=/#{status-left-length}:status-left}#[norange default]" \
"#[list=on align=#{status-justify}]" \
"#[list=left-marker]<#[list=right-marker]>#[list=on]" \
"#{W:" \
"#[range=window|#{window_index} " \
"#{window-status-style}" \
"#{?#{&&:#{window_last_flag}," \
"#{!=:#{window-status-last-style},default}}, " \
"#{window-status-last-style}," \
"}" \
"#{?#{&&:#{window_bell_flag}," \
"#{!=:#{window-status-bell-style},default}}, " \
"#{window-status-bell-style}," \
"#{?#{&&:#{||:#{window_activity_flag}," \
"#{window_silence_flag}}," \
"#{!=:" \
"#{window-status-activity-style}," \
"default}}, " \
"#{window-status-activity-style}," \
"}" \
"}" \
"]" \
"#{T:window-status-format}" \
"#[norange default]" \
"#{?window_end_flag,,#{window-status-separator}}" \
"," \
"#[range=window|#{window_index} list=focus " \
"#{?#{!=:#{window-status-current-style},default}," \
"#{window-status-current-style}," \
"#{window-status-style}" \
"}" \
"#{?#{&&:#{window_last_flag}," \
"#{!=:#{window-status-last-style},default}}, " \
"#{window-status-last-style}," \
"}" \
"#{?#{&&:#{window_bell_flag}," \
"#{!=:#{window-status-bell-style},default}}, " \
"#{window-status-bell-style}," \
"#{?#{&&:#{||:#{window_activity_flag}," \
"#{window_silence_flag}}," \
"#{!=:" \
"#{window-status-activity-style}," \
"default}}, " \
"#{window-status-activity-style}," \
"}" \
"}" \
"]" \
"#{T:window-status-current-format}" \
"#[norange list=on default]" \
"#{?window_end_flag,,#{window-status-separator}}" \
"}" \
"#[nolist align=right range=right #{status-right-style}]" \
"#{T;=/#{status-right-length}:status-right}#[norange default]"
#define OPTIONS_TABLE_STATUS_FORMAT2 \
"#[align=centre]#{P:#{?pane_active,#[reverse],}" \
"#{pane_index}[#{pane_width}x#{pane_height}]#[default] }"
static const char *options_table_status_format_default[] = {
OPTIONS_TABLE_STATUS_FORMAT1, OPTIONS_TABLE_STATUS_FORMAT2, NULL
};
/* Top-level options. */
const struct options_table_entry options_table[] = {
@@ -193,6 +262,13 @@ const struct options_table_entry options_table[] = {
.default_str = _PATH_BSHELL
},
{ .name = "default-size",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.pattern = "[0-9]*x[0-9]*",
.default_str = "80x24"
},
{ .name = "destroy-unattached",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SESSION,
@@ -261,54 +337,12 @@ const struct options_table_entry options_table[] = {
.default_str = "lock -np"
},
{ .name = "message-attr",
.type = OPTIONS_TABLE_ATTRIBUTES,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 0,
.style = "message-style"
},
{ .name = "message-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 3,
.style = "message-style"
},
{ .name = "message-command-attr",
.type = OPTIONS_TABLE_ATTRIBUTES,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 0,
.style = "message-command-style"
},
{ .name = "message-command-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 0,
.style = "message-command-style"
},
{ .name = "message-command-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 3,
.style = "message-command-style"
},
{ .name = "message-command-style",
.type = OPTIONS_TABLE_STYLE,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=black,fg=yellow"
},
{ .name = "message-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 0,
.style = "message-style"
},
{ .name = "message-style",
.type = OPTIONS_TABLE_STYLE,
.scope = OPTIONS_TABLE_SESSION,
@@ -367,30 +401,28 @@ const struct options_table_entry options_table[] = {
},
{ .name = "status",
.type = OPTIONS_TABLE_FLAG,
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_status_list,
.default_num = 1
},
{ .name = "status-attr",
.type = OPTIONS_TABLE_ATTRIBUTES,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 0,
.style = "status-style"
},
{ .name = "status-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 2,
.style = "status-style"
},
{ .name = "status-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 0,
.style = "status-style"
},
{ .name = "status-format",
.type = OPTIONS_TABLE_ARRAY,
.scope = OPTIONS_TABLE_SESSION,
.default_arr = options_table_status_format_default,
},
{ .name = "status-interval",
@@ -421,27 +453,6 @@ const struct options_table_entry options_table[] = {
.default_str = "[#S] "
},
{ .name = "status-left-attr",
.type = OPTIONS_TABLE_ATTRIBUTES,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 0,
.style = "status-left-style"
},
{ .name = "status-left-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 8,
.style = "status-left-style"
},
{ .name = "status-left-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 8,
.style = "status-left-style"
},
{ .name = "status-left-length",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SESSION,
@@ -466,28 +477,9 @@ const struct options_table_entry options_table[] = {
{ .name = "status-right",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = " \"#{=21:pane_title}\" %H:%M %d-%b-%y"
},
{ .name = "status-right-attr",
.type = OPTIONS_TABLE_ATTRIBUTES,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 0,
.style = "status-right-style"
},
{ .name = "status-right-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 8,
.style = "status-right-style"
},
{ .name = "status-right-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 8,
.style = "status-right-style"
.default_str = "#{?window_bigger,"
"[#{window_offset_x}#,#{window_offset_y}] ,}"
"\"#{=21:pane_title}\" %H:%M %d-%b-%y"
},
{ .name = "status-right-length",
@@ -513,8 +505,8 @@ const struct options_table_entry options_table[] = {
{ .name = "update-environment",
.type = OPTIONS_TABLE_ARRAY,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID "
"SSH_CONNECTION WINDOWID XAUTHORITY"
.default_str = "DISPLAY KRB5CCNAME SSH_ASKPASS SSH_AUTH_SOCK "
"SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY"
},
{ .name = "visual-activity",
@@ -588,22 +580,6 @@ const struct options_table_entry options_table[] = {
.default_num = 1
},
{ .name = "force-height",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_WINDOW,
.minimum = 0,
.maximum = INT_MAX,
.default_num = 0
},
{ .name = "force-width",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_WINDOW,
.minimum = 0,
.maximum = INT_MAX,
.default_num = 0
},
{ .name = "main-pane-height",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_WINDOW,
@@ -620,27 +596,6 @@ const struct options_table_entry options_table[] = {
.default_num = 80
},
{ .name = "mode-attr",
.type = OPTIONS_TABLE_ATTRIBUTES,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 0,
.style = "mode-style"
},
{ .name = "mode-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 3,
.style = "mode-style"
},
{ .name = "mode-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 0,
.style = "mode-style"
},
{ .name = "mode-keys",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
@@ -690,20 +645,6 @@ const struct options_table_entry options_table[] = {
.default_num = 0
},
{ .name = "pane-active-border-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 8,
.style = "pane-active-border-style"
},
{ .name = "pane-active-border-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 2,
.style = "pane-active-border-style"
},
{ .name = "pane-active-border-style",
.type = OPTIONS_TABLE_STYLE,
.scope = OPTIONS_TABLE_WINDOW,
@@ -718,20 +659,6 @@ const struct options_table_entry options_table[] = {
.default_num = 0
},
{ .name = "pane-border-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 8,
.style = "pane-border-style"
},
{ .name = "pane-border-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 8,
.style = "pane-border-style"
},
{ .name = "pane-border-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
@@ -770,101 +697,31 @@ const struct options_table_entry options_table[] = {
.default_str = "default"
},
{ .name = "window-size",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_window_size_list,
.default_num = WINDOW_SIZE_SMALLEST
},
{ .name = "window-style",
.type = OPTIONS_TABLE_STYLE,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default"
},
{ .name = "window-status-activity-attr",
.type = OPTIONS_TABLE_ATTRIBUTES,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = GRID_ATTR_REVERSE,
.style = "window-status-activity-style"
},
{ .name = "window-status-activity-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 8,
.style = "window-status-activity-style"
},
{ .name = "window-status-activity-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 8,
.style = "window-status-activity-style"
},
{ .name = "window-status-activity-style",
.type = OPTIONS_TABLE_STYLE,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "reverse"
},
{ .name = "window-status-attr",
.type = OPTIONS_TABLE_ATTRIBUTES,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 0,
.style = "window-status-style"
},
{ .name = "window-status-bell-attr",
.type = OPTIONS_TABLE_ATTRIBUTES,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = GRID_ATTR_REVERSE,
.style = "window-status-bell-style"
},
{ .name = "window-status-bell-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 8,
.style = "window-status-bell-style"
},
{ .name = "window-status-bell-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 8,
.style = "window-status-bell-style"
},
{ .name = "window-status-bell-style",
.type = OPTIONS_TABLE_STYLE,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "reverse"
},
{ .name = "window-status-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 8,
.style = "window-status-style"
},
{ .name = "window-status-current-attr",
.type = OPTIONS_TABLE_ATTRIBUTES,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 0,
.style = "window-status-current-style"
},
{ .name = "window-status-current-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 8,
.style = "window-status-current-style"
},
{ .name = "window-status-current-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 8,
.style = "window-status-current-style"
},
{ .name = "window-status-current-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
@@ -877,40 +734,12 @@ const struct options_table_entry options_table[] = {
.default_str = "default"
},
{ .name = "window-status-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 8,
.style = "window-status-style"
},
{ .name = "window-status-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "#I:#W#{?window_flags,#{window_flags}, }"
},
{ .name = "window-status-last-attr",
.type = OPTIONS_TABLE_ATTRIBUTES,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 0,
.style = "window-status-last-style"
},
{ .name = "window-status-last-bg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 8,
.style = "window-status-last-style"
},
{ .name = "window-status-last-fg",
.type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 8,
.style = "window-status-last-style"
},
{ .name = "window-status-last-style",
.type = OPTIONS_TABLE_STYLE,
.scope = OPTIONS_TABLE_WINDOW,

238
options.c
View File

@@ -30,6 +30,23 @@
* a red-black tree.
*/
struct options_array_item {
u_int index;
char *value;
RB_ENTRY(options_array_item) entry;
};
RB_HEAD(options_array, options_array_item);
static int
options_array_cmp(struct options_array_item *a1, struct options_array_item *a2)
{
if (a1->index < a2->index)
return (-1);
if (a1->index > a2->index)
return (1);
return (0);
}
RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp);
struct options_entry {
struct options *owner;
@@ -39,11 +56,8 @@ struct options_entry {
union {
char *string;
long long number;
struct grid_cell style;
struct {
const char **array;
u_int arraysize;
};
struct style style;
struct options_array array;
};
RB_ENTRY(options_entry) entry;
@@ -56,8 +70,6 @@ struct options {
static struct options_entry *options_add(struct options *, const char *);
#define OPTIONS_ARRAY_LIMIT 1000
#define OPTIONS_IS_STRING(o) \
((o)->tableentry == NULL || \
(o)->tableentry->type == OPTIONS_TABLE_STRING)
@@ -66,7 +78,6 @@ static struct options_entry *options_add(struct options *, const char *);
((o)->tableentry->type == OPTIONS_TABLE_NUMBER || \
(o)->tableentry->type == OPTIONS_TABLE_KEY || \
(o)->tableentry->type == OPTIONS_TABLE_COLOUR || \
(o)->tableentry->type == OPTIONS_TABLE_ATTRIBUTES || \
(o)->tableentry->type == OPTIONS_TABLE_FLAG || \
(o)->tableentry->type == OPTIONS_TABLE_CHOICE))
#define OPTIONS_IS_STYLE(o) \
@@ -163,22 +174,30 @@ options_empty(struct options *oo, const struct options_table_entry *oe)
o = options_add(oo, oe->name);
o->tableentry = oe;
if (oe->type == OPTIONS_TABLE_ARRAY)
RB_INIT(&o->array);
return (o);
}
struct options_entry *
options_default(struct options *oo, const struct options_table_entry *oe)
{
struct options_entry *o;
struct options_entry *o;
u_int i;
o = options_empty(oo, oe);
if (oe->type == OPTIONS_TABLE_ARRAY)
options_array_assign(o, oe->default_str);
else if (oe->type == OPTIONS_TABLE_STRING)
if (oe->type == OPTIONS_TABLE_ARRAY) {
if (oe->default_arr != NULL) {
for (i = 0; oe->default_arr[i] != NULL; i++)
options_array_set(o, i, oe->default_arr[i], 0);
} else
options_array_assign(o, oe->default_str);
} else if (oe->type == OPTIONS_TABLE_STRING)
o->string = xstrdup(oe->default_str);
else if (oe->type == OPTIONS_TABLE_STYLE) {
memcpy(&o->style, &grid_default_cell, sizeof o->style);
style_parse(&grid_default_cell, &o->style, oe->default_str);
style_set(&o->style, &grid_default_cell);
style_parse(&o->style, &grid_default_cell, oe->default_str);
} else
o->number = oe->default_num;
return (o);
@@ -205,15 +224,11 @@ void
options_remove(struct options_entry *o)
{
struct options *oo = o->owner;
u_int i;
if (OPTIONS_IS_STRING(o))
free((void *)o->string);
else if (OPTIONS_IS_ARRAY(o)) {
for (i = 0; i < o->arraysize; i++)
free((void *)o->array[i]);
free(o->array);
}
free(o->string);
else if (OPTIONS_IS_ARRAY(o))
options_array_clear(o);
RB_REMOVE(options_tree, &oo->tree, o);
free(o);
@@ -231,62 +246,79 @@ options_table_entry(struct options_entry *o)
return (o->tableentry);
}
static struct options_array_item *
options_array_item(struct options_entry *o, u_int idx)
{
struct options_array_item a;
a.index = idx;
return (RB_FIND(options_array, &o->array, &a));
}
static void
options_array_free(struct options_entry *o, struct options_array_item *a)
{
free(a->value);
RB_REMOVE(options_array, &o->array, a);
free(a);
}
void
options_array_clear(struct options_entry *o)
{
if (OPTIONS_IS_ARRAY(o))
o->arraysize = 0;
struct options_array_item *a, *a1;
if (!OPTIONS_IS_ARRAY(o))
return;
RB_FOREACH_SAFE(a, options_array, &o->array, a1)
options_array_free(o, a);
}
const char *
options_array_get(struct options_entry *o, u_int idx)
{
struct options_array_item *a;
if (!OPTIONS_IS_ARRAY(o))
return (NULL);
if (idx >= o->arraysize)
a = options_array_item(o, idx);
if (a == NULL)
return (NULL);
return (o->array[idx]);
return (a->value);
}
int
options_array_set(struct options_entry *o, u_int idx, const char *value,
int append)
{
char *new;
u_int i;
struct options_array_item *a;
char *new;
if (!OPTIONS_IS_ARRAY(o))
return (-1);
if (idx >= OPTIONS_ARRAY_LIMIT)
return (-1);
if (idx >= o->arraysize) {
o->array = xreallocarray(o->array, idx + 1, sizeof *o->array);
for (i = o->arraysize; i < idx + 1; i++)
o->array[i] = NULL;
o->arraysize = idx + 1;
a = options_array_item(o, idx);
if (value == NULL) {
if (a != NULL)
options_array_free(o, a);
return (0);
}
new = NULL;
if (value != NULL) {
if (o->array[idx] != NULL && append)
xasprintf(&new, "%s%s", o->array[idx], value);
if (a == NULL) {
a = xcalloc(1, sizeof *a);
a->index = idx;
a->value = xstrdup(value);
RB_INSERT(options_array, &o->array, a);
} else {
free(a->value);
if (a != NULL && append)
xasprintf(&new, "%s%s", a->value, value);
else
new = xstrdup(value);
a->value = new;
}
free((void *)o->array[idx]);
o->array[idx] = new;
return (0);
}
int
options_array_size(struct options_entry *o, u_int *size)
{
if (!OPTIONS_IS_ARRAY(o))
return (-1);
if (size != NULL)
*size = o->arraysize;
return (0);
}
@@ -305,37 +337,69 @@ options_array_assign(struct options_entry *o, const char *s)
while ((next = strsep(&string, separator)) != NULL) {
if (*next == '\0')
continue;
for (i = 0; i < OPTIONS_ARRAY_LIMIT; i++) {
if (i >= o->arraysize || o->array[i] == NULL)
for (i = 0; i < UINT_MAX; i++) {
if (options_array_item(o, i) == NULL)
break;
}
if (i == OPTIONS_ARRAY_LIMIT)
if (i == UINT_MAX)
break;
options_array_set(o, i, next, 0);
}
free(copy);
}
struct options_array_item *
options_array_first(struct options_entry *o)
{
if (!OPTIONS_IS_ARRAY(o))
return (NULL);
return (RB_MIN(options_array, &o->array));
}
struct options_array_item *
options_array_next(struct options_array_item *a)
{
return (RB_NEXT(options_array, &o->array, a));
}
u_int
options_array_item_index(struct options_array_item *a)
{
return (a->index);
}
const char *
options_array_item_value(struct options_array_item *a)
{
return (a->value);
}
int
options_isarray(struct options_entry *o)
{
return (OPTIONS_IS_ARRAY(o));
}
int
options_isstring(struct options_entry *o)
{
if (o->tableentry == NULL)
return (1);
return (OPTIONS_IS_STRING(o) || OPTIONS_IS_ARRAY(o));
}
const char *
options_tostring(struct options_entry *o, int idx, int numeric)
{
static char s[1024];
const char *tmp;
static char s[1024];
const char *tmp;
struct options_array_item *a;
if (OPTIONS_IS_ARRAY(o)) {
if (idx == -1)
return (NULL);
if ((u_int)idx >= o->arraysize || o->array[idx] == NULL)
a = options_array_item(o, idx);
if (a == NULL)
return ("");
return (o->array[idx]);
return (a->value);
}
if (OPTIONS_IS_STYLE(o))
return (style_tostring(&o->style));
@@ -351,9 +415,6 @@ options_tostring(struct options_entry *o, int idx, int numeric)
case OPTIONS_TABLE_COLOUR:
tmp = colour_tostring(o->number);
break;
case OPTIONS_TABLE_ATTRIBUTES:
tmp = attributes_tostring(o->number);
break;
case OPTIONS_TABLE_FLAG:
if (numeric)
xsnprintf(s, sizeof s, "%lld", o->number);
@@ -504,7 +565,7 @@ options_get_number(struct options *oo, const char *name)
return (o->number);
}
const struct grid_cell *
struct style *
options_get_style(struct options *oo, const char *name)
{
struct options_entry *o;
@@ -576,17 +637,17 @@ options_set_style(struct options *oo, const char *name, int append,
const char *value)
{
struct options_entry *o;
struct grid_cell gc;
struct style sy;
if (*name == '@')
fatalx("user option %s must be a string", name);
o = options_get_only(oo, name);
if (o != NULL && append && OPTIONS_IS_STYLE(o))
memcpy(&gc, &o->style, sizeof gc);
style_copy(&sy, &o->style);
else
memcpy(&gc, &grid_default_cell, sizeof gc);
if (style_parse(&grid_default_cell, &gc, value) == -1)
style_set(&sy, &grid_default_cell);
if (style_parse(&sy, &grid_default_cell, value) == -1)
return (NULL);
if (o == NULL) {
o = options_default(oo, options_parent_table_entry(oo, name));
@@ -596,7 +657,7 @@ options_set_style(struct options *oo, const char *name, int append,
if (!OPTIONS_IS_STYLE(o))
fatalx("option %s is not a style", name);
memcpy(&o->style, &gc, sizeof o->style);
style_copy(&o->style, &sy);
return (o);
}
@@ -643,44 +704,3 @@ options_scope_from_flags(struct args *args, int window,
return (OPTIONS_TABLE_SESSION);
}
}
void
options_style_update_new(struct options *oo, struct options_entry *o)
{
const char *newname = o->tableentry->style;
struct options_entry *new;
if (newname == NULL)
return;
new = options_get_only(oo, newname);
if (new == NULL)
new = options_set_style(oo, newname, 0, "default");
if (strstr(o->name, "-bg") != NULL)
new->style.bg = o->number;
else if (strstr(o->name, "-fg") != NULL)
new->style.fg = o->number;
else if (strstr(o->name, "-attr") != NULL)
new->style.attr = o->number;
}
void
options_style_update_old(struct options *oo, struct options_entry *o)
{
char newname[128];
int size;
size = strrchr(o->name, '-') - o->name;
xsnprintf(newname, sizeof newname, "%.*s-bg", size, o->name);
if (options_get(oo, newname) != NULL)
options_set_number(oo, newname, o->style.bg);
xsnprintf(newname, sizeof newname, "%.*s-fg", size, o->name);
if (options_get(oo, newname) != NULL)
options_set_number(oo, newname, o->style.fg);
xsnprintf(newname, sizeof newname, "%.*s-attr", size, o->name);
if (options_get(oo, newname) != NULL)
options_set_number(oo, newname, o->style.attr);
}

View File

@@ -31,6 +31,8 @@
#include <unistd.h>
#include <libutil.h>
#include "compat.h"
struct kinfo_proc *cmp_procs(struct kinfo_proc *, struct kinfo_proc *);
char *osdep_get_name(int, char *);
char *osdep_get_cwd(int);

View File

@@ -23,14 +23,17 @@
#include <errno.h>
#include <event.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
#define is_runnable(p) \
((p)->p_stat == LSRUN || (p)->p_stat == SIDL)
((p)->p_stat == LSRUN || (p)->p_stat == SIDL)
#define is_stopped(p) \
((p)->p_stat == SSTOP || (p)->p_stat == SZOMB)
((p)->p_stat == SSTOP || (p)->p_stat == SZOMB)
struct kinfo_proc2 *cmp_procs(struct kinfo_proc2 *, struct kinfo_proc2 *);
char *osdep_get_name(int, char *);
@@ -129,6 +132,22 @@ error:
char *
osdep_get_cwd(int fd)
{
static char target[PATH_MAX + 1];
char *path;
pid_t pgrp;
ssize_t n;
if ((pgrp = tcgetpgrp(fd)) == -1)
return (NULL);
xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp);
n = readlink(path, target, sizeof(target) - 1);
free(path);
if (n > 0) {
target[n] = '\0';
return (target);
}
return (NULL);
}

View File

@@ -7,3 +7,4 @@ all: $(TESTS)
$(TESTS):
sh $*.sh
sleep 1

View File

@@ -13,37 +13,37 @@ $TMUX kill-server 2>/dev/null
TMP=$(mktemp)
OUT=$(mktemp)
#trap "rm -f $TMP $OUT" 0 1 15
trap "rm -f $TMP $OUT" 0 1 15
$TMUX -f/dev/null new -d || exit 1
sleep 1
cat <<EOF|$TMUX -C a >$TMP
ls -F':#{session_width} #{session_height}'
ls -F':#{window_width} #{window_height}'
refresh -C 100,50
ls -F':#{session_width} #{session_height}'
ls -F':#{window_width} #{window_height}'
EOF
grep ^: $TMP >$OUT
printf ":80 24\n:100 50\n"|cmp -s $OUT - || exit 1
printf ":80 24\n:100 49\n"|cmp -s $OUT - || exit 1
$TMUX kill-server 2>/dev/null
$TMUX -f/dev/null new -d || exit 1
sleep 1
cat <<EOF|$TMUX -C a >$TMP
ls -F':#{session_width} #{session_height}'
cat <<EOF|$TMUX -f/dev/null -C a >$TMP
ls -F':#{window_width} #{window_height}'
refresh -C 80,24
ls -F':#{session_width} #{session_height}'
ls -F':#{window_width} #{window_height}'
EOF
grep ^: $TMP >$OUT
printf ":80 24\n:80 24\n"|cmp -s $OUT - || exit 1
printf ":80 24\n:80 23\n"|cmp -s $OUT - || exit 1
$TMUX kill-server 2>/dev/null
cat <<EOF|$TMUX -C new -x 100 -y 50 >$TMP
ls -F':#{session_width} #{session_height}'
cat <<EOF|$TMUX -f/dev/null -C new -x 100 -y 50 >$TMP
ls -F':#{window_width} #{window_height}'
refresh -C 80,24
ls -F':#{session_width} #{session_height}'
ls -F':#{window_width} #{window_height}'
EOF
grep ^: $TMP >$OUT
printf ":100 50\n:80 24\n"|cmp -s $OUT - || exit 1
printf ":100 50\n:80 23\n"|cmp -s $OUT - || exit 1
$TMUX kill-server 2>/dev/null
exit 0

6
regress/cursor-test.txt Normal file
View File

@@ -0,0 +1,6 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum.

View File

@@ -0,0 +1,33 @@
14 8 t
0 ud exercitation ullamco laboris nisi ut
1 aliquip ex ea
2 commodo consequat. Duis aute
3 irure dolor in reprehenderit in voluptat
4 e velit esse cillum dolore eu fugiat
5 nulla pariatur. Excepteur sint occaecat
6 cupidatat non proident, sunt in culpa qu
7 i officia
8 deserunt mollit anim id est laborum.
9
4 6 t
0 cupidatat
1 non proide
2 nt, sunt i
3 n culpa qu
4 i officia
5 deserunt m
6 ollit anim
7 id est la
8 borum.
9
14 8 t
0 incididunt ut labore et dolore magna aliqua. Ut en
1 im ad minim veniam, quis nostrud exercitation ulla
2 mco laboris nisi ut aliquip ex ea
3 commodo consequat. Duis aute
4 irure dolor in reprehenderit in voluptate velit es
5 se cillum dolore eu fugiat
6 nulla pariatur. Excepteur sint occaecat cupidatat
7 non proident, sunt in culpa qui officia
8 deserunt mollit anim id est laborum.
9

29
regress/cursor-test1.sh Normal file
View File

@@ -0,0 +1,29 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -f/dev/null -Ltest"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
$TMUX -f/dev/null new -d -x40 -y10 \
"cat cursor-test.txt; printf '\e[9;15H'; cat" || exit 1
$TMUX set -g window-size manual || exit 1
$TMUX display -pF '#{cursor_x} #{cursor_y} #{cursor_character}' >>$TMP
$TMUX capturep -p|awk '{print NR-1,$0}' >>$TMP
$TMUX resizew -x10 || exit 1
$TMUX display -pF '#{cursor_x} #{cursor_y} #{cursor_character}' >>$TMP
$TMUX capturep -p|awk '{print NR-1,$0}' >>$TMP
$TMUX resizew -x50 || exit 1
$TMUX display -pF '#{cursor_x} #{cursor_y} #{cursor_character}' >>$TMP
$TMUX capturep -p|awk '{print NR-1,$0}' >>$TMP
cmp -s $TMP cursor-test1.result || exit 1
$TMUX kill-server 2>/dev/null
exit 0

View File

@@ -0,0 +1,33 @@
9 7 a
0 cupidatat
1 non proide
2 nt, sunt i
3 n culpa qu
4 i officia
5 deserunt m
6 ollit anim
7 id est la
8 borum.
9
4 6 a
0 icia
1 deser
2 unt m
3 ollit
4 anim
5 id e
6 st la
7 borum
8 .
9
29 8 a
0 incididunt ut labore et dolore magna aliqua. Ut en
1 im ad minim veniam, quis nostrud exercitation ulla
2 mco laboris nisi ut aliquip ex ea
3 commodo consequat. Duis aute
4 irure dolor in reprehenderit in voluptate velit es
5 se cillum dolore eu fugiat
6 nulla pariatur. Excepteur sint occaecat cupidatat
7 non proident, sunt in culpa qui officia
8 deserunt mollit anim id est laborum.
9

29
regress/cursor-test2.sh Normal file
View File

@@ -0,0 +1,29 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
$TMUX -f/dev/null new -d -x10 -y10 \
"cat cursor-test.txt; printf '\e[8;10H'; cat" || exit 1
$TMUX set -g window-size manual || exit 1
$TMUX display -pF '#{cursor_x} #{cursor_y} #{cursor_character}' >>$TMP
$TMUX capturep -p|awk '{print NR-1,$0}' >>$TMP
$TMUX resizew -x5 || exit 1
$TMUX display -pF '#{cursor_x} #{cursor_y} #{cursor_character}' >>$TMP
$TMUX capturep -p|awk '{print NR-1,$0}' >>$TMP
$TMUX resizew -x50 || exit 1
$TMUX display -pF '#{cursor_x} #{cursor_y} #{cursor_character}' >>$TMP
$TMUX capturep -p|awk '{print NR-1,$0}' >>$TMP
cmp -s $TMP cursor-test2.result || exit 1
$TMUX kill-server 2>/dev/null
exit 0

View File

@@ -0,0 +1,9 @@
6 1 b
0 abcdefa
1 bcdefab
3 1 b
0 fabcd
1 efab
6 1 b
0 abcdefa
1 bcdefab

29
regress/cursor-test3.sh Normal file
View File

@@ -0,0 +1,29 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
$TMUX -f/dev/null new -d -x7 -y2 \
"printf 'abcdefabcdefab'; printf '\e[2;7H'; cat" || exit 1
$TMUX set -g window-size manual || exit 1
$TMUX display -pF '#{cursor_x} #{cursor_y} #{cursor_character}' >>$TMP
$TMUX capturep -p|awk '{print NR-1,$0}' >>$TMP
$TMUX resizew -x5 || exit 1
$TMUX display -pF '#{cursor_x} #{cursor_y} #{cursor_character}' >>$TMP
$TMUX capturep -p|awk '{print NR-1,$0}' >>$TMP
$TMUX resizew -x7 || exit 1
$TMUX display -pF '#{cursor_x} #{cursor_y} #{cursor_character}' >>$TMP
$TMUX capturep -p|awk '{print NR-1,$0}' >>$TMP
cmp -s $TMP cursor-test3.result || exit 1
$TMUX kill-server 2>/dev/null
exit 0

View File

@@ -0,0 +1,16 @@
0 1
0 abcdef
1
2
0 1
0 abcdef
1
2
0 1
0 def
1
2
0 1
0 abcdef
1
2

31
regress/cursor-test4.sh Normal file
View File

@@ -0,0 +1,31 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
$TMUX -f/dev/null new -d -x10 -y3 "printf 'abcdef\n'; cat" || exit 1
$TMUX set -g window-size manual || exit 1
$TMUX display -pF '#{cursor_x} #{cursor_y} #{cursor_character}' >>$TMP
$TMUX capturep -p|awk '{print NR-1,$0}' >>$TMP
$TMUX resizew -x20 || exit 1
$TMUX display -pF '#{cursor_x} #{cursor_y} #{cursor_character}' >>$TMP
$TMUX capturep -p|awk '{print NR-1,$0}' >>$TMP
$TMUX resizew -x3 || exit 1
$TMUX display -pF '#{cursor_x} #{cursor_y} #{cursor_character}' >>$TMP
$TMUX capturep -p|awk '{print NR-1,$0}' >>$TMP
$TMUX resizew -x10 || exit 1
$TMUX display -pF '#{cursor_x} #{cursor_y} #{cursor_character}' >>$TMP
$TMUX capturep -p|awk '{print NR-1,$0}' >>$TMP
cmp -s $TMP cursor-test4.result || exit 1
$TMUX kill-server 2>/dev/null
exit 0

183
regress/format-strings.sh Normal file
View File

@@ -0,0 +1,183 @@
#!/bin/sh
# Tests of formats as described in tmux(1) FORMATS
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
# test_format $format $expected_result
test_format()
{
fmt="$1"
exp="$2"
out=$($TMUX display-message -p "$fmt")
if [ "$out" != "$exp" ]; then
echo "Format test failed for '$fmt'."
echo "Expected: '$exp'"
echo "But got '$out'"
exit 1
fi
}
# test_conditional_with_pane_in_mode $format $exp1 $exp2
#
# Tests the format string $format to yield $exp1 if #{pane_in_mode} is true and
# $exp2 when #{pane_in_mode} is false.
test_conditional_with_pane_in_mode()
{
fmt="$1"
exp_true="$2"
exp_false="$3"
$TMUX copy-mode # enter copy mode
test_format "$fmt" "$exp_true"
$TMUX send-keys -X cancel # leave copy mode
test_format "$fmt" "$exp_false"
}
# test_conditional_with_session_name #format $exp_summer $exp_winter
#
# Tests the format string $format to yield $exp_summer if the session name is
# 'Summer' and $exp_winter if the session name is 'Winter'.
test_conditional_with_session_name()
{
fmt="$1"
exp_summer="$2"
exp_winter="$3"
$TMUX rename-session "Summer"
test_format "$fmt" "$exp_summer"
$TMUX rename-session "Winter"
test_format "$fmt" "$exp_winter"
$TMUX rename-session "Summer" # restore default
}
$TMUX kill-server 2>/dev/null
$TMUX -f/dev/null new-session -d || exit 1
$TMUX rename-session "Summer" || exit 1 # used later in conditionals
# Plain string without substitutions et al
test_format "abc xyz" "abc xyz"
# Test basic escapes for "#", "{", "#{" "}", "#}", ","
test_format "##" "#"
test_format "#," ","
test_format "{" "{"
test_format "##{" "#{"
test_format "#}" "}"
test_format "###}" "#}" # not a "basic" one but interesting nevertheless
# Simple expansion
test_format "#{pane_in_mode}" "0"
# Simple conditionals
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,xyz}" "abc" "xyz"
# Expansion in conditionals
test_conditional_with_pane_in_mode "#{?pane_in_mode,#{session_name},xyz}" "Summer" "xyz"
# Basic escapes in conditionals
# First argument
test_conditional_with_pane_in_mode "#{?pane_in_mode,##,xyz}" "#" "xyz"
test_conditional_with_pane_in_mode "#{?pane_in_mode,#,,xyz}" "," "xyz"
test_conditional_with_pane_in_mode "#{?pane_in_mode,{,xyz}" "{" "xyz"
test_conditional_with_pane_in_mode "#{?pane_in_mode,##{,xyz}" "#{" "xyz"
test_conditional_with_pane_in_mode "#{?pane_in_mode,#},xyz}" "}" "xyz"
# not a "basic" one but interesting nevertheless
test_conditional_with_pane_in_mode "#{?pane_in_mode,###},xyz}" "#}" "xyz"
# Second argument
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,##}" "abc" "#"
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,#,}" "abc" ","
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,{}" "abc" "{"
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,##{}" "abc" "#{"
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,#}}" "abc" "}"
# not a "basic" one but interesting nevertheless
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,###}}" "abc" "#}"
# mixed
test_conditional_with_pane_in_mode "#{?pane_in_mode,{,#}}" "{" "}"
test_conditional_with_pane_in_mode "#{?pane_in_mode,#},{}" "}" "{"
test_conditional_with_pane_in_mode "#{?pane_in_mode,##{,###}}" "#{" "#}"
test_conditional_with_pane_in_mode "#{?pane_in_mode,###},##{}" "#}" "#{"
# Conditionals split on the second comma (this is not documented)
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,xyz,bonus}" "abc" "xyz,bonus"
# Curly brackets {...} do not capture a comma inside of conditionals as the
# conditional ends on the first '}'
test_conditional_with_pane_in_mode "#{?pane_in_mode,{abc,xyz},bonus}" "{abc,bonus}" "xyz,bonus}"
# Substitutions '#{...}' capture the comma
# invalid format: #{abc,xyz} is not a known variable name.
#test_conditional_with_pane_in_mode "#{?pane_in_mode,#{abc,xyz},bonus}" "" "bonus"
# Parenthesis (...) do not captura a comma
test_conditional_with_pane_in_mode "#{?pane_in_mode,(abc,xyz),bonus}" "(abc" "xyz),bonus"
test_conditional_with_pane_in_mode "#{?pane_in_mode,(abc#,xyz),bonus}" "(abc,xyz)" "bonus"
# Brackets [...] do not captura a comma
test_conditional_with_pane_in_mode "#{?pane_in_mode,[abc,xyz],bonus}" "[abc" "xyz],bonus"
test_conditional_with_pane_in_mode "#{?pane_in_mode,[abc#,xyz],bonus}" "[abc,xyz]" "bonus"
# Escape comma inside of #(...)
# Note: #() commands are run asynchronous and are substituted with result of the
# *previous* run or a placeholder (like "<'echo ,' not ready") if the command
# has not been run before. The format is updated as soon as the command
# finishes. As we are printing the message only once it never gets updated
# and the displayed message is "<'echo ,' not ready>"
test_format "#{?pane_in_mode,#(echo #,),xyz}" "xyz"
test_conditional_with_pane_in_mode "#{?pane_in_mode,#(echo #,),xyz}" "<'echo ,' not ready>" "xyz"
# This caching does not work :-(
#$TMUX display-message -p "#(echo #,)" > /dev/null
#test_conditional_with_pane_in_mode "#{?pane_in_mode,#(echo #,),xyz}" "," "xyz"
#test_conditional_with_pane_in_mode "#{?pane_in_mode,#(echo #,),xyz}" "," "xyz"
# invalid format: '#(' is not closed in the first argument of #{?,,}.
#test_conditional_with_pane_in_mode "#{?pane_in_mode,#(echo ,),xyz}" "" "),xyz"
# Escape comma inside of #[...]
test_conditional_with_pane_in_mode "#{?pane_in_mode,#[fg=default#,bg=default]abc,xyz}" "#[fg=default,bg=default]abc" "xyz"
# invalid format: '#[' is not closed in the first argument of #{?,,}
#test_conditional_with_pane_in_mode "#{?pane_in_mode,#[fg=default,bg=default]abc,xyz}" "" "bg=default]abc,xyz"
# Conditionals with comparison
test_conditional_with_session_name "#{?#{==:#{session_name},Summer},abc,xyz}" "abc" "xyz"
# Conditionals with comparison and escaped commas
$TMUX rename-session ","
test_format "#{?#{==:#,,#{session_name}},abc,xyz}" "abc"
$TMUX rename-session "Summer" # reset to default
# Conditional in conditional
test_conditional_with_pane_in_mode "#{?pane_in_mode,#{?#{==:#{session_name},Summer},ABC,XYZ},xyz}" "ABC" "xyz"
test_conditional_with_session_name "#{?pane_in_mode,#{?#{==:#{session_name},Summer},ABC,XYZ},xyz}" "xyz" "xyz"
test_conditional_with_pane_in_mode "#{?pane_in_mode,abc,#{?#{==:#{session_name},Summer},ABC,XYZ}}" "abc" "ABC"
test_conditional_with_session_name "#{?pane_in_mode,abc,#{?#{==:#{session_name},Summer},ABC,XYZ}}" "ABC" "XYZ"
# Some fancy stackings
test_conditional_with_pane_in_mode "#{?#{==:#{?pane_in_mode,#{session_name},#(echo Spring)},Summer},abc,xyz}" "abc" "xyz"
# Format test for the literal option
# Note: The behavior for #{l:...} with escapes is sometimes weird as #{l:...}
# respects the escapes.
test_format "#{l:#{}}" "#{}"
test_format "#{l:#{pane_in_mode}}" "#{pane_in_mode}"
test_format "#{l:#{?pane_in_mode,#{?#{==:#{session_name},Summer},ABC,XYZ},xyz}}" "#{?pane_in_mode,#{?#{==:#{session_name},Summer},ABC,XYZ},xyz}"
# With escapes (which escape but are returned literally)
test_format "#{l:##{}" "##{"
test_format "#{l:#{#}}}" "#{#}}"
# Invalid formats:
#test_format "#{l:#{}" ""
#test_format "#{l:#{#}}" ""
exit 0

View File

@@ -9,9 +9,9 @@ TERM=screen
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
$TMUX new -d 'sleep 1000' || exit 1
$TMUX -f/dev/null new -d 'sleep 1000' || exit 1
P=$($TMUX display -pt0:0.0 '#{pane_pid}')
$TMUX new -d || exit 1
$TMUX -f/dev/null new -d || exit 1
sleep 1
$TMUX kill-session -t0:
sleep 1

View File

@@ -14,13 +14,13 @@ trap "rm -f $TMP" 0 1 15
$TMUX -f/dev/null new -d </dev/null || exit 1
sleep 1
$TMUX ls -F "#{session_width} #{session_height}" >$TMP
$TMUX ls -F "#{window_width} #{window_height}" >$TMP
printf "80 24\n"|cmp -s $TMP - || exit 1
$TMUX kill-server 2>/dev/null
$TMUX -f/dev/null new -d -x 100 -y 50 </dev/null || exit 1
sleep 1
$TMUX ls -F "#{session_width} #{session_height}" >$TMP
$TMUX ls -F "#{window_width} #{window_height}" >$TMP
printf "100 50\n"|cmp -s $TMP - || exit 1
$TMUX kill-server 2>/dev/null

341
resize.c
View File

@@ -22,151 +22,234 @@
#include "tmux.h"
/*
* Recalculate window and session sizes.
*
* Every session has the size of the smallest client it is attached to and
* every window the size of the smallest session it is attached to.
*
* So, when a client is resized or a session attached to or detached from a
* client, the window sizes must be recalculated. For each session, find the
* smallest client it is attached to, and resize it to that size. Then for
* every window, find the smallest session it is attached to, resize it to that
* size and clear and redraw every client with it as the current window.
*
* This is quite inefficient - better/additional data structures are needed
* to make it better.
*
* As a side effect, this function updates the SESSION_UNATTACHED flag. This
* flag is necessary to make sure unattached sessions do not limit the size of
* windows that are attached both to them and to other (attached) sessions.
*/
void
resize_window(struct window *w, u_int sx, u_int sy)
{
int zoomed;
/* Check size limits. */
if (sx < WINDOW_MINIMUM)
sx = WINDOW_MINIMUM;
if (sx > WINDOW_MAXIMUM)
sx = WINDOW_MAXIMUM;
if (sy < WINDOW_MINIMUM)
sy = WINDOW_MINIMUM;
if (sy > WINDOW_MAXIMUM)
sy = WINDOW_MAXIMUM;
/* If the window is zoomed, unzoom. */
zoomed = w->flags & WINDOW_ZOOMED;
if (zoomed)
window_unzoom(w);
/* Resize the layout first. */
layout_resize(w, sx, sy);
/* Resize the window, it can be no smaller than the layout. */
if (sx < w->layout_root->sx)
sx = w->layout_root->sx;
if (sy < w->layout_root->sy)
sy = w->layout_root->sy;
window_resize(w, sx, sy);
log_debug("%s: @%u resized to %u,%u; layout %u,%u", __func__, w->id,
sx, sy, w->layout_root->sx, w->layout_root->sy);
/* Restore the window zoom state. */
if (zoomed)
window_zoom(w->active);
tty_update_window_offset(w);
server_redraw_window(w);
notify_window("window-layout-changed", w);
}
static int
ignore_client_size(struct client *c)
{
if (c->session == NULL)
return (1);
if (c->flags & CLIENT_NOSIZEFLAGS)
return (1);
if ((c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_SIZECHANGED))
return (1);
return (0);
}
void
default_window_size(struct session *s, struct window *w, u_int *sx, u_int *sy,
int type)
{
struct client *c;
u_int cx, cy;
const char *value;
if (type == -1)
type = options_get_number(global_w_options, "window-size");
if (type == WINDOW_SIZE_MANUAL)
goto manual;
if (type == WINDOW_SIZE_LARGEST) {
*sx = *sy = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
if (w != NULL && !session_has(c->session, w))
continue;
if (w == NULL && c->session != s)
continue;
cx = c->tty.sx;
cy = c->tty.sy - status_line_size(c);
if (cx > *sx)
*sx = cx;
if (cy > *sy)
*sy = cy;
}
if (*sx == 0 || *sy == 0)
goto manual;
} else {
*sx = *sy = UINT_MAX;
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
if (w != NULL && !session_has(c->session, w))
continue;
if (w == NULL && c->session != s)
continue;
cx = c->tty.sx;
cy = c->tty.sy - status_line_size(c);
if (cx < *sx)
*sx = cx;
if (cy < *sy)
*sy = cy;
}
if (*sx == UINT_MAX || *sy == UINT_MAX)
goto manual;
}
goto done;
manual:
value = options_get_string(s->options, "default-size");
if (sscanf(value, "%ux%u", sx, sy) != 2) {
*sx = 80;
*sy = 24;
}
done:
if (*sx < WINDOW_MINIMUM)
*sx = WINDOW_MINIMUM;
if (*sx > WINDOW_MAXIMUM)
*sx = WINDOW_MAXIMUM;
if (*sy < WINDOW_MINIMUM)
*sy = WINDOW_MINIMUM;
if (*sy > WINDOW_MAXIMUM)
*sy = WINDOW_MAXIMUM;
}
void
recalculate_sizes(void)
{
struct session *s;
struct client *c;
struct window *w;
struct window_pane *wp;
u_int ssx, ssy, has, limit, lines;
int flag, is_zoomed, forced;
struct session *s;
struct client *c;
struct window *w;
u_int sx, sy, cx, cy;
int type, current, has, changed;
/*
* Clear attached count and update saved status line information for
* each session.
*/
RB_FOREACH(s, sessions, &sessions) {
lines = status_line_size(s);
s->attached = 0;
ssx = ssy = UINT_MAX;
TAILQ_FOREACH(c, &clients, entry) {
if (c->flags & CLIENT_SUSPENDED)
continue;
if ((c->flags & (CLIENT_CONTROL|CLIENT_SIZECHANGED)) ==
CLIENT_CONTROL)
continue;
if (c->session == s) {
if (c->tty.sx < ssx)
ssx = c->tty.sx;
c->flags &= ~CLIENT_STATUSOFF;
if (lines != 0 && lines + PANE_MINIMUM > c->tty.sy)
c->flags |= CLIENT_STATUSOFF;
if ((~c->flags & CLIENT_STATUSOFF) &&
!(c->flags & CLIENT_CONTROL) &&
c->tty.sy > lines &&
c->tty.sy - lines < ssy)
ssy = c->tty.sy - lines;
else if (c->tty.sy < ssy)
ssy = c->tty.sy;
s->attached++;
}
}
if (ssx == UINT_MAX || ssy == UINT_MAX) {
s->flags |= SESSION_UNATTACHED;
continue;
}
s->flags &= ~SESSION_UNATTACHED;
if (lines != 0 && ssy == 0)
ssy = lines;
if (s->sx == ssx && s->sy == ssy)
continue;
log_debug("session $%u size %u,%u (was %u,%u)", s->id, ssx, ssy,
s->sx, s->sy);
s->sx = ssx;
s->sy = ssy;
status_update_saved(s);
status_update_cache(s);
}
/*
* Increment attached count and check the status line size for each
* client.
*/
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
if (c->tty.sy <= status_line_size(c))
c->flags |= CLIENT_STATUSOFF;
else
c->flags &= ~CLIENT_STATUSOFF;
c->session->attached++;
}
/* Walk each window and adjust the size. */
RB_FOREACH(w, windows, &windows) {
if (w->active == NULL)
continue;
flag = options_get_number(w->options, "aggressive-resize");
log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy);
ssx = ssy = UINT_MAX;
RB_FOREACH(s, sessions, &sessions) {
if (s->flags & SESSION_UNATTACHED)
continue;
if (flag)
has = s->curw->window == w;
else
has = session_has(s, w);
if (has) {
if (s->sx < ssx)
ssx = s->sx;
if (s->sy < ssy)
ssy = s->sy;
type = options_get_number(w->options, "window-size");
if (type == WINDOW_SIZE_MANUAL)
continue;
current = options_get_number(w->options, "aggressive-resize");
changed = 1;
if (type == WINDOW_SIZE_LARGEST) {
sx = sy = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
s = c->session;
if (current)
has = (s->curw->window == w);
else
has = session_has(s, w);
if (!has)
continue;
cx = c->tty.sx;
cy = c->tty.sy - status_line_size(c);
if (cx > sx)
sx = cx;
if (cy > sy)
sy = cy;
}
if (sx == 0 || sy == 0)
changed = 0;
} else {
sx = sy = UINT_MAX;
TAILQ_FOREACH(c, &clients, entry) {
if (ignore_client_size(c))
continue;
s = c->session;
if (current)
has = (s->curw->window == w);
else
has = session_has(s, w);
if (!has)
continue;
cx = c->tty.sx;
cy = c->tty.sy - status_line_size(c);
if (cx < sx)
sx = cx;
if (cy < sy)
sy = cy;
}
if (sx == UINT_MAX || sy == UINT_MAX)
changed = 0;
}
if (ssx == UINT_MAX || ssy == UINT_MAX)
if (w->sx == sx && w->sy == sy)
changed = 0;
if (!changed) {
tty_update_window_offset(w);
continue;
forced = 0;
limit = options_get_number(w->options, "force-width");
if (limit >= PANE_MINIMUM && ssx > limit) {
ssx = limit;
forced |= WINDOW_FORCEWIDTH;
}
limit = options_get_number(w->options, "force-height");
if (limit >= PANE_MINIMUM && ssy > limit) {
ssy = limit;
forced |= WINDOW_FORCEHEIGHT;
}
if (w->sx == ssx && w->sy == ssy)
continue;
log_debug("window @%u size %u,%u (was %u,%u)", w->id, ssx, ssy,
w->sx, w->sy);
w->flags &= ~(WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT);
w->flags |= forced;
is_zoomed = w->flags & WINDOW_ZOOMED;
if (is_zoomed)
window_unzoom(w);
layout_resize(w, ssx, ssy);
window_resize(w, ssx, ssy);
if (is_zoomed && window_pane_visible(w->active))
window_zoom(w->active);
/*
* If the current pane is now not visible, move to the next
* that is.
*/
wp = w->active;
while (!window_pane_visible(w->active)) {
w->active = TAILQ_PREV(w->active, window_panes, entry);
if (w->active == NULL)
w->active = TAILQ_LAST(&w->panes, window_panes);
if (w->active == wp)
break;
}
if (w->active == w->last)
w->last = NULL;
server_redraw_window(w);
notify_window("window-layout-changed", w);
log_debug("%s: @%u changed to %u,%u", __func__, w->id, sx, sy);
resize_window(w, sx, sy);
}
}

View File

@@ -23,22 +23,27 @@
#include "tmux.h"
static int screen_redraw_cell_border1(struct window_pane *, u_int, u_int);
static int screen_redraw_cell_border(struct client *, u_int, u_int);
static int screen_redraw_check_cell(struct client *, u_int, u_int, int,
struct window_pane **);
static int screen_redraw_check_is(u_int, u_int, int, int, struct window *,
struct window_pane *, struct window_pane *);
struct screen_redraw_ctx {
struct client *c;
static int screen_redraw_make_pane_status(struct client *, struct window *,
u_int lines;
int top;
int pane_status;
u_int sx;
u_int sy;
u_int ox;
u_int oy;
};
static void screen_redraw_draw_borders(struct screen_redraw_ctx *);
static void screen_redraw_draw_panes(struct screen_redraw_ctx *);
static void screen_redraw_draw_status(struct screen_redraw_ctx *);
static void screen_redraw_draw_pane(struct screen_redraw_ctx *,
struct window_pane *);
static void screen_redraw_draw_number(struct screen_redraw_ctx *,
struct window_pane *);
static void screen_redraw_draw_pane_status(struct client *, int);
static void screen_redraw_draw_borders(struct client *, int, u_int, u_int);
static void screen_redraw_draw_panes(struct client *, u_int, u_int);
static void screen_redraw_draw_status(struct client *, u_int, u_int);
static void screen_redraw_draw_number(struct client *, struct window_pane *,
u_int, u_int);
#define CELL_INSIDE 0
#define CELL_LEFTRIGHT 1
@@ -269,8 +274,8 @@ screen_redraw_make_pane_status(struct client *c, struct window *w,
struct grid_cell gc;
const char *fmt;
struct format_tree *ft;
char *out;
size_t outlen;
char *expanded;
u_int width, i;
struct screen_write_ctx ctx;
struct screen old;
@@ -284,27 +289,30 @@ screen_redraw_make_pane_status(struct client *c, struct window *w,
ft = format_create(c, NULL, FORMAT_PANE|wp->id, 0);
format_defaults(ft, c, NULL, NULL, wp);
expanded = format_expand_time(ft, fmt);
if (wp->sx < 4)
wp->status_size = width = 0;
else
wp->status_size = width = wp->sx - 4;
memcpy(&old, &wp->status_screen, sizeof old);
screen_init(&wp->status_screen, wp->sx, 1, 0);
screen_init(&wp->status_screen, width, 1, 0);
wp->status_screen.mode = 0;
out = format_expand(ft, fmt);
outlen = screen_write_cstrlen("%s", out);
if (outlen > wp->sx - 4)
outlen = wp->sx - 4;
screen_resize(&wp->status_screen, outlen, 1, 0);
screen_write_start(&ctx, NULL, &wp->status_screen);
screen_write_cursormove(&ctx, 0, 0);
screen_write_clearline(&ctx, 8);
screen_write_cnputs(&ctx, outlen, &gc, "%s", out);
gc.attr |= GRID_ATTR_CHARSET;
for (i = 0; i < width; i++)
screen_write_putc(&ctx, &gc, 'q');
gc.attr &= ~GRID_ATTR_CHARSET;
screen_write_cursormove(&ctx, 0, 0, 0);
format_draw(&ctx, &gc, width, expanded, NULL);
screen_write_stop(&ctx);
free(out);
free(expanded);
format_free(ft);
wp->status_size = outlen;
if (grid_compare(wp->status_screen.grid, old.grid) == 0) {
screen_free(&old);
return (0);
@@ -315,35 +323,67 @@ screen_redraw_make_pane_status(struct client *c, struct window *w,
/* Draw pane status. */
static void
screen_redraw_draw_pane_status(struct client *c, int pane_status)
screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
{
struct client *c = ctx->c;
struct window *w = c->session->curw->window;
struct options *oo = c->session->options;
struct tty *tty = &c->tty;
struct window_pane *wp;
int spos;
u_int yoff;
struct screen *s;
u_int i, x, width, xoff, yoff, size;
log_debug("%s: %s @%u", __func__, c->name, w->id);
spos = options_get_number(oo, "status-position");
TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_visible(wp))
continue;
if (pane_status == CELL_STATUS_TOP)
s = &wp->status_screen;
size = wp->status_size;
if (ctx->pane_status == CELL_STATUS_TOP)
yoff = wp->yoff - 1;
else
yoff = wp->yoff + wp->sy;
if (spos == 0)
yoff += 1;
xoff = wp->xoff + 2;
tty_draw_line(tty, NULL, &wp->status_screen, 0, wp->xoff + 2,
yoff);
if (xoff + size <= ctx->ox ||
xoff >= ctx->ox + ctx->sx ||
yoff < ctx->oy ||
yoff >= ctx->oy + ctx->sy)
continue;
if (xoff >= ctx->ox && xoff + size <= ctx->ox + ctx->sx) {
/* All visible. */
i = 0;
x = xoff - ctx->ox;
width = size;
} else if (xoff < ctx->ox && xoff + size > ctx->ox + ctx->sx) {
/* Both left and right not visible. */
i = ctx->ox;
x = 0;
width = ctx->sx;
} else if (xoff < ctx->ox) {
/* Left not visible. */
i = ctx->ox - xoff;
x = 0;
width = size - i;
} else {
/* Right not visible. */
i = 0;
x = xoff - ctx->ox;
width = size - x;
}
if (ctx->top)
yoff += ctx->lines;
tty_draw_line(tty, NULL, s, i, 0, width, x, yoff - ctx->oy);
}
tty_cursor(tty, 0, 0);
}
/* Update status line and change flags if unchanged. */
void
screen_redraw_update(struct client *c)
static int
screen_redraw_update(struct client *c, int flags)
{
struct window *w = c->session->curw->window;
struct window_pane *wp;
@@ -356,8 +396,8 @@ screen_redraw_update(struct client *c)
redraw = status_prompt_redraw(c);
else
redraw = status_redraw(c);
if (!redraw)
c->flags &= ~CLIENT_STATUS;
if (!redraw && (~flags & CLIENT_REDRAWSTATUSALWAYS))
flags &= ~CLIENT_REDRAWSTATUS;
if (options_get_number(wo, "pane-border-status") != CELL_STATUS_OFF) {
redraw = 0;
@@ -366,119 +406,128 @@ screen_redraw_update(struct client *c)
redraw = 1;
}
if (redraw)
c->flags |= CLIENT_BORDERS;
flags |= CLIENT_REDRAWBORDERS;
}
return (flags);
}
/* Set up redraw context. */
static void
screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx)
{
struct session *s = c->session;
struct options *oo = s->options;
struct window *w = s->curw->window;
struct options *wo = w->options;
memset(ctx, 0, sizeof *ctx);
ctx->c = c;
ctx->lines = status_line_size(c);
if (c->message_string != NULL || c->prompt_string != NULL)
ctx->lines = (ctx->lines == 0) ? 1 : ctx->lines;
if (ctx->lines != 0 && options_get_number(oo, "status-position") == 0)
ctx->top = 1;
ctx->pane_status = options_get_number(wo, "pane-border-status");
tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy);
log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name,
w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->lines, ctx->top);
}
/* Redraw entire screen. */
void
screen_redraw_screen(struct client *c, int draw_panes, int draw_status,
int draw_borders)
screen_redraw_screen(struct client *c)
{
struct options *oo = c->session->options;
struct tty *tty = &c->tty;
struct window *w = c->session->curw->window;
struct options *wo = w->options;
u_int top, lines;
int position, pane_status;
struct screen_redraw_ctx ctx;
int flags;
if (c->flags & CLIENT_SUSPENDED)
return;
if (c->flags & CLIENT_STATUSOFF)
lines = 0;
else
lines = status_line_size(c->session);
if (c->message_string != NULL || c->prompt_string != NULL)
lines = (lines == 0) ? 1 : lines;
flags = screen_redraw_update(c, c->flags);
screen_redraw_set_context(c, &ctx);
position = options_get_number(oo, "status-position");
if (lines != 0 && position == 0)
top = 1;
else
top = 0;
if (lines == 0)
draw_status = 0;
if (draw_borders) {
pane_status = options_get_number(wo, "pane-border-status");
screen_redraw_draw_borders(c, pane_status, lines, top);
if (pane_status != CELL_STATUS_OFF)
screen_redraw_draw_pane_status(c, pane_status);
if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) {
if (ctx.pane_status != CELL_STATUS_OFF)
screen_redraw_draw_pane_status(&ctx);
screen_redraw_draw_borders(&ctx);
}
if (draw_panes)
screen_redraw_draw_panes(c, lines, top);
if (draw_status)
screen_redraw_draw_status(c, lines, top);
tty_reset(tty);
if (flags & CLIENT_REDRAWWINDOW)
screen_redraw_draw_panes(&ctx);
if (ctx.lines != 0 &&
(flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS)))
screen_redraw_draw_status(&ctx);
tty_reset(&c->tty);
}
/* Draw a single pane. */
/* Redraw a single pane. */
void
screen_redraw_pane(struct client *c, struct window_pane *wp)
{
u_int i, yoff;
struct screen_redraw_ctx ctx;
if (!window_pane_visible(wp))
return;
yoff = wp->yoff;
if (status_at_line(c) == 0)
yoff += status_line_size(c->session);
screen_redraw_set_context(c, &ctx);
log_debug("%s: redraw pane %%%u (at %u,%u)", c->name, wp->id,
wp->xoff, yoff);
for (i = 0; i < wp->sy; i++)
tty_draw_pane(&c->tty, wp, i, wp->xoff, yoff);
screen_redraw_draw_pane(&ctx, wp);
tty_reset(&c->tty);
}
/* Draw a border cell. */
static void
screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j,
struct grid_cell *m_active_gc, struct grid_cell *active_gc,
struct grid_cell *m_other_gc, struct grid_cell *other_gc)
{
struct client *c = ctx->c;
struct session *s = c->session;
struct window *w = s->curw->window;
struct tty *tty = &c->tty;
struct window_pane *wp;
struct window_pane *active = w->active;
struct window_pane *marked = marked_pane.wp;
u_int type, x = ctx->ox + i, y = ctx->oy + j;
int flag, pane_status = ctx->pane_status;
type = screen_redraw_check_cell(c, x, y, pane_status, &wp);
if (type == CELL_INSIDE)
return;
flag = screen_redraw_check_is(x, y, type, pane_status, w, active, wp);
if (server_is_marked(s, s->curw, marked_pane.wp) &&
screen_redraw_check_is(x, y, type, pane_status, w, marked, wp)) {
if (flag)
tty_attributes(tty, m_active_gc, NULL);
else
tty_attributes(tty, m_other_gc, NULL);
} else if (flag)
tty_attributes(tty, active_gc, NULL);
else
tty_attributes(tty, other_gc, NULL);
if (ctx->top)
tty_cursor(tty, i, ctx->lines + j);
else
tty_cursor(tty, i, j);
tty_putc(tty, CELL_BORDERS[type]);
}
/* Draw the borders. */
static void
screen_redraw_draw_borders(struct client *c, int pane_status, u_int lines,
u_int top)
screen_redraw_draw_borders(struct screen_redraw_ctx *ctx)
{
struct client *c = ctx->c;
struct session *s = c->session;
struct window *w = s->curw->window;
struct options *oo = w->options;
struct tty *tty = &c->tty;
struct window_pane *wp;
struct options *oo = w->options;
struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc;
struct grid_cell msg_gc;
u_int i, j, type, msgx = 0, msgy = 0;
int active, small, flags;
char msg[256];
const char *tmp;
size_t msglen = 0;
u_int i, j;
small = (tty->sy - lines + top > w->sy) || (tty->sx > w->sx);
if (small) {
flags = w->flags & (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT);
if (flags == (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT))
tmp = "force-width, force-height";
else if (flags == WINDOW_FORCEWIDTH)
tmp = "force-width";
else if (flags == WINDOW_FORCEHEIGHT)
tmp = "force-height";
else if (c->flags & CLIENT_STATUSOFF)
tmp = "status line";
else
tmp = "a smaller client";
xsnprintf(msg, sizeof msg, "(size %ux%u from %s)",
w->sx, w->sy, tmp);
msglen = strlen(msg);
if (tty->sy - 1 - lines + top > w->sy && tty->sx >= msglen) {
msgx = tty->sx - msglen;
msgy = tty->sy - 1 - lines + top;
} else if (tty->sx - w->sx > msglen) {
msgx = tty->sx - msglen;
msgy = tty->sy - 1 - lines + top;
} else
small = 0;
}
log_debug("%s: %s @%u", __func__, c->name, w->id);
style_apply(&other_gc, oo, "pane-border-style");
style_apply(&active_gc, oo, "pane-active-border-style");
@@ -489,114 +538,181 @@ screen_redraw_draw_borders(struct client *c, int pane_status, u_int lines,
memcpy(&m_active_gc, &active_gc, sizeof m_active_gc);
m_active_gc.attr ^= GRID_ATTR_REVERSE;
for (j = 0; j < tty->sy - lines; j++) {
for (j = 0; j < tty->sy - ctx->lines; j++) {
for (i = 0; i < tty->sx; i++) {
type = screen_redraw_check_cell(c, i, j, pane_status,
&wp);
if (type == CELL_INSIDE)
continue;
if (type == CELL_OUTSIDE && small &&
i > msgx && j == msgy)
continue;
active = screen_redraw_check_is(i, j, type, pane_status,
w, w->active, wp);
if (server_is_marked(s, s->curw, marked_pane.wp) &&
screen_redraw_check_is(i, j, type, pane_status, w,
marked_pane.wp, wp)) {
if (active)
tty_attributes(tty, &m_active_gc, NULL);
else
tty_attributes(tty, &m_other_gc, NULL);
} else if (active)
tty_attributes(tty, &active_gc, NULL);
else
tty_attributes(tty, &other_gc, NULL);
if (top)
tty_cursor(tty, i, lines + j);
else
tty_cursor(tty, i, j);
tty_putc(tty, CELL_BORDERS[type]);
screen_redraw_draw_borders_cell(ctx, i, j,
&m_active_gc, &active_gc, &m_other_gc, &other_gc);
}
}
if (small) {
memcpy(&msg_gc, &grid_default_cell, sizeof msg_gc);
tty_attributes(tty, &msg_gc, NULL);
tty_cursor(tty, msgx, msgy);
tty_puts(tty, msg);
}
}
/* Draw the panes. */
static void
screen_redraw_draw_panes(struct client *c, u_int lines, u_int top)
screen_redraw_draw_panes(struct screen_redraw_ctx *ctx)
{
struct client *c = ctx->c;
struct window *w = c->session->curw->window;
struct tty *tty = &c->tty;
struct window_pane *wp;
u_int i, y;
if (top)
y = lines;
else
y = 0;
log_debug("%s: %s @%u", __func__, c->name, w->id);
TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_visible(wp))
continue;
for (i = 0; i < wp->sy; i++)
tty_draw_pane(tty, wp, i, wp->xoff, y + wp->yoff);
screen_redraw_draw_pane(ctx, wp);
if (c->flags & CLIENT_IDENTIFY)
screen_redraw_draw_number(c, wp, lines, top);
screen_redraw_draw_number(ctx, wp);
}
}
/* Draw the status line. */
static void
screen_redraw_draw_status(struct client *c, u_int lines, u_int top)
screen_redraw_draw_status(struct screen_redraw_ctx *ctx)
{
struct client *c = ctx->c;
struct window *w = c->session->curw->window;
struct tty *tty = &c->tty;
struct screen *s = c->status.active;
u_int i, y;
if (top)
log_debug("%s: %s @%u", __func__, c->name, w->id);
if (ctx->top)
y = 0;
else
y = tty->sy - lines;
for (i = 0; i < lines; i++)
tty_draw_line(tty, NULL, &c->status.status, i, 0, y);
y = c->tty.sy - ctx->lines;
for (i = 0; i < ctx->lines; i++)
tty_draw_line(tty, NULL, s, 0, i, UINT_MAX, 0, y + i);
}
/* Draw one pane. */
static void
screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
{
struct client *c = ctx->c;
struct window *w = c->session->curw->window;
struct tty *tty = &c->tty;
struct screen *s;
u_int i, j, top, x, y, width;
log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id);
if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx)
return;
if (ctx->top)
top = ctx->lines;
else
top = 0;
s = wp->screen;
for (j = 0; j < wp->sy; j++) {
if (wp->yoff + j < ctx->oy || wp->yoff + j >= ctx->oy + ctx->sy)
continue;
y = top + wp->yoff + j - ctx->oy;
if (wp->xoff >= ctx->ox &&
wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
/* All visible. */
i = 0;
x = wp->xoff - ctx->ox;
width = wp->sx;
} else if (wp->xoff < ctx->ox &&
wp->xoff + wp->sx > ctx->ox + ctx->sx) {
/* Both left and right not visible. */
i = ctx->ox;
x = 0;
width = ctx->sx;
} else if (wp->xoff < ctx->ox) {
/* Left not visible. */
i = ctx->ox - wp->xoff;
x = 0;
width = wp->sx - i;
} else {
/* Right not visible. */
i = 0;
x = wp->xoff - ctx->ox;
width = ctx->sx - x;
}
log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u",
__func__, c->name, wp->id, i, j, x, y, width);
tty_draw_line(tty, wp, s, i, j, width, x, y);
}
}
/* Draw number on a pane. */
static void
screen_redraw_draw_number(struct client *c, struct window_pane *wp,
u_int lines, u_int top)
screen_redraw_draw_number(struct screen_redraw_ctx *ctx, struct window_pane *wp)
{
struct client *c = ctx->c;
struct tty *tty = &c->tty;
struct session *s = c->session;
struct options *oo = s->options;
struct window *w = wp->window;
struct grid_cell gc;
u_int idx, px, py, i, j, xoff, yoff;
u_int idx, px, py, i, j, xoff, yoff, sx, sy;
int colour, active_colour;
char buf[16], *ptr;
size_t len;
if (wp->xoff + wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + ctx->sx ||
wp->yoff + wp->sy <= ctx->oy ||
wp->yoff >= ctx->oy + ctx->sy)
return;
if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
/* All visible. */
xoff = wp->xoff - ctx->ox;
sx = wp->sx;
} else if (wp->xoff < ctx->ox &&
wp->xoff + wp->sx > ctx->ox + ctx->sx) {
/* Both left and right not visible. */
xoff = 0;
sx = ctx->sx;
} else if (wp->xoff < ctx->ox) {
/* Left not visible. */
xoff = 0;
sx = wp->sx - (ctx->ox - wp->xoff);
} else {
/* Right not visible. */
xoff = wp->xoff - ctx->ox;
sx = wp->sx - xoff;
}
if (wp->yoff >= ctx->oy && wp->yoff + wp->sy <= ctx->oy + ctx->sy) {
/* All visible. */
yoff = wp->yoff - ctx->oy;
sy = wp->sy;
} else if (wp->yoff < ctx->oy &&
wp->yoff + wp->sy > ctx->oy + ctx->sy) {
/* Both top and bottom not visible. */
yoff = 0;
sy = ctx->sy;
} else if (wp->yoff < ctx->oy) {
/* Top not visible. */
yoff = 0;
sy = wp->sy - (ctx->oy - wp->yoff);
} else {
/* Bottom not visible. */
yoff = wp->yoff - ctx->oy;
sy = wp->sy - yoff;
}
if (ctx->top)
yoff += ctx->lines;
px = sx / 2;
py = sy / 2;
if (window_pane_index(wp, &idx) != 0)
fatalx("index not found");
len = xsnprintf(buf, sizeof buf, "%u", idx);
if (wp->sx < len)
if (sx < len)
return;
colour = options_get_number(oo, "display-panes-colour");
active_colour = options_get_number(oo, "display-panes-active-colour");
px = wp->sx / 2; py = wp->sy / 2;
xoff = wp->xoff; yoff = wp->yoff;
if (top)
yoff += lines;
if (wp->sx < len * 6 || wp->sy < 5) {
if (sx < len * 6 || sy < 5) {
tty_cursor(tty, xoff + px - len / 2, yoff + py);
goto draw_text;
}
@@ -628,9 +744,9 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp,
}
len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy);
if (wp->sx < len || wp->sy < 6)
if (sx < len || sy < 6)
return;
tty_cursor(tty, xoff + wp->sx - len, yoff);
tty_cursor(tty, xoff + sx - len, yoff);
draw_text:
memcpy(&gc, &grid_default_cell, sizeof gc);

View File

@@ -54,12 +54,53 @@ struct screen_write_collect_line {
TAILQ_HEAD(, screen_write_collect_item) items;
};
static void
screen_write_offset_timer(__unused int fd, __unused short events, void *data)
{
struct window *w = data;
tty_update_window_offset(w);
}
/* Set cursor position. */
static void
screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy)
{
struct window_pane *wp = ctx->wp;
struct window *w;
struct screen *s = ctx->s;
struct timeval tv = { .tv_usec = 10000 };
if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy)
return;
if (cx != -1) {
if ((u_int)cx > screen_size_x(s)) /* allow last column */
cx = screen_size_x(s) - 1;
s->cx = cx;
}
if (cy != -1) {
if ((u_int)cy > screen_size_y(s) - 1)
cy = screen_size_y(s) - 1;
s->cy = cy;
}
if (wp == NULL)
return;
w = wp->window;
if (!event_initialized(&w->offset_timer))
evtimer_set(&w->offset_timer, screen_write_offset_timer, w);
if (!evtimer_pending(&w->offset_timer, NULL))
evtimer_add(&w->offset_timer, &tv);
}
/* Initialize writing with a window. */
void
screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp,
struct screen *s)
{
char tmp[16];
char tmp[32];
u_int y;
memset(ctx, 0, sizeof *ctx);
@@ -78,8 +119,10 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp,
ctx->scrolled = 0;
ctx->bg = 8;
if (wp != NULL)
snprintf(tmp, sizeof tmp, "pane %%%u", wp->id);
if (wp != NULL) {
snprintf(tmp, sizeof tmp, "pane %%%u (at %u,%u)", wp->id,
wp->xoff, wp->yoff);
}
log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s),
screen_size_y(ctx->s), wp == NULL ? "no pane" : tmp);
}
@@ -107,11 +150,10 @@ 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_INSERT|MODE_KCURSOR|MODE_KKEYPAD|MODE_FOCUSON);
s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_UTF8|MODE_MOUSE_SGR);
s->mode = MODE_CURSOR | MODE_WRAP;
screen_write_clearscreen(ctx, 8);
screen_write_cursormove(ctx, 0, 0);
screen_write_set_cursor(ctx, 0, 0);
}
/* Write character. */
@@ -127,41 +169,6 @@ screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
screen_write_cell(ctx, &gc);
}
/* Calculate string length, with embedded formatting. */
size_t
screen_write_cstrlen(const char *fmt, ...)
{
va_list ap;
char *msg, *msg2, *ptr, *ptr2;
size_t size;
va_start(ap, fmt);
xvasprintf(&msg, fmt, ap);
va_end(ap);
msg2 = xmalloc(strlen(msg) + 1);
ptr = msg;
ptr2 = msg2;
while (*ptr != '\0') {
if (ptr[0] == '#' && ptr[1] == '[') {
while (*ptr != ']' && *ptr != '\0')
ptr++;
if (*ptr == ']')
ptr++;
continue;
}
*ptr2++ = *ptr++;
}
*ptr2 = '\0';
size = screen_write_strlen("%s", msg2);
free(msg);
free(msg2);
return (size);
}
/* Calculate string length. */
size_t
screen_write_strlen(const char *fmt, ...)
@@ -280,77 +287,6 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
free(msg);
}
/* Write string, similar to nputs, but with embedded formatting (#[]). */
void
screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
const struct grid_cell *gcp, const char *fmt, ...)
{
struct grid_cell gc;
struct utf8_data *ud = &gc.data;
va_list ap;
char *msg;
u_char *ptr, *last;
size_t left, size = 0;
enum utf8_state more;
memcpy(&gc, gcp, sizeof gc);
va_start(ap, fmt);
xvasprintf(&msg, fmt, ap);
va_end(ap);
ptr = msg;
while (*ptr != '\0') {
if (ptr[0] == '#' && ptr[1] == '[') {
ptr += 2;
last = ptr + strcspn(ptr, "]");
if (*last == '\0') {
/* No ]. Not much point in doing anything. */
break;
}
*last = '\0';
style_parse(gcp, &gc, ptr);
ptr = last + 1;
continue;
}
if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) {
ptr++;
left = strlen(ptr);
if (left < (size_t)ud->size - 1)
break;
while ((more = utf8_append(ud, *ptr)) == UTF8_MORE)
ptr++;
ptr++;
if (more != UTF8_DONE)
continue;
if (maxlen > 0 && size + ud->width > (size_t)maxlen) {
while (size < (size_t)maxlen) {
screen_write_putc(ctx, &gc, ' ');
size++;
}
break;
}
size += ud->width;
screen_write_cell(ctx, &gc);
} else {
if (maxlen > 0 && size + 1 > (size_t)maxlen)
break;
if (*ptr > 0x1f && *ptr < 0x7f) {
size++;
screen_write_putc(ctx, &gc, *ptr);
}
ptr++;
}
}
free(msg);
}
/* Copy from another screen. Assumes target region is big enough. */
void
screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px,
@@ -382,13 +318,13 @@ screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px,
screen_write_cell(ctx, &gc);
}
cy++;
screen_write_cursormove(ctx, cx, cy);
screen_write_set_cursor(ctx, cx, cy);
}
}
/*
* Copy from another screen but without the selection stuff. Also assumes the
* target region is already big enough and already cleared.
* target region is already big enough.
*/
void
screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
@@ -408,13 +344,12 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
break;
cx = s->cx;
for (xx = px; xx < px + nx; xx++) {
if (xx >= gd->linedata[yy].cellsize)
if (xx >= grid_get_line(gd, yy)->cellsize)
break;
grid_get_cell(gd, xx, yy, &gc);
if (xx + gc.data.width > px + nx)
break;
if (!grid_cells_equal(&gc, &grid_default_cell))
grid_view_set_cell(ctx->s->grid, cx, cy, &gc);
grid_view_set_cell(ctx->s->grid, cx, cy, &gc);
cx++;
}
cy++;
@@ -440,7 +375,7 @@ screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right)
screen_write_putc(ctx, &gc, 'q');
screen_write_putc(ctx, &gc, right ? 'u' : 'q');
screen_write_cursormove(ctx, cx, cy);
screen_write_set_cursor(ctx, cx, cy);
}
/* Draw a horizontal line on screen. */
@@ -459,13 +394,13 @@ screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
screen_write_putc(ctx, &gc, top ? 'w' : 'x');
for (i = 1; i < ny - 1; i++) {
screen_write_cursormove(ctx, cx, cy + i);
screen_write_set_cursor(ctx, cx, cy + i);
screen_write_putc(ctx, &gc, 'x');
}
screen_write_cursormove(ctx, cx, cy + ny - 1);
screen_write_set_cursor(ctx, cx, cy + ny - 1);
screen_write_putc(ctx, &gc, bottom ? 'v' : 'x');
screen_write_cursormove(ctx, cx, cy);
screen_write_set_cursor(ctx, cx, cy);
}
/* Draw a box on screen. */
@@ -487,22 +422,22 @@ screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny)
screen_write_putc(ctx, &gc, 'q');
screen_write_putc(ctx, &gc, 'k');
screen_write_cursormove(ctx, cx, cy + ny - 1);
screen_write_set_cursor(ctx, cx, cy + ny - 1);
screen_write_putc(ctx, &gc, 'm');
for (i = 1; i < nx - 1; i++)
screen_write_putc(ctx, &gc, 'q');
screen_write_putc(ctx, &gc, 'j');
for (i = 1; i < ny - 1; i++) {
screen_write_cursormove(ctx, cx, cy + i);
screen_write_set_cursor(ctx, cx, cy + i);
screen_write_putc(ctx, &gc, 'x');
}
for (i = 1; i < ny - 1; i++) {
screen_write_cursormove(ctx, cx + nx - 1, cy + i);
screen_write_set_cursor(ctx, cx + nx - 1, cy + i);
screen_write_putc(ctx, &gc, 'x');
}
screen_write_cursormove(ctx, cx, cy);
screen_write_set_cursor(ctx, cx, cy);
}
/*
@@ -557,7 +492,7 @@ screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
if (src->mode & MODE_CURSOR) {
grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
gc.attr |= GRID_ATTR_REVERSE;
screen_write_cursormove(ctx, cx + (src->cx - px),
screen_write_set_cursor(ctx, cx + (src->cx - px),
cy + (src->cy - py));
screen_write_cell(ctx, &gc);
}
@@ -603,25 +538,26 @@ void
screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
{
struct screen *s = ctx->s;
u_int cx = s->cx, cy = s->cy;
if (ny == 0)
ny = 1;
if (s->cy < s->rupper) {
if (cy < s->rupper) {
/* Above region. */
if (ny > s->cy)
ny = s->cy;
if (ny > cy)
ny = cy;
} else {
/* Below region. */
if (ny > s->cy - s->rupper)
ny = s->cy - s->rupper;
if (ny > cy - s->rupper)
ny = cy - s->rupper;
}
if (s->cx == screen_size_x(s))
s->cx--;
if (ny == 0)
return;
if (cx == screen_size_x(s))
cx--;
s->cy -= ny;
cy -= ny;
screen_write_set_cursor(ctx, cx, cy);
}
/* Cursor down by ny. */
@@ -629,25 +565,28 @@ void
screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
{
struct screen *s = ctx->s;
u_int cx = s->cx, cy = s->cy;
if (ny == 0)
ny = 1;
if (s->cy > s->rlower) {
if (cy > s->rlower) {
/* Below region. */
if (ny > screen_size_y(s) - 1 - s->cy)
ny = screen_size_y(s) - 1 - s->cy;
if (ny > screen_size_y(s) - 1 - cy)
ny = screen_size_y(s) - 1 - cy;
} else {
/* Above region. */
if (ny > s->rlower - s->cy)
ny = s->rlower - s->cy;
if (ny > s->rlower - cy)
ny = s->rlower - cy;
}
if (s->cx == screen_size_x(s))
s->cx--;
if (ny == 0)
if (cx == screen_size_x(s))
cx--;
else if (ny == 0)
return;
s->cy += ny;
cy += ny;
screen_write_set_cursor(ctx, cx, cy);
}
/* Cursor right by nx. */
@@ -655,16 +594,19 @@ void
screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
{
struct screen *s = ctx->s;
u_int cx = s->cx, cy = s->cy;
if (nx == 0)
nx = 1;
if (nx > screen_size_x(s) - 1 - s->cx)
nx = screen_size_x(s) - 1 - s->cx;
if (nx > screen_size_x(s) - 1 - cx)
nx = screen_size_x(s) - 1 - cx;
if (nx == 0)
return;
s->cx += nx;
cx += nx;
screen_write_set_cursor(ctx, cx, cy);
}
/* Cursor left by nx. */
@@ -672,16 +614,19 @@ void
screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx)
{
struct screen *s = ctx->s;
u_int cx = s->cx, cy = s->cy;
if (nx == 0)
nx = 1;
if (nx > s->cx)
nx = s->cx;
if (nx > cx)
nx = cx;
if (nx == 0)
return;
s->cx -= nx;
cx -= nx;
screen_write_set_cursor(ctx, cx, cy);
}
/* Backspace; cursor left unless at start of wrapped line when can move up. */
@@ -690,17 +635,20 @@ screen_write_backspace(struct screen_write_ctx *ctx)
{
struct screen *s = ctx->s;
struct grid_line *gl;
u_int cx = s->cx, cy = s->cy;
if (s->cx == 0) {
if (s->cy == 0)
if (cx == 0) {
if (cy == 0)
return;
gl = &s->grid->linedata[s->grid->hsize + s->cy - 1];
gl = grid_get_line(s->grid, s->grid->hsize + cy - 1);
if (gl->flags & GRID_LINE_WRAPPED) {
s->cy--;
s->cx = screen_size_x(s) - 1;
cy--;
cx = screen_size_x(s) - 1;
}
} else
s->cx--;
cx--;
screen_write_set_cursor(ctx, cx, cy);
}
/* VT100 alignment test. */
@@ -712,8 +660,6 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
struct grid_cell gc;
u_int xx, yy;
screen_write_initctx(ctx, &ttyctx);
memcpy(&gc, &grid_default_cell, sizeof gc);
utf8_set(&gc.data, 'E');
@@ -722,12 +668,13 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
grid_view_set_cell(s->grid, xx, yy, &gc);
}
s->cx = 0;
s->cy = 0;
screen_write_set_cursor(ctx, 0, 0);
s->rupper = 0;
s->rlower = screen_size_y(s) - 1;
screen_write_initctx(ctx, &ttyctx);
screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1);
tty_write(tty_cmd_alignmenttest, &ttyctx);
}
@@ -917,8 +864,8 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s);
gl = &s->grid->linedata[s->grid->hsize + s->cy];
if (gl->cellsize == 0 && bg == 8)
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
return;
screen_write_initctx(ctx, &ttyctx);
@@ -940,8 +887,8 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx;
u_int sx = screen_size_x(s);
gl = &s->grid->linedata[s->grid->hsize + s->cy];
if (s->cx > sx - 1 || (s->cx >= gl->cellsize && bg == 8))
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
return;
screen_write_initctx(ctx, &ttyctx);
@@ -979,17 +926,24 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
/* Move cursor to px,py. */
void
screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py)
screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
int origin)
{
struct screen *s = ctx->s;
if (px > screen_size_x(s) - 1)
if (origin && py != -1 && (s->mode & MODE_ORIGIN)) {
if ((u_int)py > s->rlower - s->rupper)
py = s->rlower;
else
py += s->rupper;
}
if (px != -1 && (u_int)px > screen_size_x(s) - 1)
px = screen_size_x(s) - 1;
if (py > screen_size_y(s) - 1)
if (py != -1 && (u_int)py > screen_size_y(s) - 1)
py = screen_size_y(s) - 1;
s->cx = px;
s->cy = py;
screen_write_set_cursor(ctx, px, py);
}
/* Reverse index (up with scroll). */
@@ -1005,7 +959,7 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
if (s->cy == s->rupper)
grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
else if (s->cy > 0)
s->cy--;
screen_write_set_cursor(ctx, -1, s->cy - 1);
screen_write_collect_flush(ctx, 0);
tty_write(tty_cmd_reverseindex, &ttyctx);
@@ -1028,8 +982,7 @@ screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper,
screen_write_collect_flush(ctx, 0);
/* Cursor moves to top-left. */
s->cx = 0;
s->cy = 0;
screen_write_set_cursor(ctx, 0, 0);
s->rupper = rupper;
s->rlower = rlower;
@@ -1043,7 +996,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
struct grid *gd = s->grid;
struct grid_line *gl;
gl = &gd->linedata[gd->hsize + s->cy];
gl = grid_get_line(gd, gd->hsize + s->cy);
if (wrapped)
gl->flags |= GRID_LINE_WRAPPED;
else
@@ -1062,7 +1015,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
screen_write_collect_scroll(ctx);
ctx->scrolled++;
} else if (s->cy < screen_size_y(s) - 1)
s->cy++;
screen_write_set_cursor(ctx, -1, s->cy + 1);
}
/* Scroll up. */
@@ -1094,9 +1047,7 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
void
screen_write_carriagereturn(struct screen_write_ctx *ctx)
{
struct screen *s = ctx->s;
s->cx = 0;
screen_write_set_cursor(ctx, 0, -1);
}
/* Clear to end of screen from cursor. */
@@ -1251,7 +1202,7 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only)
cx = s->cx; cy = s->cy;
for (y = 0; y < screen_size_y(s); y++) {
TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) {
screen_write_cursormove(ctx, ci->x, y);
screen_write_set_cursor(ctx, ci->x, y);
screen_write_initctx(ctx, &ttyctx);
ttyctx.cell = &ci->gc;
ttyctx.wrapped = ci->wrapped;
@@ -1300,14 +1251,15 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
grid_view_set_cell(s->grid, xx, s->cy,
&grid_default_cell);
}
if (gc.data.width > 1)
if (gc.data.width > 1) {
grid_view_set_cell(s->grid, xx, s->cy,
&grid_default_cell);
}
}
memcpy(&gc, &ci->gc, sizeof gc);
grid_view_set_cells(s->grid, s->cx, s->cy, &gc, ci->data, ci->used);
s->cx += ci->used;
screen_write_set_cursor(ctx, s->cx + ci->used, -1);
for (xx = s->cx; xx < screen_size_x(s); xx++) {
grid_view_get_cell(s->grid, xx, s->cy, &gc);
@@ -1343,7 +1295,7 @@ screen_write_collect_add(struct screen_write_ctx *ctx,
collect = 0;
else if (s->mode & MODE_INSERT)
collect = 0;
else if (s->sel.flag)
else if (s->sel != NULL)
collect = 0;
if (!collect) {
screen_write_collect_end(ctx);
@@ -1361,7 +1313,7 @@ screen_write_collect_add(struct screen_write_ctx *ctx,
log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
ci->wrapped = 1;
screen_write_linefeed(ctx, 1, 8);
s->cx = 0;
screen_write_set_cursor(ctx, 0, -1);
}
if (ci->used == 0)
@@ -1395,7 +1347,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
screen_write_collect_flush(ctx, 0);
if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) {
cx = s->cx; cy = s->cy;
screen_write_cursormove(ctx, xx, s->cy);
screen_write_set_cursor(ctx, xx, s->cy);
screen_write_initctx(ctx, &ttyctx);
ttyctx.cell = gc;
tty_write(tty_cmd_cell, &ttyctx);
@@ -1423,7 +1375,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
screen_write_linefeed(ctx, 1, 8);
s->cx = 0;
screen_write_set_cursor(ctx, 0, -1);
screen_write_collect_flush(ctx, 1);
}
@@ -1433,7 +1385,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
screen_write_initctx(ctx, &ttyctx);
/* Handle overwriting of UTF-8 characters. */
gl = &s->grid->linedata[s->grid->hsize + s->cy];
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
if (gl->flags & GRID_LINE_EXTENDED) {
grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
if (screen_write_overwrite(ctx, &now_gc, width))
@@ -1496,9 +1448,9 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
*/
last = !(s->mode & MODE_WRAP);
if (s->cx <= sx - last - width)
s->cx += width;
screen_write_set_cursor(ctx, s->cx + width, -1);
else
s->cx = sx - last;
screen_write_set_cursor(ctx, sx - last, -1);
/* Create space for character in insert mode. */
if (s->mode & MODE_INSERT) {

137
screen.c
View File

@@ -24,6 +24,22 @@
#include "tmux.h"
/* Selected area in screen. */
struct screen_sel {
int hidden;
int rectangle;
int modekeys;
u_int sx;
u_int sy;
u_int ex;
u_int ey;
struct grid_cell cell;
};
/* Entry on title stack. */
struct screen_title_entry {
char *text;
@@ -31,7 +47,6 @@ struct screen_title_entry {
};
TAILQ_HEAD(screen_titles, screen_title_entry);
static void screen_resize_x(struct screen *, u_int);
static void screen_resize_y(struct screen *, u_int);
static void screen_reflow(struct screen *, u_int);
@@ -66,6 +81,7 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
s->cstyle = 0;
s->ccolour = xstrdup("");
s->tabs = NULL;
s->sel = NULL;
screen_reinit(s);
}
@@ -94,6 +110,7 @@ screen_reinit(struct screen *s)
void
screen_free(struct screen *s)
{
free(s->sel);
free(s->tabs);
free(s->title);
free(s->ccolour);
@@ -188,13 +205,7 @@ screen_resize(struct screen *s, u_int sx, u_int sy, int reflow)
sy = 1;
if (sx != screen_size_x(s)) {
screen_resize_x(s, sx);
/*
* It is unclear what should happen to tabs on resize. xterm
* seems to try and maintain them, rxvt resets them. Resetting
* is simpler and more reliable so let's do that.
*/
s->grid->sx = sx;
screen_reset_tabs(s);
} else
reflow = 0;
@@ -206,28 +217,6 @@ screen_resize(struct screen *s, u_int sx, u_int sy, int reflow)
screen_reflow(s, sx);
}
static void
screen_resize_x(struct screen *s, u_int sx)
{
struct grid *gd = s->grid;
if (sx == 0)
fatalx("zero size");
/*
* Treat resizing horizontally simply: just ensure the cursor is
* on-screen and change the size. Don't bother to truncate any lines -
* then the data should be accessible if the size is then increased.
*
* The only potential wrinkle is if UTF-8 double-width characters are
* left in the last column, but UTF-8 terminals should deal with this
* sanely.
*/
if (s->cx >= sx)
s->cx = sx - 1;
gd->sx = sx;
}
static void
screen_resize_y(struct screen *s, u_int sy)
{
@@ -280,9 +269,8 @@ screen_resize_y(struct screen *s, u_int sy)
s->cy -= needed;
}
/* Resize line arrays. */
gd->linedata = xreallocarray(gd->linedata, gd->hsize + sy,
sizeof *gd->linedata);
/* Resize line array. */
grid_adjust_lines(gd, gd->hsize + sy);
/* Size increasing. */
if (sy > oldy) {
@@ -305,7 +293,7 @@ screen_resize_y(struct screen *s, u_int sy)
/* Then fill the rest in with blanks. */
for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++)
memset(&gd->linedata[i], 0, sizeof gd->linedata[i]);
memset(grid_get_line(gd, i), 0, sizeof(struct grid_line));
}
/* Set the new size, and reset the scroll region. */
@@ -317,51 +305,49 @@ screen_resize_y(struct screen *s, u_int sy)
/* Set selection. */
void
screen_set_selection(struct screen *s, u_int sx, u_int sy,
u_int ex, u_int ey, u_int rectflag, struct grid_cell *gc)
u_int ex, u_int ey, u_int rectangle, int modekeys, struct grid_cell *gc)
{
struct screen_sel *sel = &s->sel;
if (s->sel == NULL)
s->sel = xcalloc(1, sizeof *s->sel);
memcpy(&sel->cell, gc, sizeof sel->cell);
sel->flag = 1;
sel->hidden = 0;
memcpy(&s->sel->cell, gc, sizeof s->sel->cell);
s->sel->hidden = 0;
s->sel->rectangle = rectangle;
s->sel->modekeys = modekeys;
sel->rectflag = rectflag;
sel->sx = sx; sel->sy = sy;
sel->ex = ex; sel->ey = ey;
s->sel->sx = sx;
s->sel->sy = sy;
s->sel->ex = ex;
s->sel->ey = ey;
}
/* Clear selection. */
void
screen_clear_selection(struct screen *s)
{
struct screen_sel *sel = &s->sel;
sel->flag = 0;
sel->hidden = 0;
sel->lineflag = LINE_SEL_NONE;
free(s->sel);
s->sel = NULL;
}
/* Hide selection. */
void
screen_hide_selection(struct screen *s)
{
struct screen_sel *sel = &s->sel;
sel->hidden = 1;
if (s->sel != NULL)
s->sel->hidden = 1;
}
/* Check if cell in selection. */
int
screen_check_selection(struct screen *s, u_int px, u_int py)
{
struct screen_sel *sel = &s->sel;
struct screen_sel *sel = s->sel;
u_int xx;
if (!sel->flag || sel->hidden)
if (sel == NULL || sel->hidden)
return (0);
if (sel->rectflag) {
if (sel->rectangle) {
if (sel->sy < sel->ey) {
/* start line < end line -- downward selection. */
if (py < sel->sy || py > sel->ey)
@@ -409,7 +395,11 @@ screen_check_selection(struct screen *s, u_int px, u_int py)
if (py == sel->sy && px < sel->sx)
return (0);
if (py == sel->ey && px > sel->ex)
if (sel->modekeys == MODEKEY_EMACS)
xx = (sel->ex == 0 ? 0 : sel->ex - 1);
else
xx = sel->ex;
if (py == sel->ey && px > xx)
return (0);
} else if (sel->sy > sel->ey) {
/* starting line > ending line -- upward selection. */
@@ -440,7 +430,11 @@ screen_check_selection(struct screen *s, u_int px, u_int py)
return (0);
} else {
/* selection start (sx) is on the left */
if (px < sel->sx || px > sel->ex)
if (sel->modekeys == MODEKEY_EMACS)
xx = (sel->ex == 0 ? 0 : sel->ex - 1);
else
xx = sel->ex;
if (px < sel->sx || px > xx)
return (0);
}
}
@@ -454,10 +448,10 @@ void
screen_select_cell(struct screen *s, struct grid_cell *dst,
const struct grid_cell *src)
{
if (!s->sel.flag || s->sel.hidden)
if (s->sel == NULL || s->sel->hidden)
return;
memcpy(dst, &s->sel.cell, sizeof *dst);
memcpy(dst, &s->sel->cell, sizeof *dst);
utf8_copy(&dst->data, &src->data);
dst->attr = dst->attr & ~GRID_ATTR_CHARSET;
@@ -469,5 +463,30 @@ screen_select_cell(struct screen *s, struct grid_cell *dst,
static void
screen_reflow(struct screen *s, u_int new_x)
{
grid_reflow(s->grid, new_x, &s->cy);
u_int cx = s->cx, cy = s->grid->hsize + s->cy, wx, wy;
struct timeval start, tv;
gettimeofday(&start, NULL);
grid_wrap_position(s->grid, cx, cy, &wx, &wy);
log_debug("%s: cursor %u,%u is %u,%u", __func__, cx, cy, wx, wy);
grid_reflow(s->grid, new_x);
grid_unwrap_position(s->grid, &cx, &cy, wx, wy);
log_debug("%s: new cursor is %u,%u", __func__, cx, cy);
if (cy >= s->grid->hsize) {
s->cx = cx;
s->cy = cy - s->grid->hsize;
} else {
s->cx = 0;
s->cy = 0;
}
gettimeofday(&tv, NULL);
timersub(&tv, &start, &tv);
log_debug("%s: reflow took %llu.%06u seconds", __func__,
(unsigned long long)tv.tv_sec, (u_int)tv.tv_usec);
}

View File

@@ -41,6 +41,8 @@ static void server_client_check_redraw(struct client *);
static void server_client_set_title(struct client *);
static void server_client_reset_state(struct client *);
static int server_client_assume_paste(struct session *);
static void server_client_clear_identify(struct client *,
struct window_pane *);
static void server_client_dispatch(struct imsg *, void *);
static void server_client_dispatch_command(struct client *, struct imsg *);
@@ -91,7 +93,7 @@ server_client_set_identify(struct client *c, u_int delay)
}
/* Clear identify mode on client. */
void
static void
server_client_clear_identify(struct client *c, struct window_pane *wp)
{
if (~c->flags & CLIENT_IDENTIFY)
@@ -182,18 +184,25 @@ server_client_create(int fd)
TAILQ_INIT(&c->queue);
c->stdin_data = evbuffer_new();
if (c->stdin_data == NULL)
fatalx("out of memory");
c->stdout_data = evbuffer_new();
if (c->stdout_data == NULL)
fatalx("out of memory");
c->stderr_data = evbuffer_new();
if (c->stderr_data == NULL)
fatalx("out of memory");
c->tty.fd = -1;
c->title = NULL;
c->session = NULL;
c->last_session = NULL;
c->tty.sx = 80;
c->tty.sy = 24;
screen_init(&c->status.status, c->tty.sx, 1, 0);
status_init(c);
c->message_string = NULL;
TAILQ_INIT(&c->message_log);
@@ -270,13 +279,7 @@ server_client_lost(struct client *c)
if (c->stderr_data != c->stdout_data)
evbuffer_free(c->stderr_data);
if (event_initialized(&c->status.timer))
evtimer_del(&c->status.timer);
screen_free(&c->status.status);
if (c->status.old_status != NULL) {
screen_free(c->status.old_status);
free(c->status.old_status);
}
status_free(c);
free(c->title);
free((void *)c->cwd);
@@ -298,6 +301,7 @@ server_client_lost(struct client *c)
free(msg);
}
free(c->prompt_saved);
free(c->prompt_string);
free(c->prompt_buffer);
@@ -405,20 +409,21 @@ server_client_check_mouse(struct client *c)
{
struct session *s = c->session;
struct mouse_event *m = &c->tty.mouse;
struct window *w;
struct winlink *wl;
struct window_pane *wp;
u_int x, y, b;
u_int x, y, b, sx, sy, px, py;
int flag;
key_code key;
struct timeval tv;
struct style_range *sr;
enum { NOTYPE, MOVE, DOWN, UP, DRAG, WHEEL, DOUBLE, TRIPLE } type;
enum { NOWHERE, PANE, STATUS, BORDER } where;
enum { NOWHERE, PANE, STATUS, STATUS_LEFT, STATUS_RIGHT, STATUS_DEFAULT, BORDER } where;
type = NOTYPE;
where = NOWHERE;
log_debug("mouse %02x at %u,%u (last %u,%u) (%d)", m->b, m->x, m->y,
m->lx, m->ly, c->tty.mouse_drag_flag);
log_debug("%s mouse %02x at %u,%u (last %u,%u) (%d)", c->name, m->b,
m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag);
/* What type of event is this? */
if ((m->sgr_type != ' ' &&
@@ -437,7 +442,7 @@ server_client_check_mouse(struct client *c)
x = m->x, y = m->y, b = m->b;
log_debug("drag update at %u,%u", x, y);
} else {
x = m->lx, y = m->ly, b = m->lb;
x = m->lx - m->ox, y = m->ly - m->oy, b = m->lb;
log_debug("drag start at %u,%u", x, y);
}
} else if (MOUSE_WHEEL(m->b)) {
@@ -491,48 +496,88 @@ have_event:
if (type == NOTYPE)
return (KEYC_UNKNOWN);
/* Always save the session. */
/* Save the session. */
m->s = s->id;
m->w = -1;
/* Is this on the status line? */
m->statusat = status_at_line(c);
if (m->statusat != -1 && y == (u_int)m->statusat) {
w = status_get_window_at(c, x);
if (w == NULL)
return (KEYC_UNKNOWN);
m->w = w->id;
where = STATUS;
} else
m->w = -1;
if (m->statusat != -1 &&
y >= (u_int)m->statusat &&
y < m->statusat + status_line_size(c)) {
sr = status_get_range(c, x, y - m->statusat);
if (sr == NULL) {
where = STATUS_DEFAULT;
} else {
switch (sr->type) {
case STYLE_RANGE_NONE:
return (KEYC_UNKNOWN);
case STYLE_RANGE_LEFT:
where = STATUS_LEFT;
break;
case STYLE_RANGE_RIGHT:
where = STATUS_RIGHT;
break;
case STYLE_RANGE_WINDOW:
wl = winlink_find_by_index(&s->windows, sr->argument);
if (wl == NULL)
return (KEYC_UNKNOWN);
m->w = wl->window->id;
where = STATUS;
break;
}
}
}
/* Not on status line. Adjust position and check for border or pane. */
if (where == NOWHERE) {
px = x;
if (m->statusat == 0 && y > 0)
y--;
py = y - 1;
else if (m->statusat > 0 && y >= (u_int)m->statusat)
y = m->statusat - 1;
py = m->statusat - 1;
else
py = y;
TAILQ_FOREACH(wp, &s->curw->window->panes, entry) {
if ((wp->xoff + wp->sx == x &&
wp->yoff <= 1 + y &&
wp->yoff + wp->sy >= y) ||
(wp->yoff + wp->sy == y &&
wp->xoff <= 1 + x &&
wp->xoff + wp->sx >= x))
break;
}
if (wp != NULL)
where = BORDER;
else {
wp = window_get_active_at(s->curw->window, x, y);
if (wp != NULL) {
where = PANE;
log_debug("mouse at %u,%u is on pane %%%u",
x, y, wp->id);
tty_window_offset(&c->tty, &m->ox, &m->oy, &sx, &sy);
log_debug("mouse window @%u at %u,%u (%ux%u)",
s->curw->window->id, m->ox, m->oy, sx, sy);
if (px > sx || py > sy)
return (KEYC_UNKNOWN);
px = px + m->ox;
py = py + m->oy;
m->x = x + m->ox;
m->y = y + m->oy;
/* Try the pane borders if not zoomed. */
if (~s->curw->window->flags & WINDOW_ZOOMED) {
TAILQ_FOREACH(wp, &s->curw->window->panes, entry) {
if ((wp->xoff + wp->sx == px &&
wp->yoff <= 1 + py &&
wp->yoff + wp->sy >= py) ||
(wp->yoff + wp->sy == py &&
wp->xoff <= 1 + px &&
wp->xoff + wp->sx >= px))
break;
}
if (wp != NULL)
where = BORDER;
}
/* Otherwise try inside the pane. */
if (where == NOWHERE) {
wp = window_get_active_at(s->curw->window, px, py);
if (wp != NULL)
where = PANE;
}
if (where == NOWHERE)
return (KEYC_UNKNOWN);
if (where == PANE)
log_debug("mouse %u,%u on pane %%%u", x, y, wp->id);
else if (where == BORDER)
log_debug("mouse on pane %%%u border", wp->id);
m->wp = wp->id;
m->w = wp->window->id;
} else
@@ -556,6 +601,12 @@ have_event:
key = KEYC_MOUSEDRAGEND1_PANE;
if (where == STATUS)
key = KEYC_MOUSEDRAGEND1_STATUS;
if (where == STATUS_LEFT)
key = KEYC_MOUSEDRAGEND1_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_MOUSEDRAGEND1_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_MOUSEDRAGEND1_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_MOUSEDRAGEND1_BORDER;
break;
@@ -564,6 +615,12 @@ have_event:
key = KEYC_MOUSEDRAGEND2_PANE;
if (where == STATUS)
key = KEYC_MOUSEDRAGEND2_STATUS;
if (where == STATUS_LEFT)
key = KEYC_MOUSEDRAGEND2_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_MOUSEDRAGEND2_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_MOUSEDRAGEND2_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_MOUSEDRAGEND2_BORDER;
break;
@@ -572,6 +629,12 @@ have_event:
key = KEYC_MOUSEDRAGEND3_PANE;
if (where == STATUS)
key = KEYC_MOUSEDRAGEND3_STATUS;
if (where == STATUS_LEFT)
key = KEYC_MOUSEDRAGEND3_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_MOUSEDRAGEND3_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_MOUSEDRAGEND3_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_MOUSEDRAGEND3_BORDER;
break;
@@ -594,6 +657,12 @@ have_event:
key = KEYC_MOUSEMOVE_PANE;
if (where == STATUS)
key = KEYC_MOUSEMOVE_STATUS;
if (where == STATUS_LEFT)
key = KEYC_MOUSEMOVE_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_MOUSEMOVE_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_MOUSEMOVE_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_MOUSEMOVE_BORDER;
break;
@@ -607,6 +676,12 @@ have_event:
key = KEYC_MOUSEDRAG1_PANE;
if (where == STATUS)
key = KEYC_MOUSEDRAG1_STATUS;
if (where == STATUS_LEFT)
key = KEYC_MOUSEDRAG1_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_MOUSEDRAG1_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_MOUSEDRAG1_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_MOUSEDRAG1_BORDER;
break;
@@ -615,6 +690,12 @@ have_event:
key = KEYC_MOUSEDRAG2_PANE;
if (where == STATUS)
key = KEYC_MOUSEDRAG2_STATUS;
if (where == STATUS_LEFT)
key = KEYC_MOUSEDRAG2_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_MOUSEDRAG2_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_MOUSEDRAG2_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_MOUSEDRAG2_BORDER;
break;
@@ -623,6 +704,12 @@ have_event:
key = KEYC_MOUSEDRAG3_PANE;
if (where == STATUS)
key = KEYC_MOUSEDRAG3_STATUS;
if (where == STATUS_LEFT)
key = KEYC_MOUSEDRAG3_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_MOUSEDRAG3_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_MOUSEDRAG3_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_MOUSEDRAG3_BORDER;
break;
@@ -641,6 +728,12 @@ have_event:
key = KEYC_WHEELUP_PANE;
if (where == STATUS)
key = KEYC_WHEELUP_STATUS;
if (where == STATUS_LEFT)
key = KEYC_WHEELUP_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_WHEELUP_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_WHEELUP_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_WHEELUP_BORDER;
} else {
@@ -648,6 +741,12 @@ have_event:
key = KEYC_WHEELDOWN_PANE;
if (where == STATUS)
key = KEYC_WHEELDOWN_STATUS;
if (where == STATUS_LEFT)
key = KEYC_WHEELDOWN_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_WHEELDOWN_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_WHEELDOWN_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_WHEELDOWN_BORDER;
}
@@ -659,6 +758,12 @@ have_event:
key = KEYC_MOUSEUP1_PANE;
if (where == STATUS)
key = KEYC_MOUSEUP1_STATUS;
if (where == STATUS_LEFT)
key = KEYC_MOUSEUP1_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_MOUSEUP1_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_MOUSEUP1_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_MOUSEUP1_BORDER;
break;
@@ -667,6 +772,12 @@ have_event:
key = KEYC_MOUSEUP2_PANE;
if (where == STATUS)
key = KEYC_MOUSEUP2_STATUS;
if (where == STATUS_LEFT)
key = KEYC_MOUSEUP2_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_MOUSEUP2_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_MOUSEUP2_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_MOUSEUP2_BORDER;
break;
@@ -675,6 +786,12 @@ have_event:
key = KEYC_MOUSEUP3_PANE;
if (where == STATUS)
key = KEYC_MOUSEUP3_STATUS;
if (where == STATUS_LEFT)
key = KEYC_MOUSEUP3_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_MOUSEUP3_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_MOUSEUP3_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_MOUSEUP3_BORDER;
break;
@@ -687,6 +804,12 @@ have_event:
key = KEYC_MOUSEDOWN1_PANE;
if (where == STATUS)
key = KEYC_MOUSEDOWN1_STATUS;
if (where == STATUS_LEFT)
key = KEYC_MOUSEDOWN1_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_MOUSEDOWN1_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_MOUSEDOWN1_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_MOUSEDOWN1_BORDER;
break;
@@ -695,6 +818,12 @@ have_event:
key = KEYC_MOUSEDOWN2_PANE;
if (where == STATUS)
key = KEYC_MOUSEDOWN2_STATUS;
if (where == STATUS_LEFT)
key = KEYC_MOUSEDOWN2_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_MOUSEDOWN2_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_MOUSEDOWN2_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_MOUSEDOWN2_BORDER;
break;
@@ -703,6 +832,12 @@ have_event:
key = KEYC_MOUSEDOWN3_PANE;
if (where == STATUS)
key = KEYC_MOUSEDOWN3_STATUS;
if (where == STATUS_LEFT)
key = KEYC_MOUSEDOWN3_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_MOUSEDOWN3_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_MOUSEDOWN3_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_MOUSEDOWN3_BORDER;
break;
@@ -715,6 +850,12 @@ have_event:
key = KEYC_DOUBLECLICK1_PANE;
if (where == STATUS)
key = KEYC_DOUBLECLICK1_STATUS;
if (where == STATUS_LEFT)
key = KEYC_DOUBLECLICK1_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_DOUBLECLICK1_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_DOUBLECLICK1_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_DOUBLECLICK1_BORDER;
break;
@@ -723,6 +864,12 @@ have_event:
key = KEYC_DOUBLECLICK2_PANE;
if (where == STATUS)
key = KEYC_DOUBLECLICK2_STATUS;
if (where == STATUS_LEFT)
key = KEYC_DOUBLECLICK2_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_DOUBLECLICK2_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_DOUBLECLICK2_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_DOUBLECLICK2_BORDER;
break;
@@ -731,6 +878,12 @@ have_event:
key = KEYC_DOUBLECLICK3_PANE;
if (where == STATUS)
key = KEYC_DOUBLECLICK3_STATUS;
if (where == STATUS_LEFT)
key = KEYC_DOUBLECLICK3_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_DOUBLECLICK3_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_DOUBLECLICK3_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_DOUBLECLICK3_BORDER;
break;
@@ -743,6 +896,12 @@ have_event:
key = KEYC_TRIPLECLICK1_PANE;
if (where == STATUS)
key = KEYC_TRIPLECLICK1_STATUS;
if (where == STATUS_LEFT)
key = KEYC_TRIPLECLICK1_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_TRIPLECLICK1_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_TRIPLECLICK1_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_TRIPLECLICK1_BORDER;
break;
@@ -751,6 +910,12 @@ have_event:
key = KEYC_TRIPLECLICK2_PANE;
if (where == STATUS)
key = KEYC_TRIPLECLICK2_STATUS;
if (where == STATUS_LEFT)
key = KEYC_TRIPLECLICK2_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_TRIPLECLICK2_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_TRIPLECLICK2_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_TRIPLECLICK2_BORDER;
break;
@@ -759,6 +924,12 @@ have_event:
key = KEYC_TRIPLECLICK3_PANE;
if (where == STATUS)
key = KEYC_TRIPLECLICK3_STATUS;
if (where == STATUS_LEFT)
key = KEYC_TRIPLECLICK3_STATUS_LEFT;
if (where == STATUS_RIGHT)
key = KEYC_TRIPLECLICK3_STATUS_RIGHT;
if (where == STATUS_DEFAULT)
key = KEYC_TRIPLECLICK3_STATUS_DEFAULT;
if (where == BORDER)
key = KEYC_TRIPLECLICK3_BORDER;
break;
@@ -807,21 +978,24 @@ server_client_assume_paste(struct session *s)
void
server_client_handle_key(struct client *c, key_code key)
{
struct mouse_event *m = &c->tty.mouse;
struct session *s = c->session;
struct window *w;
struct window_pane *wp;
struct timeval tv;
struct key_table *table, *first;
struct key_binding bd_find, *bd;
int xtimeout, flags;
struct cmd_find_state fs;
key_code key0;
struct mouse_event *m = &c->tty.mouse;
struct session *s = c->session;
struct winlink *wl;
struct window *w;
struct window_pane *wp;
struct window_mode_entry *wme;
struct timeval tv;
struct key_table *table, *first;
struct key_binding *bd;
int xtimeout, flags;
struct cmd_find_state fs;
key_code key0;
/* Check the client is good to accept input. */
if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
return;
w = s->curw->window;
wl = s->curw;
w = wl->window;
/* Update the activity timer. */
if (gettimeofday(&c->activity_time, NULL) != 0)
@@ -834,8 +1008,6 @@ server_client_handle_key(struct client *c, key_code key)
return;
window_unzoom(w);
wp = window_pane_at_index(w, key - '0');
if (wp != NULL && !window_pane_visible(wp))
wp = NULL;
server_client_clear_identify(c, wp);
return;
}
@@ -882,11 +1054,11 @@ server_client_handle_key(struct client *c, key_code key)
/* Forward mouse keys if disabled. */
if (KEYC_IS_MOUSE(key) && !options_get_number(s->options, "mouse"))
goto forward;
goto forward_key;
/* Treat everything as a regular key when pasting is detected. */
if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s))
goto forward;
goto forward_key;
/*
* Work out the current key table. If the pane is in a mode, use
@@ -894,18 +1066,18 @@ server_client_handle_key(struct client *c, key_code key)
*/
if (server_client_is_default_key_table(c, c->keytable) &&
wp != NULL &&
wp->mode != NULL &&
wp->mode->key_table != NULL)
table = key_bindings_get_table(wp->mode->key_table(wp), 1);
(wme = TAILQ_FIRST(&wp->modes)) != NULL &&
wme->mode->key_table != NULL)
table = key_bindings_get_table(wme->mode->key_table(wme), 1);
else
table = c->keytable;
first = table;
table_changed:
/*
* The prefix always takes precedence and forces a switch to the prefix
* table, unless we are already there.
*/
retry:
key0 = (key & ~KEYC_XTERM);
if ((key0 == (key_code)options_get_number(s->options, "prefix") ||
key0 == (key_code)options_get_number(s->options, "prefix2")) &&
@@ -916,6 +1088,7 @@ retry:
}
flags = c->flags;
try_again:
/* Log key table. */
if (wp == NULL)
log_debug("key table %s (no pane)", table->name);
@@ -925,8 +1098,7 @@ retry:
log_debug("currently repeating");
/* Try to see if there is a key binding in the current table. */
bd_find.key = key0;
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
bd = key_bindings_get(table, key0);
if (bd != NULL) {
/*
* Key was matched in this table. If currently repeating but a
@@ -935,11 +1107,13 @@ retry:
*/
if ((c->flags & CLIENT_REPEAT) &&
(~bd->flags & KEY_BINDING_REPEAT)) {
log_debug("found in key table %s (not repeating)",
table->name);
server_client_set_key_table(c, NULL);
first = table = c->keytable;
c->flags &= ~CLIENT_REPEAT;
server_status_client(c);
table = c->keytable;
goto retry;
goto table_changed;
}
log_debug("found in key table %s", table->name);
@@ -973,6 +1147,14 @@ retry:
return;
}
/*
* No match, try the ANY key.
*/
if (key0 != KEYC_ANY) {
key0 = KEYC_ANY;
goto try_again;
}
/*
* No match in this table. If not in the root table or if repeating,
* switch the client back to the root table and try again.
@@ -980,11 +1162,14 @@ retry:
log_debug("not found in key table %s", table->name);
if (!server_client_is_default_key_table(c, table) ||
(c->flags & CLIENT_REPEAT)) {
log_debug("trying in root table");
server_client_set_key_table(c, NULL);
table = c->keytable;
if (c->flags & CLIENT_REPEAT)
first = table;
c->flags &= ~CLIENT_REPEAT;
server_status_client(c);
table = c->keytable;
goto retry;
goto table_changed;
}
/*
@@ -997,11 +1182,11 @@ retry:
return;
}
forward:
forward_key:
if (c->flags & CLIENT_READONLY)
return;
if (wp != NULL)
window_pane_key(wp, c, s, key, m);
window_pane_key(wp, c, s, wl, key, m);
}
/* Client functions that need to happen every loop. */
@@ -1158,10 +1343,6 @@ server_client_check_focus(struct window_pane *wp)
push = wp->flags & PANE_FOCUSPUSH;
wp->flags &= ~PANE_FOCUSPUSH;
/* If we don't care about focus, forget it. */
if (!(wp->base.mode & MODE_FOCUSON))
return;
/* If we're not the active pane in our window, we're not focused. */
if (wp->window->active != wp)
goto not_focused;
@@ -1177,7 +1358,7 @@ server_client_check_focus(struct window_pane *wp)
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL || !(c->flags & CLIENT_FOCUSED))
continue;
if (c->session->flags & SESSION_UNATTACHED)
if (c->session->attached == 0)
continue;
if (c->session->curw->window == wp->window)
@@ -1185,14 +1366,20 @@ server_client_check_focus(struct window_pane *wp)
}
not_focused:
if (push || (wp->flags & PANE_FOCUSED))
bufferevent_write(wp->event, "\033[O", 3);
if (push || (wp->flags & PANE_FOCUSED)) {
if (wp->base.mode & MODE_FOCUSON)
bufferevent_write(wp->event, "\033[O", 3);
notify_pane("pane-focus-out", wp);
}
wp->flags &= ~PANE_FOCUSED;
return;
focused:
if (push || !(wp->flags & PANE_FOCUSED))
bufferevent_write(wp->event, "\033[I", 3);
if (push || !(wp->flags & PANE_FOCUSED)) {
if (wp->base.mode & MODE_FOCUSON)
bufferevent_write(wp->event, "\033[I", 3);
notify_pane("pane-focus-in", wp);
}
wp->flags |= PANE_FOCUSED;
}
@@ -1212,28 +1399,37 @@ server_client_reset_state(struct client *c)
struct window_pane *wp = w->active, *loop;
struct screen *s = wp->screen;
struct options *oo = c->session->options;
int lines, mode;
int mode, cursor = 0;
u_int cx = 0, cy = 0, ox, oy, sx, sy;
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return;
mode = s->mode;
tty_region_off(&c->tty);
tty_margin_off(&c->tty);
if (status_at_line(c) != 0)
lines = 0;
else
lines = status_line_size(c->session);
if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - lines)
tty_cursor(&c->tty, 0, 0);
else
tty_cursor(&c->tty, wp->xoff + s->cx, lines + wp->yoff + s->cy);
/* Move cursor to pane cursor and offset. */
cursor = 0;
tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx &&
wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) {
cursor = 1;
cx = wp->xoff + s->cx - ox;
cy = wp->yoff + s->cy - oy;
if (status_at_line(c) == 0)
cy += status_line_size(c);
}
if (!cursor)
mode &= ~MODE_CURSOR;
tty_cursor(&c->tty, cx, cy);
/*
* Set mouse mode if requested. To support dragging, always use button
* mode.
*/
mode = s->mode;
if (options_get_number(oo, "mouse")) {
mode &= ~ALL_MOUSE_MODES;
TAILQ_FOREACH(loop, &w->panes, entry) {
@@ -1310,13 +1506,19 @@ server_client_check_redraw(struct client *c)
struct session *s = c->session;
struct tty *tty = &c->tty;
struct window_pane *wp;
int needed, flags, masked;
int needed, flags;
struct timeval tv = { .tv_usec = 1000 };
static struct event ev;
size_t left;
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return;
if (c->flags & CLIENT_ALLREDRAWFLAGS) {
log_debug("%s: redraw%s%s%s", c->name,
(c->flags & CLIENT_REDRAWWINDOW) ? " window" : "",
(c->flags & CLIENT_REDRAWSTATUS) ? " status" : "",
(c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "");
}
/*
* If there is outstanding data, defer the redraw until it has been
@@ -1324,7 +1526,7 @@ server_client_check_redraw(struct client *c)
* end up back here.
*/
needed = 0;
if (c->flags & CLIENT_REDRAW)
if (c->flags & CLIENT_ALLREDRAWFLAGS)
needed = 1;
else {
TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
@@ -1347,25 +1549,19 @@ server_client_check_redraw(struct client *c)
* We may have got here for a single pane redraw, but force a
* full redraw next time in case other panes have been updated.
*/
c->flags |= CLIENT_REDRAW;
c->flags |= CLIENT_ALLREDRAWFLAGS;
return;
} else if (needed)
log_debug("%s: redraw needed", c->name);
if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
if (options_get_number(s->options, "set-titles"))
server_client_set_title(c);
screen_redraw_update(c); /* will adjust flags */
}
flags = tty->flags & (TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR);
tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE)) | TTY_NOCURSOR;
if (c->flags & CLIENT_REDRAW) {
tty_update_mode(tty, tty->mode, NULL);
screen_redraw_screen(c, 1, 1, 1);
c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS);
} else {
if (~c->flags & CLIENT_REDRAWWINDOW) {
/*
* If not redrawing the entire window, check whether each pane
* needs to be redrawn.
*/
TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
if (wp->flags & PANE_REDRAW) {
tty_update_mode(tty, tty->mode, NULL);
@@ -1374,21 +1570,16 @@ server_client_check_redraw(struct client *c)
}
}
masked = c->flags & (CLIENT_BORDERS|CLIENT_STATUS);
if (masked != 0)
tty_update_mode(tty, tty->mode, NULL);
if (masked == CLIENT_BORDERS)
screen_redraw_screen(c, 0, 0, 1);
else if (masked == CLIENT_STATUS)
screen_redraw_screen(c, 0, 1, 0);
else if (masked != 0)
screen_redraw_screen(c, 0, 1, 1);
if (c->flags & CLIENT_ALLREDRAWFLAGS) {
if (options_get_number(s->options, "set-titles"))
server_client_set_title(c);
screen_redraw_screen(c);
}
tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags;
tty_update_mode(tty, tty->mode, NULL);
c->flags &= ~(CLIENT_REDRAW|CLIENT_BORDERS|CLIENT_STATUS|
CLIENT_STATUSFORCE);
c->flags &= ~(CLIENT_ALLREDRAWFLAGS|CLIENT_STATUSFORCE);
if (needed) {
/*
@@ -1415,7 +1606,7 @@ server_client_set_title(struct client *c)
ft = format_create(c, NULL, FORMAT_NONE, 0);
format_defaults(ft, c, NULL, NULL, NULL);
title = format_expand_time(ft, template, time(NULL));
title = format_expand_time(ft, template);
if (c->title == NULL || strcmp(title, c->title) != 0) {
free(c->title);
c->title = xstrdup(title);
@@ -1861,15 +2052,21 @@ server_client_add_message(struct client *c, const char *fmt, ...)
/* Get client working directory. */
const char *
server_client_get_cwd(struct client *c)
server_client_get_cwd(struct client *c, struct session *s)
{
struct session *s;
const char *home;
if (!cfg_finished && cfg_client != NULL)
return (cfg_client->cwd);
if (c != NULL && c->session == NULL && c->cwd != NULL)
return (c->cwd);
if (s != NULL && s->cwd != NULL)
return (s->cwd);
if (c != NULL && (s = c->session) != NULL && s->cwd != NULL)
return (s->cwd);
return (".");
if ((home = find_home()) != NULL)
return (home);
return ("/");
}
/* Resolve an absolute path or relative to client working directory. */
@@ -1881,7 +2078,7 @@ server_client_get_path(struct client *c, const char *file)
if (*file == '/')
path = xstrdup(file);
else
xasprintf(&path, "%s/%s", server_client_get_cwd(c), file);
xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file);
if (realpath(path, resolved) == NULL)
return (path);
free(path);

View File

@@ -33,13 +33,13 @@ static void server_destroy_session_group(struct session *);
void
server_redraw_client(struct client *c)
{
c->flags |= CLIENT_REDRAW;
c->flags |= CLIENT_ALLREDRAWFLAGS;
}
void
server_status_client(struct client *c)
{
c->flags |= CLIENT_STATUS;
c->flags |= CLIENT_REDRAWSTATUS;
}
void
@@ -108,7 +108,7 @@ server_redraw_window_borders(struct window *w)
TAILQ_FOREACH(c, &clients, entry) {
if (c->session != NULL && c->session->curw->window == w)
c->flags |= CLIENT_BORDERS;
c->flags |= CLIENT_REDRAWBORDERS;
}
}
@@ -175,6 +175,22 @@ server_lock_client(struct client *c)
proc_send(c->peer, MSG_LOCK, -1, cmd, strlen(cmd) + 1);
}
void
server_kill_pane(struct window_pane *wp)
{
struct window *w = wp->window;
if (window_count_panes(w) == 1) {
server_kill_window(w);
recalculate_sizes();
} else {
server_unzoom_window(w);
layout_close_pane(wp);
window_remove_pane(w, wp);
server_redraw_window(w);
}
}
void
server_kill_window(struct window *w)
{
@@ -287,6 +303,7 @@ server_destroy_pane(struct window_pane *wp, int notify)
utempter_remove_record(wp->fd);
#endif
bufferevent_free(wp->event);
wp->event = NULL;
close(wp->fd);
wp->fd = -1;
}
@@ -304,7 +321,7 @@ server_destroy_pane(struct window_pane *wp, int notify)
screen_write_start(&ctx, wp, &wp->base);
screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1);
screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1);
screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1, 0);
screen_write_linefeed(&ctx, 1, 8);
memcpy(&gc, &grid_default_cell, sizeof gc);
@@ -394,6 +411,7 @@ server_destroy_session(struct session *s)
c->last_session = NULL;
c->session = s_new;
server_client_set_key_table(c, NULL);
tty_update_client_offset(c);
status_timer_start(c);
notify_client("client-session-changed", c);
session_update_activity(s_new, NULL);
@@ -415,7 +433,7 @@ server_check_unattached(void)
* set, collect them.
*/
RB_FOREACH(s, sessions, &sessions) {
if (!(s->flags & SESSION_UNATTACHED))
if (s->attached != 0)
continue;
if (options_get_number (s->options, "destroy-unattached"))
session_destroy(s, __func__);

View File

@@ -161,7 +161,6 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd,
char *lockfile)
{
int pair[2];
struct job *job;
sigset_t set, oldset;
struct client *c;
char *cause = NULL;
@@ -201,7 +200,6 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd,
RB_INIT(&all_window_panes);
TAILQ_INIT(&clients);
RB_INIT(&sessions);
RB_INIT(&session_groups);
key_bindings_init();
gettimeofday(&start_time, NULL);
@@ -223,17 +221,13 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd,
}
start_cfg();
server_add_accept(0);
proc_loop(server_proc, server_loop);
LIST_FOREACH(job, &all_jobs, entry) {
if (job->pid != -1)
kill(job->pid, SIGTERM);
}
job_kill_all();
status_prompt_save_history();
exit(0);
}
@@ -243,7 +237,6 @@ server_loop(void)
{
struct client *c;
u_int items;
struct job *job;
do {
items = cmdq_next(NULL);
@@ -276,10 +269,8 @@ server_loop(void)
if (!TAILQ_EMPTY(&clients))
return (0);
LIST_FOREACH(job, &all_jobs, entry) {
if ((~job->flags & JOB_NOWAIT) && job->state == JOB_RUNNING)
return (0);
}
if (job_still_running())
return (0);
return (1);
}
@@ -319,7 +310,7 @@ server_update_socket(void)
n = 0;
RB_FOREACH(s, sessions, &sessions) {
if (!(s->flags & SESSION_UNATTACHED)) {
if (s->attached != 0) {
n++;
break;
}
@@ -457,7 +448,6 @@ server_child_exited(pid_t pid, int status)
{
struct window *w, *w1;
struct window_pane *wp;
struct job *job;
RB_FOREACH_SAFE(w, windows, &windows, w1) {
TAILQ_FOREACH(wp, &w->panes, entry) {
@@ -474,13 +464,7 @@ server_child_exited(pid_t pid, int status)
}
}
}
LIST_FOREACH(job, &all_jobs, entry) {
if (pid == job->pid) {
job_died(job, status); /* might free job */
break;
}
}
job_check_died(pid, status);
}
/* Handle stopped children. */

View File

@@ -28,7 +28,7 @@
struct sessions sessions;
static u_int next_session_id;
struct session_groups session_groups;
struct session_groups session_groups = RB_INITIALIZER(&session_groups);
static void session_free(int, short, void *);
@@ -40,21 +40,19 @@ static struct winlink *session_previous_alert(struct winlink *);
static void session_group_remove(struct session *);
static void session_group_synchronize1(struct session *, struct session *);
RB_GENERATE(sessions, session, entry, session_cmp);
int
session_cmp(struct session *s1, struct session *s2)
{
return (strcmp(s1->name, s2->name));
}
RB_GENERATE(sessions, session, entry, session_cmp);
RB_GENERATE(session_groups, session_group, entry, session_group_cmp);
int
static int
session_group_cmp(struct session_group *s1, struct session_group *s2)
{
return (strcmp(s1->name, s2->name));
}
RB_GENERATE_STATIC(session_groups, session_group, entry, session_group_cmp);
/*
* Find if session is still alive. This is true if it is still on the global
@@ -114,8 +112,8 @@ session_find_by_id(u_int id)
/* Create a new session. */
struct session *
session_create(const char *prefix, const char *name, int argc, char **argv,
const char *path, const char *cwd, struct environ *env, struct termios *tio,
int idx, u_int sx, u_int sy, char **cause)
const char *path, const char *cwd, struct environ *env, struct options *oo,
struct termios *tio, int idx, char **cause)
{
struct session *s;
struct winlink *wl;
@@ -134,10 +132,10 @@ session_create(const char *prefix, const char *name, int argc, char **argv,
if (env != NULL)
environ_copy(env, s->environ);
s->options = options_create(global_s_options);
s->options = oo;
s->hooks = hooks_create(global_hooks);
status_update_saved(s);
status_update_cache(s);
s->tio = NULL;
if (tio != NULL) {
@@ -145,9 +143,6 @@ session_create(const char *prefix, const char *name, int argc, char **argv,
memcpy(s->tio, tio, sizeof *s->tio);
}
s->sx = sx;
s->sy = sy;
if (name != NULL) {
s->name = xstrdup(name);
s->id = next_session_id++;
@@ -267,7 +262,7 @@ session_lock_timer(__unused int fd, __unused short events, void *arg)
{
struct session *s = arg;
if (s->flags & SESSION_UNATTACHED)
if (s->attached == 0)
return;
log_debug("session %s locked, activity time %lld", s->name,
@@ -290,16 +285,17 @@ session_update_activity(struct session *s, struct timeval *from)
else
memcpy(&s->activity_time, from, sizeof s->activity_time);
log_debug("session %s activity %lld.%06d (last %lld.%06d)", s->name,
(long long)s->activity_time.tv_sec, (int)s->activity_time.tv_usec,
(long long)last->tv_sec, (int)last->tv_usec);
log_debug("session $%u %s activity %lld.%06d (last %lld.%06d)", s->id,
s->name, (long long)s->activity_time.tv_sec,
(int)s->activity_time.tv_usec, (long long)last->tv_sec,
(int)last->tv_usec);
if (evtimer_initialized(&s->lock_timer))
evtimer_del(&s->lock_timer);
else
evtimer_set(&s->lock_timer, session_lock_timer, s);
if (~s->flags & SESSION_UNATTACHED) {
if (s->attached != 0) {
timerclear(&tv);
tv.tv_sec = options_get_number(s->options, "lock-after-time");
if (tv.tv_sec != 0)
@@ -350,7 +346,7 @@ session_new(struct session *s, const char *name, int argc, char **argv,
struct winlink *wl;
struct environ *env;
const char *shell;
u_int hlimit;
u_int hlimit, sx, sy;
if ((wl = winlink_add(&s->windows, idx)) == NULL) {
xasprintf(cause, "index in use: %d", idx);
@@ -362,10 +358,11 @@ session_new(struct session *s, const char *name, int argc, char **argv,
if (*shell == '\0' || areshell(shell))
shell = _PATH_BSHELL;
default_window_size(s, NULL, &sx, &sy, -1);
hlimit = options_get_number(s->options, "history-limit");
env = environ_for_session(s, 0);
w = window_create_spawn(name, argc, argv, path, shell, cwd, env, s->tio,
s->sx, s->sy, hlimit, cause);
sx, sy, hlimit, cause);
if (w == NULL) {
winlink_remove(&s->windows, wl);
environ_free(env);
@@ -548,6 +545,7 @@ session_set_current(struct session *s, struct winlink *wl)
s->curw = wl;
winlink_clear_flags(wl);
window_update_activity(wl->window);
tty_update_window_offset(wl->window);
notify_session("session-window-changed", s);
return (0);
}

733
status.c

File diff suppressed because it is too large Load Diff

277
style.c
View File

@@ -19,32 +19,51 @@
#include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". */
/* Mask for bits not included in style. */
#define STYLE_ATTR_MASK (~GRID_ATTR_CHARSET)
/* Default style. */
static struct style style_default = {
{ 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } },
STYLE_ALIGN_DEFAULT,
STYLE_LIST_OFF,
STYLE_RANGE_NONE, 0
};
/*
* 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
* alredy.
*/
int
style_parse(const struct grid_cell *defgc, struct grid_cell *gc,
const char *in)
style_parse(struct style *sy, const struct grid_cell *base, const char *in)
{
struct grid_cell savedgc;
const char delimiters[] = " ,";
char tmp[32];
int val, fg, bg, attr, flags;
size_t end;
struct style saved;
const char delimiters[] = " ,", *cp;
char tmp[256], *found;
int value;
size_t end;
if (*in == '\0')
return (0);
if (strchr(delimiters, in[strlen(in) - 1]) != NULL)
return (-1);
memcpy(&savedgc, gc, sizeof savedgc);
style_copy(&saved, sy);
fg = gc->fg;
bg = gc->bg;
attr = gc->attr;
flags = gc->flags;
do {
while (*in != '\0' && strchr(delimiters, *in) != NULL) {
in++;
end--;
}
if (*in == '\0')
break;
end = strcspn(in, delimiters);
if (end > (sizeof tmp) - 1)
goto error;
@@ -52,74 +71,164 @@ style_parse(const struct grid_cell *defgc, struct grid_cell *gc,
tmp[end] = '\0';
if (strcasecmp(tmp, "default") == 0) {
fg = defgc->fg;
bg = defgc->bg;
attr = defgc->attr;
flags = defgc->flags;
sy->gc.fg = base->fg;
sy->gc.bg = base->bg;
sy->gc.attr = base->attr;
sy->gc.flags = base->flags;
} else if (strcasecmp(tmp, "nolist") == 0)
sy->list = STYLE_LIST_OFF;
else if (strncasecmp(tmp, "list=", 5) == 0) {
if (strcasecmp(tmp + 5, "on") == 0)
sy->list = STYLE_LIST_ON;
else if (strcasecmp(tmp + 5, "focus") == 0)
sy->list = STYLE_LIST_FOCUS;
else if (strcasecmp(tmp + 5, "left-marker") == 0)
sy->list = STYLE_LIST_LEFT_MARKER;
else if (strcasecmp(tmp + 5, "right-marker") == 0)
sy->list = STYLE_LIST_RIGHT_MARKER;
else
goto error;
} else if (strcasecmp(tmp, "norange") == 0) {
sy->range_type = style_default.range_type;
sy->range_argument = style_default.range_type;
} 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;
} else if (strcasecmp(tmp + 6, "right") == 0) {
if (found != NULL)
goto error;
sy->range_type = STYLE_RANGE_RIGHT;
sy->range_argument = 0;
} else if (strcasecmp(tmp + 6, "window") == 0) {
if (found == NULL)
goto error;
sy->range_type = STYLE_RANGE_WINDOW;
sy->range_argument = atoi(found);
}
} else if (strcasecmp(tmp, "noalign") == 0)
sy->align = style_default.align;
else if (end > 6 && strncasecmp(tmp, "align=", 6) == 0) {
if (strcasecmp(tmp + 6, "left") == 0)
sy->align = STYLE_ALIGN_LEFT;
else if (strcasecmp(tmp + 6, "centre") == 0)
sy->align = STYLE_ALIGN_CENTRE;
else if (strcasecmp(tmp + 6, "right") == 0)
sy->align = STYLE_ALIGN_RIGHT;
else
goto error;
} else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) {
if ((val = colour_fromstring(tmp + 3)) == -1)
if ((value = colour_fromstring(tmp + 3)) == -1)
goto error;
if (*in == 'f' || *in == 'F') {
if (val != 8)
fg = val;
if (value != 8)
sy->gc.fg = value;
else
fg = defgc->fg;
sy->gc.fg = base->fg;
} else if (*in == 'b' || *in == 'B') {
if (val != 8)
bg = val;
if (value != 8)
sy->gc.bg = value;
else
bg = defgc->bg;
sy->gc.bg = base->bg;
} else
goto error;
} else if (strcasecmp(tmp, "none") == 0)
attr = 0;
sy->gc.attr = 0;
else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) {
if ((val = attributes_fromstring(tmp + 2)) == -1)
if ((value = attributes_fromstring(tmp + 2)) == -1)
goto error;
attr &= ~val;
sy->gc.attr &= ~value;
} else {
if ((val = attributes_fromstring(tmp)) == -1)
if ((value = attributes_fromstring(tmp)) == -1)
goto error;
attr |= val;
sy->gc.attr |= value;
}
in += end + strspn(in + end, delimiters);
} while (*in != '\0');
gc->fg = fg;
gc->bg = bg;
gc->attr = attr;
gc->flags = flags;
return (0);
error:
memcpy(gc, &savedgc, sizeof *gc);
style_copy(sy, &saved);
return (-1);
}
/* Convert style to a string. */
const char *
style_tostring(struct grid_cell *gc)
style_tostring(struct style *sy)
{
int off = 0, comma = 0;
static char s[256];
struct grid_cell *gc = &sy->gc;
int off = 0;
const char *comma = "", *tmp;
static char s[256];
char b[16];
*s = '\0';
if (sy->list != STYLE_LIST_OFF) {
if (sy->list == STYLE_LIST_ON)
tmp = "on";
else if (sy->list == STYLE_LIST_FOCUS)
tmp = "focus";
else if (sy->list == STYLE_LIST_LEFT_MARKER)
tmp = "left-marker";
else if (sy->list == STYLE_LIST_RIGHT_MARKER)
tmp = "right-marker";
off += xsnprintf(s + off, sizeof s - off, "%slist=%s", comma,
tmp);
comma = ",";
}
if (sy->range_type != STYLE_RANGE_NONE) {
if (sy->range_type == STYLE_RANGE_LEFT)
tmp = "left";
else if (sy->range_type == STYLE_RANGE_RIGHT)
tmp = "right";
else if (sy->range_type == STYLE_RANGE_WINDOW) {
snprintf(b, sizeof b, "window|%u", sy->range_argument);
tmp = b;
}
off += xsnprintf(s + off, sizeof s - off, "%srange=%s", comma,
tmp);
comma = ",";
}
if (sy->align != STYLE_ALIGN_DEFAULT) {
if (sy->align == STYLE_ALIGN_LEFT)
tmp = "left";
else if (sy->align == STYLE_ALIGN_CENTRE)
tmp = "centre";
else if (sy->align == STYLE_ALIGN_RIGHT)
tmp = "right";
off += xsnprintf(s + off, sizeof s - off, "%salign=%s", comma,
tmp);
comma = ",";
}
if (gc->fg != 8) {
off += xsnprintf(s, sizeof s, "fg=%s", colour_tostring(gc->fg));
comma = 1;
off += xsnprintf(s + off, sizeof s - off, "%sfg=%s", comma,
colour_tostring(gc->fg));
comma = ",";
}
if (gc->bg != 8) {
off += xsnprintf(s + off, sizeof s - off, "%sbg=%s",
comma ? "," : "", colour_tostring(gc->bg));
comma = 1;
off += xsnprintf(s + off, sizeof s - off, "%sbg=%s", comma,
colour_tostring(gc->bg));
comma = ",";
}
if (gc->attr != 0 && gc->attr != GRID_ATTR_CHARSET) {
xsnprintf(s + off, sizeof s - off, "%s%s",
comma ? "," : "", attributes_tostring(gc->attr));
xsnprintf(s + off, sizeof s - off, "%s%s", comma,
attributes_tostring(gc->attr));
comma = ",";
}
if (*s == '\0')
@@ -131,38 +240,66 @@ style_tostring(struct grid_cell *gc)
void
style_apply(struct grid_cell *gc, struct options *oo, const char *name)
{
const struct grid_cell *gcp;
struct style *sy;
memcpy(gc, &grid_default_cell, sizeof *gc);
gcp = options_get_style(oo, name);
gc->fg = gcp->fg;
gc->bg = gcp->bg;
gc->attr |= gcp->attr;
sy = options_get_style(oo, name);
gc->fg = sy->gc.fg;
gc->bg = sy->gc.bg;
gc->attr |= sy->gc.attr;
}
/* Apply a style, updating if default. */
void
style_apply_update(struct grid_cell *gc, struct options *oo, const char *name)
{
const struct grid_cell *gcp;
struct style *sy;
gcp = options_get_style(oo, name);
if (gcp->fg != 8)
gc->fg = gcp->fg;
if (gcp->bg != 8)
gc->bg = gcp->bg;
if (gcp->attr != 0)
gc->attr |= gcp->attr;
sy = options_get_style(oo, name);
if (sy->gc.fg != 8)
gc->fg = sy->gc.fg;
if (sy->gc.bg != 8)
gc->bg = sy->gc.bg;
if (sy->gc.attr != 0)
gc->attr |= sy->gc.attr;
}
/* Check if two styles are the same. */
int
style_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
/* Initialize style from cell. */
void
style_set(struct style *sy, const struct grid_cell *gc)
{
return (gc1->fg == gc2->fg &&
gc1->bg == gc2->bg &&
(gc1->flags & ~GRID_FLAG_PADDING) ==
(gc2->flags & ~GRID_FLAG_PADDING) &&
(gc1->attr & ~GRID_ATTR_CHARSET) ==
(gc2->attr & ~GRID_ATTR_CHARSET));
memcpy(sy, &style_default, sizeof *sy);
memcpy(&sy->gc, gc, sizeof sy->gc);
}
/* Copy style. */
void
style_copy(struct style *dst, struct style *src)
{
memcpy(dst, src, sizeof *dst);
}
/* Check if two styles are (visibly) the same. */
int
style_equal(struct style *sy1, struct style *sy2)
{
struct grid_cell *gc1 = &sy1->gc;
struct grid_cell *gc2 = &sy2->gc;
if (gc1->fg != gc2->fg)
return (0);
if (gc1->bg != gc2->bg)
return (0);
if ((gc1->attr & STYLE_ATTR_MASK) != (gc2->attr & STYLE_ATTR_MASK))
return (0);
if (sy1->align != sy2->align)
return (0);
return (1);
}
/* Is this style default? */
int
style_is_default(struct style *sy)
{
return (style_equal(sy, &style_default));
}

611
tmux.1
View File

@@ -234,7 +234,7 @@ If no commands are specified, the
.Ic new-session
command is assumed.
.El
.Sh KEY BINDINGS
.Sh DEFAULT KEY BINDINGS
.Nm
may be controlled from an attached client by using a key combination of a
prefix key,
@@ -255,6 +255,7 @@ client.
.It !
Break the current pane out of the window.
.It \&"
.\" "
Split the current pane into two, top and bottom.
.It #
List all paste buffers.
@@ -628,13 +629,13 @@ refers to a
.Nm
command, passed with the command and arguments separately, for example:
.Bd -literal -offset indent
bind-key F1 set-window-option force-width 81
bind-key F1 set-option status off
.Ed
.Pp
Or if using
.Xr sh 1 :
.Bd -literal -offset indent
$ tmux bind-key F1 set-window-option force-width 81
$ tmux bind-key F1 set-option status off
.Ed
.Pp
Multiple commands may be specified together as part of a
@@ -850,11 +851,22 @@ and
are the name of and shell command to execute in the initial window.
With
.Fl d ,
the initial size is 80 x 24;
the initial size comes from the global
.Ar default-size
option;
.Fl x
and
.Fl y
can be used to specify a different size.
.Ql -
uses the size of the current client if any.
If
.Fl x
or
.Fl y
is given, the
.Ar default-size
option is set for the session.
.Pp
If run from a terminal, any
.Xr termios 4
@@ -919,9 +931,10 @@ is used, the
.Ic update-environment
option will not be applied.
.It Xo Ic refresh-client
.Op Fl cDlLRSU
.Op Fl C Ar width,height
.Op Fl S
.Op Fl t Ar target-client
.Op Ar adjustment
.Xc
.D1 (alias: Ic refresh )
Refresh the current client if bound to a key, or a single client if one is given
@@ -931,8 +944,57 @@ If
.Fl S
is specified, only update the client's status line.
.Pp
The
.Fl U ,
.Fl D ,
.Fl L
.Fl R ,
and
.Fl c
flags allow the visible portion of a window which is larger than the client
to be changed.
.Fl U
moves the visible part up by
.Ar adjustment
rows and
.Fl D
down,
.Fl L
left by
.Ar adjustment
columns and
.Fl R
right.
.Fl c
returns to tracking the cursor automatically.
If
.Ar adjustment
is omitted, 1 is used.
Note that the visible position is a property of the client not of the
window, changing the current window in the attached session will reset
it.
.Pp
.Fl C
sets the width and height of a control client.
.Fl l
requests the clipboard from the client using the
.Xr xterm 1
escape sequence and stores it in a new paste buffer.
.Pp
.Fl L ,
.Fl R ,
.Fl U
and
.Fl D
move the visible portion of the window left, right, up or down
by
.Ar adjustment ,
if the window is larger than the client.
.Fl c
resets so that the position follows the cursor.
See the
.Ic window-size
option.
.It Xo Ic rename-session
.Op Fl t Ar target-session
.Ar new-name
@@ -966,7 +1028,7 @@ show debugging information about jobs and terminals.
Execute commands from
.Ar path
(which may be a
.Xr glob 3
.Xr glob 7
pattern).
If
.Fl q
@@ -1463,6 +1525,8 @@ The following keys may be used in tree mode:
.It Li "Enter" Ta "Choose selected item"
.It Li "Up" Ta "Select previous item"
.It Li "Down" Ta "Select next item"
.It Li "x" Ta "Kill selected item"
.It Li "X" Ta "Kill tagged items"
.It Li "<" Ta "Scroll list of previews left"
.It Li ">" Ta "Scroll list of previews right"
.It Li "C-s" Ta "Search by name"
@@ -1506,6 +1570,7 @@ first.
This command works only if at least one client is attached.
.It Xo
.Ic display-panes
.Op Fl b
.Op Fl d Ar duration
.Op Fl t Ar target-client
.Op Ar template
@@ -1539,8 +1604,11 @@ substituted by the pane ID.
The default
.Ar template
is "select-pane -t '%%'".
With
.Fl b ,
other commands are not blocked from running until the indicator is closed.
.It Xo Ic find-window
.Op Fl CNT
.Op Fl CNTZ
.Op Fl t Ar target-pane
.Ar match-string
.Xc
@@ -1559,6 +1627,8 @@ matches only the window name and
matches only the window title.
The default is
.Fl CNT .
.Fl Z
zooms the pane.
.Pp
This command works only if at least one client is attached.
.It Xo Ic join-pane
@@ -1912,6 +1982,38 @@ and unzoomed (its normal position in the layout).
.Fl M
begins mouse resizing (only valid if bound to a mouse key binding, see
.Sx MOUSE SUPPORT ) .
.It Xo Ic resize-window
.Op Fl aADLRU
.Op Fl t Ar target-window
.Op Fl x Ar width
.Op Fl y Ar height
.Op Ar adjustment
.Xc
.D1 (alias: Ic resizew )
Resize a window, up, down, left or right by
.Ar adjustment
with
.Fl U ,
.Fl D ,
.Fl L
or
.Fl R ,
or
to an absolute size
with
.Fl x
or
.Fl y .
The
.Ar adjustment
is given in lines or cells (the default is 1).
.Fl A
sets the size of the largest session containing the window;
.Fl a
the size of the smallest.
This command will automatically set
.Ic window-size
to manual in the window options.
.It Xo Ic respawn-pane
.Op Fl c Ar start-directory
.Op Fl k
@@ -2655,6 +2757,16 @@ The default is an empty string, which instructs
to create a login shell using the value of the
.Ic default-shell
option.
.It Ic default-size Ar XxY
Set the default size of new windows when the
.Ar window-size
option is set to manual or when a session is created with
.Ic new-session
.Fl d .
The value is the width and height separated by an
.Ql x
character.
The default is 80x24.
.It Ic default-shell Ar path
Specify the default shell.
This is used as the login shell for new windows when the
@@ -2671,6 +2783,10 @@ or
This option should be configured when
.Nm
is used as a login shell.
.It Ic default-size Ar XxY
Set the default size of windows when the size is not set or the
.Ic window-size
option is manual.
.It Xo Ic destroy-unattached
.Op Ic on | off
.Xc
@@ -2724,74 +2840,19 @@ The default is to run
with
.Fl np .
.It Ic message-command-style Ar style
Set status line message command style, where
.Ar style
is a comma-separated list of characteristics to be specified.
.Pp
These may be
.Ql bg=colour
to set the background colour,
.Ql fg=colour
to set the foreground colour, and a list of attributes as specified below.
.Pp
The colour is one of:
.Ic black ,
.Ic red ,
.Ic green ,
.Ic yellow ,
.Ic blue ,
.Ic magenta ,
.Ic cyan ,
.Ic white ,
aixterm bright variants (if supported:
.Ic brightred ,
.Ic brightgreen ,
and so on),
.Ic colour0
to
.Ic colour255
from the 256-colour set,
.Ic default ,
or a hexadecimal RGB string such as
.Ql #ffffff ,
which chooses the closest match from the default 256-colour set.
.Pp
The attributes is either
.Ic none
or a comma-delimited list of one or more of:
.Ic bright
(or
.Ic bold ) ,
.Ic dim ,
.Ic underscore ,
.Ic blink ,
.Ic reverse ,
.Ic hidden ,
.Ic italics ,
or
.Ic strikethrough
to turn an attribute on, or an attribute prefixed with
.Ql no
to turn one off.
.Pp
Examples are:
.Bd -literal -offset indent
fg=yellow,bold,underscore,blink
bg=black,fg=default,noreverse
.Ed
.Pp
With the
.Fl a
flag to the
.Ic set-option
command the new style is added otherwise the existing style is replaced.
Set status line message command style.
For how to specify
.Ar style ,
see the
.Sx STYLES
section.
.It Ic message-style Ar style
Set status line message style.
For how to specify
.Ar style ,
see the
.Ic message-command-style
option.
.Sx STYLES
section.
.It Xo Ic mouse
.Op Ic on | off
.Xc
@@ -2867,9 +2928,22 @@ is on.
The values are the same as those for
.Ic activity-action .
.It Xo Ic status
.Op Ic on | off
.Op Ic off | on | 2 | 3 | 4 | 5
.Xc
Show or hide the status line.
Show or hide the status line or specify its size.
Using
.Ic on
gives a status line one row in height;
.Ic 2 ,
.Ic 3 ,
.Ic 4
or
.Ic 5
more rows.
.It Ic status-format[] Ar format
Specify the format to be used for each line of the status line.
The default builds the top status line from the various individual status
options below.
.It Ic status-interval Ar interval
Update the status line every
.Ar interval
@@ -2898,17 +2972,12 @@ Display
(by default the session name) to the left of the status line.
.Ar string
will be passed through
.Xr strftime 3
and formats (see
.Sx FORMATS )
will be expanded.
It may also contain the special character sequence #[] to change the colour
or attributes, for example
.Ql #[fg=red,bright]
to set a bright red foreground.
See the
.Ic message-command-style
option for a description of colours and attributes.
.Xr strftime 3 .
Also see the
.Sx FORMATS
and
.Sx STYLES
sections.
.Pp
For details on how the names and titles can be set see the
.Sx "NAMES AND TITLES"
@@ -2932,8 +3001,8 @@ Set the style of the left part of the status line.
For how to specify
.Ar style ,
see the
.Ic message-command-style
option.
.Sx STYLES
section.
.It Xo Ic status-position
.Op Ic top | bottom
.Xc
@@ -2960,15 +3029,15 @@ Set the style of the right part of the status line.
For how to specify
.Ar style ,
see the
.Ic message-command-style
option.
.Sx STYLES
section.
.It Ic status-style Ar style
Set status line style.
For how to specify
.Ar style ,
see the
.Ic message-command-style
option.
.Sx STYLES
section.
.It Ic update-environment[] Ar variable
Set list of environment variables to be copied into the session environment
when a new session is created or an existing session is attached.
@@ -3052,10 +3121,13 @@ Supported window options are:
Aggressively resize the chosen window.
This means that
.Nm
will resize the window to the size of the smallest session for which it is the
current window, rather than the smallest session to which it is attached.
The window may resize when the current window is changed on another sessions;
this option is good for full-screen programs which support
will resize the window to the size of the smallest or largest session
(see the
.Ic window-size
option) for which it is the current window, rather than the session to
which it is attached.
The window may resize when the current window is changed on another
session; this option is good for full-screen programs which support
.Dv SIGWINCH
and poor for interactive programs such as shells.
.Pp
@@ -3118,16 +3190,6 @@ Set clock colour.
.Xc
Set clock hour format.
.Pp
.It Ic force-height Ar height
.It Ic force-width Ar width
Prevent
.Nm
from resizing a window to greater than
.Ar width
or
.Ar height .
A value of zero restores the default unlimited setting.
.Pp
.It Ic main-pane-height Ar height
.It Ic main-pane-width Ar width
Set the width or height of the main (left or top) pane in the
@@ -3152,8 +3214,8 @@ Set window modes style.
For how to specify
.Ar style ,
see the
.Ic message-command-style
option.
.Sx STYLES
section.
.Pp
.It Xo Ic monitor-activity
.Op Ic on | off
@@ -3201,8 +3263,8 @@ Set the pane border style for the currently active pane.
For how to specify
.Ar style ,
see the
.Ic message-command-style
option.
.Sx STYLES
section.
Attributes are ignored.
.Pp
.It Ic pane-base-index Ar index
@@ -3223,8 +3285,8 @@ Set the pane border style for panes aside from the active pane.
For how to specify
.Ar style ,
see the
.Ic message-command-style
option.
.Sx STYLES
section.
Attributes are ignored.
.Pp
.It Xo Ic remain-on-exit
@@ -3247,24 +3309,24 @@ Set the style for the window's active pane.
For how to specify
.Ar style ,
see the
.Ic message-command-style
option.
.Sx STYLES
section.
.Pp
.It Ic window-status-activity-style Ar style
Set status line style for windows with an activity alert.
For how to specify
.Ar style ,
see the
.Ic message-command-style
option.
.Sx STYLES
section.
.Pp
.It Ic window-status-bell-style Ar style
Set status line style for windows with a bell alert.
For how to specify
.Ar style ,
see the
.Ic message-command-style
option.
.Sx STYLES
section.
.Pp
.It Ic window-status-current-format Ar string
Like
@@ -3276,24 +3338,24 @@ Set status line style for the currently active window.
For how to specify
.Ar style ,
see the
.Ic message-command-style
option.
.Sx STYLES
section.
.Pp
.It Ic window-status-format Ar string
Set the format in which the window is displayed in the status line window list.
See the
.Ar status-left
option for details of special character sequences available.
The default is
.Ql #I:#W#F .
.Sx FORMATS
and
.Sx STYLES
sections.
.Pp
.It Ic window-status-last-style Ar style
Set status line style for the last active window.
For how to specify
.Ar style ,
see the
.Ic message-command-style
option.
.Sx STYLES
section.
.Pp
.It Ic window-status-separator Ar string
Sets the separator drawn between windows in the status line.
@@ -3304,7 +3366,29 @@ Set status line style for a single window.
For how to specify
.Ar style ,
see the
.Ic message-command-style
.Sx STYLES
section.
.Pp
.It Xo Ic Ic window-size
.Ar largest | Ar smallest | Ar manual
.Xc
Configure how
.Nm
determines the window size.
If set to
.Ar largest ,
the size of the largest attached session is used; if
.Ar smallest ,
the size of the smallest.
If
.Ar manual ,
the size of a new window is set from the
.Ic default-size
option and windows are resized automatically.
See also the
.Ic resize-window
command and the
.Ic aggressive-resize
option.
.Pp
.It Ic window-style Ar style
@@ -3312,8 +3396,23 @@ Set the default window style.
For how to specify
.Ar style ,
see the
.Ic message-command-style
option.
.Sx STYLES
section.
.Pp
.It Xo Ic window-size
.Op Ic smallest | largest | manual
.Xc
Tell
.Nm
how to automatically size windows either the size of the smallest session
containing the window, the size of the largest, or manual size.
See also the
.Ic resize-window
command and the
.Ic default-size
and
.Ic aggressive-resize
options.
.Pp
.It Xo Ic wrap-search
.Op Ic on | off
@@ -3424,6 +3523,14 @@ Run when the program running in a pane exits, but
is on so the pane has not closed.
.It pane-exited
Run when the program running in a pane exits.
.It pane-focus-in
Run when the focus enters a pane, if the
.Ic focus-events
option is on.
.It pane-focus-out
Run when the focus exits a pane, if the
.Ic focus-events
option is on.
.It pane-set-clipboard
Run when the terminal clipboard is set using the
.Xr xterm 1
@@ -3445,12 +3552,14 @@ Run when a window is unlinked from a session.
Hooks are managed with these commands:
.Bl -tag -width Ds
.It Xo Ic set-hook
.Op Fl gu
.Op Fl gRu
.Op Fl t Ar target-session
.Ar hook-name
.Ar command
.Xc
Sets (or with
Without
.Fl R ,
sets (or with
.Fl u
unsets) hook
.Ar hook-name
@@ -3466,6 +3575,12 @@ hooks (for
with
.Fl t ) .
Like options, session hooks inherit from the global ones.
.Pp
With
.Fl R ,
run
.Ar hook-name
immediately.
.It Xo Ic show-hooks
.Op Fl g
.Op Fl t Ar target-session
@@ -3482,13 +3597,16 @@ option is on (the default is off),
allows mouse events to be bound as keys.
The name of each key is made up of a mouse event (such as
.Ql MouseUp1 )
and a location suffix (one of
.Ql Pane
for the contents of a pane,
.Ql Border
for a pane border or
.Ql Status
for the status line).
and a location suffix, one of the following:
.Bl -column "XXXXXXXXXXXXX" -offset indent
.It Li "Pane" Ta "the contents of a pane"
.It Li "Border" Ta "a pane border"
.It Li "Status" Ta "the status line window list"
.It Li "StatusLeft" Ta "the left part of the status line"
.It Li "StatusRight" Ta "the right part of the status line"
.It Li "StatusDefault" Ta "any other part of the status line"
.El
.Pp
The following mouse events are available:
.Bl -column "MouseDown1" "MouseDrag1" "WheelDown" -offset indent
.It Li "WheelUp" Ta "WheelDown" Ta ""
@@ -3545,11 +3663,17 @@ The possible variables are listed in the table below, or the name of a
.Nm
option may be used for an option's value.
Some variables have a shorter alias such as
.Ql #S ,
and
.Ql #S ;
.Ql ##
is replaced by a single
.Ql # .
.Ql # ,
.Ql #,
by a
.Ql \&,
and
.Ql #}
by a
.Ql } .
.Pp
Conditionals are available by prefixing with
.Ql \&?
@@ -3571,6 +3695,22 @@ if
is enabled, or
.Ql no
if not.
Conditionals can be nested arbitrarily.
Inside a conditional,
.Ql \&,
and
.Ql }
must be escaped as
.Ql #,
and
.Ql #} ,
unless they are part of a
.Ql #{...}
replacement.
For example:
.Bd -literal -offset indent
#{?pane_in_mode,#[fg=white#,bg=red],#[fg=red#,bg=white]}#W .
.Ed
.Pp
Comparisons may be expressed by prefixing two comma-separated
alternatives by
@@ -3598,7 +3738,7 @@ and
.Ql &&
evaluate to true if either or both of two comma-separated alternatives are
true, for example
.Ql #{||,#{pane_in_mode},#{alternate_on}} .
.Ql #{||:#{pane_in_mode},#{alternate_on}} .
A
.Ql C
performs a search for an
@@ -3634,6 +3774,35 @@ prefixes are
and
.Xr dirname 3
of the variable respectively.
.Ql q:
will escape
.Xr sh 1
special characters.
.Ql E:
will expand the format twice, for example
.Ql #{E:status-left}
is the result of expanding the content of the
.Ic status-left
option rather than the content itself.
.Ql T:
is like
.Ql E:
but also expands
.Xr strftime 3
specifiers.
.Ql S: ,
.Ql W:
or
.Ql P:
will loop over each session, window or pane 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.
For example, to get a list of windows formatted like the status line:
.Bd -literal -offset indent
#{W:#{E:window-status-format} ,#{E:window-status-current-format} }
.Ed
.Pp
A prefix of the form
.Ql s/foo/bar/:
will substitute
@@ -3694,6 +3863,7 @@ The following variables are available, where appropriate:
.It Li "command_list_alias" Ta "" Ta "Command alias if listing commands"
.It Li "command_list_usage" Ta "" Ta "Command usage if listing commands"
.It Li "cursor_flag" Ta "" Ta "Pane cursor flag"
.It Li "cursor_character" Ta "" Ta "Character at cursor in pane"
.It Li "cursor_x" Ta "" Ta "Cursor X position in pane"
.It Li "cursor_y" Ta "" Ta "Cursor Y position in pane"
.It Li "history_bytes" Ta "" Ta "Number of bytes in window history"
@@ -3745,6 +3915,7 @@ The following variables are available, where appropriate:
.It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane"
.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"
.It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane"
.It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane"
.It Li "scroll_position" Ta "" Ta "Scroll position in copy mode"
@@ -3759,12 +3930,10 @@ The following variables are available, where appropriate:
.It Li "session_group_size" Ta "" Ta "Size of session group"
.It Li "session_group_list" Ta "" Ta "List of sessions in group"
.It Li "session_grouped" Ta "" Ta "1 if session in a group"
.It Li "session_height" Ta "" Ta "Height of session"
.It Li "session_id" Ta "" Ta "Unique session ID"
.It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached"
.It Li "session_name" Ta "#S" Ta "Name of session"
.It Li "session_stack" Ta "" Ta "Window indexes in most recent order"
.It Li "session_width" Ta "" Ta "Width of session"
.It Li "session_windows" Ta "" Ta "Number of windows in session"
.It Li "socket_path" Ta "" Ta "Server socket path"
.It Li "start_time" Ta "" Ta "Server start time"
@@ -3773,6 +3942,8 @@ The following variables are available, where appropriate:
.It Li "window_activity_flag" Ta "" Ta "1 if window has activity"
.It Li "window_active" Ta "" Ta "1 if window active"
.It Li "window_bell_flag" Ta "" Ta "1 if window has bell"
.It Li "window_bigger" Ta "" Ta "1 if window is larger than client"
.It Li "window_end_flag" Ta "" Ta "1 if window has the highest index"
.It Li "window_flags" Ta "#F" Ta "Window flags"
.It Li "window_format" Ta "" Ta "1 if format is for a window (not assuming the current)"
.It Li "window_height" Ta "" Ta "Height of window"
@@ -3782,14 +3953,137 @@ The following variables are available, where appropriate:
.It Li "window_layout" Ta "" Ta "Window layout description, ignoring zoomed window panes"
.It Li "window_linked" Ta "" Ta "1 if window is linked across sessions"
.It Li "window_name" Ta "#W" Ta "Name of window"
.It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client"
.It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client"
.It Li "window_panes" Ta "" Ta "Number of panes in window"
.It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert"
.It Li "window_stack_index" Ta "" Ta "Index in session most recent stack"
.It Li "window_start_flag" Ta "" Ta "1 if window has the lowest index"
.It Li "window_visible_layout" Ta "" Ta "Window layout description, respecting zoomed window panes"
.It Li "window_width" Ta "" Ta "Width of window"
.It Li "window_zoomed_flag" Ta "" Ta "1 if window is zoomed"
.It Li "wrap_flag" Ta "" Ta "Pane wrap flag"
.El
.Sh STYLES
.Nm
offers various options to specify the colour and attributes of aspects of the
interface, for example
.Ic status-style
for the status line.
In addition, embedded styles may be specified in format options, such as
.Ic status-left-format ,
by enclosing them in
.Ql #[
and
.Ql ] .
.Pp
A style may be the single term
.Ql default
to specify the default style (which may inherit from another option) or a space
or comma separated list of the following:
.Bl -tag -width Ds
.It Ic fg=colour
Set the foreground colour.
The colour is one of:
.Ic black ,
.Ic red ,
.Ic green ,
.Ic yellow ,
.Ic blue ,
.Ic magenta ,
.Ic cyan ,
.Ic white ;
if supported the bright variants
.Ic brightred ,
.Ic brightgreen ,
.Ic brightyellow ;
.Ic colour0
to
.Ic colour255
from the 256-colour set;
.Ic default
for the default colour;
.Ic terminal
for the terminal default colour; or a hexadecimal RGB string such as
.Ql #ffffff .
.It Ic bg=colour
Set the background colour.
.It Ic none
Set no attributes (turn off any active attributes).
.It Xo Ic bright
(or
.Ic bold ) ,
.Ic dim ,
.Ic underscore ,
.Ic blink ,
.Ic reverse ,
.Ic hidden ,
.Ic italics ,
.Ic strikethrough ,
.Ic double-underscore ,
.Ic curly-underscore ,
.Ic dotted-underscore ,
.Ic dashed-underscore
.Xc
Set an attribute.
Any of the attributes may be prefixed with
.Ql no
to unset.
.It Xo Ic align=left
(or
.Ic noalign ) ,
.Ic align=centre ,
.Ic align=right
.Xc
Align text to the left, centre or right of the available space if appropriate.
.It Xo Ic list=on ,
.Ic list=focus ,
.Ic list=left-marker ,
.Ic list=right=marker ,
.Ic nolist
.Xc
Mark the position of the various window list components in the
.Ic status-format
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);
.Ic list=left-marker
and
.Ic list=right-marker
mark the text to be used to mark that text has been trimmed from the left or
right of the list if there is not enough space.
.It Xo Ic range=left ,
.Ic range=right ,
.Ic range=window|X ,
.Ic norange
.Xc
Mark a range in the
. Ic status-format
option.
.Ic range=left
and
.Ic range=right
are the text used for the
.Ql StatusLeft
and
.Ql StatusRight
mouse keys.
.Ic range=window|X
is the range for a window passed to the
.Ql Status
mouse key, where
.Ql X
is a window index.
.El
.Pp
Examples are:
.Bd -literal -offset indent
fg=yellow bold underscore blink
bg=black,fg=default,noreverse
.Ed
.Sh NAMES AND TITLES
.Nm
distinguishes between names and titles.
@@ -3826,7 +4120,9 @@ for
or
.Ic new-session ) .
.It
An escape sequence:
An escape sequence (if the
.Ic allow-rename
option is turned on):
.Bd -literal -offset indent
$ printf '\e033kWINDOW_NAME\e033\e\e'
.Ed
@@ -3839,7 +4135,7 @@ option.
.El
.Pp
When a pane is first created, its title is the hostname.
A pane's title can be set via the OSC title setting sequence, for example:
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'
.Ed
@@ -3915,15 +4211,20 @@ is used, the output is formatted as a set of Bourne shell commands.
.Nm
includes an optional status line which is displayed in the bottom line of each
terminal.
By default, the status line is enabled (it may be disabled with the
.Pp
By default, the status line is enabled and one line in height (it may be
disabled or made multiple lines with the
.Ic status
session option) and contains, from left-to-right: the name of the current
session in square brackets; the window list; the title of the active pane
in double quotes; and the time and date.
.Pp
The status line is made of three parts: configurable left and right sections
(which may contain dynamic content such as the time or output from a shell
command, see the
Each line of the status line is configured with the
.Ic status-format
option.
The default is made of three parts: configurable left and right sections (which
may contain dynamic content such as the time or output from a shell command,
see the
.Ic status-left ,
.Ic status-left-length ,
.Ic status-right ,
@@ -4068,7 +4369,7 @@ option.
This command works only from inside
.Nm .
.It Xo Ic display-message
.Op Fl p
.Op Fl apv
.Op Fl c Ar target-client
.Op Fl t Ar target-pane
.Op Ar message
@@ -4090,6 +4391,11 @@ if
.Fl t
is given, otherwise the active pane for the session attached to
.Ar target-client .
.Pp
.Fl v
prints verbose logging as the format is parsed and
.Fl a
lists the format variables and their values.
.El
.Sh BUFFERS
.Nm
@@ -4354,8 +4660,6 @@ is used, the channel is locked and any clients that try to lock the same
channel are made to wait until the channel is unlocked with
.Ic wait-for
.Fl U .
This command only works from outside
.Nm .
.El
.Sh TERMINFO EXTENSIONS
.Nm
@@ -4372,6 +4676,11 @@ to change the cursor colour from inside
.Bd -literal -offset indent
$ printf '\e033]12;red\e033\e\e'
.Ed
.It Em \&Smulx
Set a styled underline.
The single parameter is one of: 0 for no underline, 1 for normal
underline, 2 for double underline, 3 for curly underline, 4 for dotted
underline and 5 for dashed underline.
.It Em \&Ss , Se
Set or reset the cursor style.
If set, a sequence such as this may be used
@@ -4388,7 +4697,7 @@ Indicate that the terminal supports the
.Ql direct colour
RGB escape sequence (for example, \ee[38;2;255;255;255m).
.Pp
If supported, this is used for the OSC initialize colour escape sequence (which
If supported, this is used for the initialize colour escape sequence (which
may be enabled by adding the
.Ql initc
and

29
tmux.c
View File

@@ -163,6 +163,31 @@ setblocking(int fd, int state)
}
}
const char *
find_cwd(void)
{
char resolved1[PATH_MAX], resolved2[PATH_MAX];
static char cwd[PATH_MAX];
const char *pwd;
if (getcwd(cwd, sizeof cwd) == NULL)
return (NULL);
if ((pwd = getenv("PWD")) == NULL || *pwd == '\0')
return (cwd);
/*
* We want to use PWD so that symbolic links are maintained,
* but only if it matches the actual working directory.
*/
if (realpath(pwd, resolved1) == NULL)
return (cwd);
if (realpath(cwd, resolved2) == NULL)
return (cwd);
if (strcmp(resolved1, resolved2) != 0)
return (cwd);
return (pwd);
}
const char *
find_home(void)
{
@@ -188,7 +213,6 @@ int
main(int argc, char **argv)
{
char *path, *label, *cause, **var;
char tmp[PATH_MAX];
const char *s, *shell, *cwd;
int opt, flags, keys;
const struct options_table_entry *oe;
@@ -293,8 +317,7 @@ main(int argc, char **argv)
global_environ = environ_create();
for (var = environ; *var != NULL; var++)
environ_put(global_environ, *var);
if ((cwd = getenv("PWD")) == NULL &&
(cwd = getcwd(tmp, sizeof tmp)) != NULL)
if ((cwd = find_cwd()) != NULL)
environ_set(global_environ, "PWD", "%s", cwd);
global_options = options_create(NULL);

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