Compare commits

...

531 Commits
3.6a ... 3.7b

Author SHA1 Message Date
Nicholas Marriott
e802909de0 Fix so that the end of a synchronized update again triggers a redraw. 2026-07-01 17:49:40 +01:00
Nicholas Marriott
0e418b62d2 Remove C-r from help. 2026-06-30 07:44:51 +01:00
Nicholas Marriott
78a2145a47 Update CHANGES. 2026-06-30 07:42:25 +01:00
Nicholas Marriott
dbe50934b1 Add caching of scrollbar options to 3.7a branch. 2026-06-30 07:41:02 +01:00
nicm
166267c87a Allow :. in names again, forbidding them is overly pernickety. Only
forbid invalid UTF-8 and #(.
2026-06-29 19:53:42 +01:00
nicm
132a63d1da Allow empty window and session names. 2026-06-29 17:36:40 +01:00
nicm
4e612612dc Only forbid #( in names and titles (styles are #[ and are useful). 2026-06-29 08:11:08 +01:00
Nicholas Marriott
6c2ef75681 Bump version. 2026-06-28 16:54:51 +01:00
Nicholas Marriott
84291b021f Fix check so as to not use NULL name. 2026-06-27 10:58:39 +01:00
Nicholas Marriott
81f88f8517 Add a missing {. 2026-06-26 18:55:59 +01:00
Nicholas Marriott
4eafb27b89 tmux 3.7. 2026-06-26 18:50:58 +01:00
Nicholas Marriott
4fc4644652 Tweak. 2026-06-26 18:50:42 +01:00
nicm
e7f414416f Set check callback for menus so they aren't overwritten by sync. 2026-06-23 08:35:09 +01:00
nicm
3b8e25fef4 Initialize wname so it we are not freeing garbage if it is not used. 2026-06-17 09:11:00 +01:00
nicm
64e83caf04 Be more strict about what names and titles we allow and reject them
immediately when possible, but allow # again for those directly set by
commands (but not escape sequences). From Barrett Ruth in GitHub issue
5175.
2026-06-17 09:10:55 +01:00
nicm
762e503978 Allow rectangle selection to extend past end of current line to behave
the same as vi with virtualedit=block set. From Mark Kelly in GitHub
issue 5227.
2026-06-16 11:11:47 +01:00
Nicholas Marriott
c809aea278 Fix a merge problem. 2026-06-16 10:24:06 +01:00
Nicholas Marriott
1596f9c9f5 Bump version again. 2026-06-16 10:22:51 +01:00
nicm
2f28d9a168 Fix resizing floating pane with a left scrollbar, from Michael Grant. 2026-06-16 10:22:12 +01:00
Nicholas Marriott
baac29debc Prefer libtinfow if it is available, GitHub issue 5224 from Lars Wendler. 2026-06-16 10:21:12 +01:00
nicm
2c4df765c5 Fix missing border when drawing floating panes. From Michael Grant. 2026-06-16 10:21:09 +01:00
nicm
2d9afa6348 Use correct x position when drawing clipped line. 2026-06-15 18:39:43 +01:00
nicm
bae5c14941 Tidy up screen_redraw_get_visible_ranges by using a couple of
temporaries for start and end of range.
2026-06-15 18:39:28 +01:00
nicm
9cf8de099c Convert cursor position back to pane coordinates for tty_cmd_cell. 2026-06-15 18:39:23 +01:00
nicm
f98aaf544d When redrawing a whole pane line, fall into tty_draw_line for any
characters that are not plain ASCII. Fixes redraw issues with partial
tabs and other wide characters when a pane is partly outside the window.
2026-06-15 09:07:23 +01:00
nicm
78afc24736 Fix various errors in redrawing:
- Fix the active pane colour when only two panes and scrollbars enabled.

- Clip left and right scrollbars the same for floating panes.

- Do not subtract scrollbar width twice when working out width of status
  line.

- Check if a character is inside a visible range correctly (do not
  include the next position outside the range).
2026-06-15 09:06:58 +01:00
nicm
83e4534387 Skip floating panes when working out the top or bottom cell. Fixes
missing bottom status pane status line when floating panes exist.
2026-06-15 09:06:55 +01:00
nicm
dabaae1534 Make buffer creation time sort oldest first like it used to, but change
windows to match sessions and panes as newest first.
2026-06-12 10:19:05 +01:00
nicm
cc87db74c7 Two fixes for RI codepoints. Firstly, do not combine more than two of
them - previously we were ending up with four codepoints in one cell
which tmux believed to be width 2, but terminals considered width 4.
Secondly, invalidate cursor position before redrawing the cell when the
second codepoint is received, terminals vary in how they manage
backspace and cursor movement across these characters, so it is better
to use absolute rather than relative positioning. GitHub issue 4853.
2026-06-09 13:20:14 +01:00
Nicholas Marriott
aa1f0653e9 Bump version to 3.7-rc2. 2026-06-09 09:47:18 +01:00
nicm
8771b6051f Fix mouse events on tiled pane status line - when panes share a border,
prefer the pane for which the border is the status line. With Dane
Jensen.
2026-06-09 09:12:21 +01:00
Nicholas Marriott
bbd4768bb6 3.7-rc version. 2026-06-08 21:08:37 +01:00
Thomas Adam
cb237b5193 Merge branch 'obsd-master' 2026-06-08 16:30:06 +01:00
nicm
85d72b9952 Return immediately if the list is empty in mode_tree_key instead of
crashing, from Bryce Miller in GitHub issue 5170.
2026-06-08 15:16:21 +00:00
Thomas Adam
a04c33c42d Merge branch 'obsd-master' 2026-06-07 21:15:06 +01:00
nicm
529afada82 Move checking of whether the cursor is visible inside the if so that it
always hits the calculation of the oy offset when the status line is at
the top. From Michael Grant.
2026-06-07 20:05:16 +00:00
nicm
a532f4868f Add -H flag to capture-pane to show hyperlinks. 2026-06-07 20:03:02 +00:00
Thomas Adam
e1d37c37b5 Merge branch 'obsd-master' 2026-06-07 17:30:06 +01:00
nicm
ae5e0e8c05 Add -L to show line numbers with capture-pane and -F to show line flags. 2026-06-07 16:23:05 +00:00
Nicholas Marriott
bbea0ef439 Update copy mode vi test, from Max Vim. 2026-06-07 17:22:14 +01:00
Nicholas Marriott
a134c43f4d Update test result. 2026-06-07 16:15:32 +01:00
Nicholas Marriott
e0ebae4481 Fix a couple of tests and add -f/dev/null to them all. 2026-06-07 16:11:38 +01:00
Thomas Adam
1fbc51b69f Merge branch 'obsd-master' 2026-06-07 16:00:06 +01:00
nicm
42f3e7f7ce Clear entire lines when removing from history or freeing. 2026-06-07 14:41:27 +00:00
Thomas Adam
e163cbacec Merge branch 'obsd-master' 2026-06-07 15:30:06 +01:00
Nicholas Marriott
0ec6870902 Add to CHANGES. 2026-06-07 15:24:53 +01:00
Thomas Adam
55421ede70 Merge branch 'obsd-master' 2026-06-07 15:15:06 +01:00
nicm
3acd247f5d Redraw entire session when making a new pane. 2026-06-07 14:11:20 +00:00
Thomas Adam
59151704b4 Merge branch 'obsd-master' 2026-06-07 15:00:06 +01:00
nicm
343ee5ece1 Add -g flag to kill-session to kill all sessions in a group, GitHub
issue 5157 from github at jiku dot jp.
2026-06-07 13:51:42 +00:00
nicm
0256ee77c2 Add some additional environment variables needed for Wayland, from
shbernal dot 01 at gmail dot com.
2026-06-07 13:37:39 +00:00
Thomas Adam
72f5415fc8 Merge branch 'obsd-master' 2026-06-07 14:30:06 +01:00
nicm
ef8e9df02f Tweak previous. 2026-06-07 13:29:16 +00:00
nicm
306ee0eddb Add -E to split-window to explicitly make an empty pane rather than
requiring an empty command.
2026-06-07 13:15:28 +00:00
Nicholas Marriott
3d9e6bf454 Update CHANGES for 3.7. 2026-06-07 12:01:09 +01:00
Thomas Adam
0fc6cb4099 Merge branch 'obsd-master' 2026-06-07 11:15:06 +01:00
nicm
5744021ac9 Allow floating panes to be created partially off the window, based on a
change from Michael Grant.
2026-06-07 09:54:25 +00:00
Thomas Adam
54be05e5e4 Merge branch 'obsd-master' 2026-06-07 09:30:06 +01:00
nicm
c9e22ab67b Enable floating panes. These are panes which sit above the layout
("tiled panes") like popups but unlike popups are not modal and behave
like panes (so the same escape sequence support). Floating panes are
created with the "new-pane" command, bound to * by default.

Currently floating panes can only be moved and resized using the mouse.
The default second status line (if "status-format" is set to 2) has
changed to show a list of panes.

This is the first step and many obvious features are not yet complete
(notably the ability to swap floating panes, resize them using
resize-pane, change them between floating and tiles, and restore custom
layouts with floating panes).

Mostly written by Michael Grant with help from Dane Jensen.
2026-06-07 08:25:59 +00:00
nicm
4063f968c6 Do not allow swapping floating panes for the moment. 2026-06-07 08:22:58 +00:00
Thomas Adam
8388c53a47 Merge branch 'obsd-master' 2026-06-06 19:00:06 +01:00
nicm
3a72f3beb8 Use correct X position for visible range checks in
screen_write_fast_copy, GitHub issue 5164 from Barrett Ruth.
2026-06-06 17:35:30 +00:00
nicm
35709a30ee Change so that floating panes are always part of the root layout. From
Dane Jensen.
2026-06-06 17:30:48 +00:00
Thomas Adam
f9a683cf39 Merge branch 'obsd-master' 2026-06-05 09:15:07 +01:00
nicm
d7a2b52da8 Add a way to quote for command arguments and use for key in list-keys,
GitHub issue 5153.
2026-06-05 08:04:03 +00:00
Thomas Adam
f43adc364a Merge branch 'obsd-master' 2026-06-04 10:45:09 +01:00
nicm
6beebbe074 Fix scrollbar drag position when window is taller than tty, from Michael
Grant.
2026-06-04 09:24:03 +00:00
Thomas Adam
019f8eb278 Merge branch 'obsd-master' 2026-06-03 21:30:06 +01:00
nicm
580f9ced08 Change run-shell expansion to just be #{1} etc not #1 which interferes
with colours.
2026-06-03 20:18:49 +00:00
nicm
642cc15b99 Change so panes scroll when the cursor is at the top instead of halfway
down, from Michael Grant.
2026-06-03 20:16:14 +00:00
Thomas Adam
cfeade6055 Merge branch 'obsd-master' 2026-06-02 21:00:06 +01:00
nicm
390207cdf1 Use new layout functions for join-pane also, from Dane Jensen. 2026-06-02 19:31:36 +00:00
Thomas Adam
c97b2b95e6 Merge branch 'obsd-master' 2026-06-02 20:30:06 +01:00
nicm
a3bf632276 Move cell creation into layout.c, from Dane Jensen. 2026-06-02 19:25:06 +00:00
Thomas Adam
5368ee32dd github: update lock-threads to v6
Use the latest release of lock-threads.
2026-06-02 16:26:13 +01:00
Thomas Adam
e89f02bd8f Merge branch 'obsd-master' 2026-06-02 09:30:06 +01:00
nicm
ae6a749d3a Move the PANE_FLOATING flag into the layout cell and add an accessor.
From Dane Jensen.
2026-06-02 08:13:50 +00:00
Thomas Adam
0759030ee8 Merge branch 'obsd-master' 2026-06-02 08:30:06 +01:00
nicm
35f4c8b333 Fix border colour bug in the floating panes branch where inactive
floating pane borders incorrectly use the active colour at intersections
with tiled pane borders. From Michael Grant.
2026-06-02 07:11:49 +00:00
Thomas Adam
97ac32f536 Merge branch 'obsd-master' 2026-06-01 21:30:06 +01:00
Thomas Adam
2f3ef88dbf Merge branch 'obsd-master' 2026-06-01 21:15:06 +01:00
nicm
9725a0b9bf Remove the actual LAYOUT_FLOATING enum member. 2026-06-01 20:10:01 +00:00
nicm
b368e89bea Remove LAYOUT_FLOATING and disable custom layouts with floating panes
for now, from Dane Jensen.
2026-06-01 20:09:26 +00:00
nicm
3269833caa Fix vertical position of scrollbar having wrong vertical offset when
status-position top, from Michael Grant.
2026-06-01 20:00:25 +00:00
nicm
922f7d1645 Fix resize of floating panes with pane-border-status top, from Michael
Grant.
2026-06-01 19:59:04 +00:00
nicm
a1873fef61 Only set saved_layout_cell if creating a new cell, avoids crash when
respawning pane. Reported by Mingjie Shen in GitHubb issue 5143.
2026-06-01 19:56:26 +00:00
Thomas Adam
0eff3b6b97 Merge branch 'obsd-master' 2026-06-01 19:30:06 +01:00
nicm
f6557d1ea2 Add a Z sort order in tree mode. 2026-06-01 18:19:51 +00:00
Thomas Adam
718eb2b4cc Merge branch 'obsd-master' 2026-06-01 19:15:06 +01:00
nicm
cd6c01e42b Mark floating panes in tree mode. 2026-06-01 18:00:20 +00:00
Thomas Adam
0b773dd792 Merge branch 'obsd-master' 2026-06-01 15:15:06 +01:00
nicm
b68ab3a4b4 Add pane_x, y, z format variables and show in list-panes. 2026-06-01 14:01:09 +00:00
Thomas Adam
d8c9f88fe7 Merge branch 'obsd-master' 2026-06-01 15:00:06 +01:00
Nicholas Marriott
f6398ba51c Add some more DECRQM responses, from Ayman Bagabas in GitHub issue 5118. 2026-06-01 14:30:33 +01:00
nicm
67dbf01eeb Add some more DECRQM responses, from Ayman Bagabas in GitHub issue 5118. 2026-06-01 13:28:49 +00:00
Thomas Adam
7c71064b64 Merge branch 'obsd-master' 2026-06-01 12:15:06 +01:00
nicm
4d91ff8d8d Do not try to use pane if there isn't one when redrawing wide characters,
GitHub issues 5145 and 5139.
2026-06-01 10:53:28 +00:00
Thomas Adam
e778f7f805 Merge branch 'obsd-master' 2026-06-01 09:45:06 +01:00
nicm
3bff7a9e62 Allow run-shell arguments after a shell command to be expanded as #1, #2
and so on. From Rasmus Thystrup Karstensen in GitHub issue 5121.
2026-06-01 08:27:37 +00:00
Thomas Adam
705c2ac55a Merge branch 'obsd-master' 2026-05-31 23:00:06 +01:00
Thomas Adam
3f3d65e452 Merge branch 'obsd-master' 2026-05-31 22:30:06 +01:00
nicm
a9721ea268 Only draw visible parts of numbers for display-panes. 2026-05-31 21:29:41 +00:00
nicm
d876bb8cf3 When pane-border-indicators is set to "both" or "arrows", only draw
arrows on the active floating pane and no other floating panes. Also
there is no need to loop in screen_redraw_cell_border if only checking
one pane.
2026-05-31 21:08:23 +00:00
Thomas Adam
b17dcc6773 Merge branch 'obsd-master' 2026-05-31 22:00:06 +01:00
nicm
e3ee2b327b Move back to starting position when pane is outside the window at all. 2026-05-31 20:41:07 +00:00
Thomas Adam
74b91d6dec Merge branch 'obsd-master' 2026-05-30 17:45:06 +01:00
nicm
7a3b7ae812 Return empty ranges if line is below window. 2026-05-30 16:10:13 +00:00
Thomas Adam
35df67c893 Merge branch 'obsd-master' 2026-05-30 12:30:06 +01:00
nicm
290303e64f Missed this file from previous. 2026-05-30 11:20:03 +00:00
nicm
f8ab7a5158 Handle ranges for panes which are outside the window to the left. 2026-05-30 11:19:39 +00:00
Thomas Adam
decaaf4b55 Merge branch 'obsd-master' 2026-05-30 11:15:06 +01:00
nicm
b9d228c72f Do not crash when a pane offset is negative. 2026-05-30 09:48:30 +00:00
Thomas Adam
11e48c3fa9 Merge branch 'obsd-master' 2026-05-30 10:15:06 +01:00
nicm
c40302a7ff Check if the range is invalid using start,end rather than length since
it will never be negative.
2026-05-30 08:58:29 +00:00
Thomas Adam
964e22cfef Merge branch 'obsd-master' 2026-05-29 17:15:06 +01:00
nicm
55db69e3b2 Correct offsets for redrawing entire line here too. 2026-05-29 15:51:03 +00:00
Thomas Adam
99b08ecde5 Merge branch 'obsd-master' 2026-05-29 09:15:06 +01:00
nicm
4ecf93487e Use correct X position for redrawing wide characters. 2026-05-29 07:49:01 +00:00
Nicholas Marriott
f066933418 Uninstall the man page too. 2026-05-28 15:20:04 +01:00
Thomas Adam
481d8299ca Merge branch 'obsd-master' 2026-05-28 12:45:06 +01:00
nicm
3b19536df9 Swap panes on z-index list as well. 2026-05-28 11:17:35 +00:00
Thomas Adam
2212157b61 Merge branch 'obsd-master' 2026-05-28 12:00:07 +01:00
nicm
7ebeed1ae7 Do not try to use last pane if there isn't one. 2026-05-28 10:45:17 +00:00
nicm
7769097915 Hide menu options that do not work for floating panes. 2026-05-28 10:34:38 +00:00
Thomas Adam
595b0cb471 Merge branch 'obsd-master' 2026-05-28 10:00:08 +01:00
nicm
c50c3629e6 Use the correct ranges when a pane is covered by a popup in tty_draw_pane. 2026-05-28 08:43:57 +00:00
Thomas Adam
887b6f4da7 Merge branch 'obsd-master' 2026-05-28 09:15:07 +01:00
nicm
9c6cfcd2e9 Obscured check should not be true if pane is same size as window. 2026-05-28 07:58:45 +00:00
Thomas Adam
658c7d1dbf Merge branch 'obsd-master' 2026-05-28 08:45:08 +01:00
nicm
ecfe6e4e7a Check visible ranges for collected cell output. 2026-05-28 07:23:52 +00:00
Thomas Adam
f53c4e7525 Merge branch 'obsd-master' 2026-05-27 22:15:06 +01:00
Nicholas Marriott
3690c64e88 Use -g -ggdb for debug. 2026-05-27 22:07:57 +01:00
Thomas Adam
c489f786b6 Merge branch 'obsd-master' 2026-05-27 22:00:06 +01:00
nicm
5c40cc43b2 Bring in the single-cell redraw parts for floating panes. 2026-05-27 20:57:53 +00:00
nicm
54ae850322 Do not adjust end position when removing partial padding, it does not
change.
2026-05-27 20:47:23 +00:00
Thomas Adam
a9b9d2f6d3 Merge branch 'obsd-master' 2026-05-27 21:00:08 +01:00
nicm
f00f92e7e3 Check visible ranges when copying screens. 2026-05-27 19:43:46 +00:00
nicm
bbe26df83d Add an accessor function needed for floating panes. 2026-05-27 19:36:04 +00:00
Thomas Adam
daf9988b3a Merge branch 'obsd-master' 2026-05-27 20:15:06 +01:00
nicm
ee67452772 Add window-pane-status-format options and adjust the default second
status line to show panes, also change how window-style is checked now
it is a pane option.
2026-05-27 18:57:10 +00:00
Thomas Adam
781364f7f9 Merge branch 'obsd-master' 2026-05-27 17:45:06 +01:00
nicm
84d016ddcf Floating panes clearing bits. 2026-05-27 16:25:01 +00:00
Nicholas Marriott
0241fdac1a Remove extra declaration. 2026-05-27 16:52:09 +01:00
Thomas Adam
d62bae442b Merge branch 'obsd-master' 2026-05-27 16:46:04 +01:00
nicm
9cc8947a43 Cache border and active border style separately, fixes problem reported
by Marcel Partap in GitHub issue 5125.
2026-05-27 13:41:20 +00:00
nicm
bff68fa079 Remove reference to an option that hasn't been added yet. 2026-05-27 12:52:47 +00:00
nicm
00c59041ea Bring in some more floating panes changes - obscured panes need to be
handled specially in screen_write_* rather than dropping into the client
redraw (tty.c).
2026-05-27 11:54:28 +00:00
Thomas Adam
a77b81f0e5 Merge branch 'obsd-master' 2026-05-27 08:15:06 +01:00
jsg
f839588c69 fix sort_get_clients() indentation; ok nicm@ 2026-05-27 07:05:20 +00:00
nicm
27d719cd4d Check error result correctly (*cause not cause), pointed out by jsg. 2026-05-27 07:01:36 +00:00
Thomas Adam
7a15dc6772 Merge branch 'obsd-master' 2026-05-25 16:15:06 +01:00
nicm
bfd7f468ce pane_zoomed_flag went missing somewhere in tmux.1, put it back. 2026-05-25 14:53:09 +00:00
Thomas Adam
cca1e77f95 Merge branch 'obsd-master' 2026-05-25 09:15:07 +01:00
nicm
cc81287665 More of floating panes redraw path, still mostly using flags which are never set. 2026-05-25 08:07:48 +00:00
nicm
ca95153b45 Preserve flags when creating new cells, from Brian Youngs. 2026-05-25 07:55:46 +00:00
Nicholas Marriott
eb330a6df6 Restore SIXEL bit. 2026-05-24 10:05:41 +01:00
Thomas Adam
b498979d4d Merge branch 'obsd-master' 2026-05-24 10:00:06 +01:00
nicm
f047b3f8e7 Do not crash when freeing layout cell, reported by Jere Viikari. 2026-05-24 08:40:43 +00:00
Thomas Adam
c67787536b Merge branch 'obsd-master' 2026-05-23 18:00:06 +01:00
nicm
3cfc96d4de Add missing argument to %unlinked-window-renamed, reported by Andrew
Grangaard.
2026-05-23 16:43:04 +00:00
Thomas Adam
91a8c66537 Merge branch 'obsd-master' 2026-05-23 08:45:06 +01:00
nicm
5c3a4746b7 Some code tidying up. 2026-05-23 07:22:50 +00:00
Nicholas Marriott
b8e0004ff2 Fix missing arguments. 2026-05-22 18:33:04 +01:00
Thomas Adam
4e0aabd8a7 Merge branch 'obsd-master' 2026-05-22 16:54:06 +01:00
nicm
143a177055 Tighten up read-only checks on attach-session, detach-client and
switch-client so that a user should be able to only detach their own
client. Reported by John Walker.
2026-05-22 15:22:43 +00:00
nicm
d45a9cad8c Work out if a pane is obscured by another one when drawing. 2026-05-22 11:55:43 +00:00
Nicholas Marriott
285a3b7522 Fix up SIXEL with recent changes. 2026-05-22 12:44:08 +01:00
Thomas Adam
d4255818c4 Merge branch 'obsd-master' 2026-05-22 12:01:06 +01:00
Thomas Adam
f714f3ee78 Merge branch 'obsd-master' 2026-05-22 10:28:51 +01:00
nicm
36bc351557 Use a union for the data passed around in tty_ctx instead of void *. 2026-05-22 09:21:32 +00:00
nicm
d5ee330d20 Tidy up a load of different flags and special values in the tty_ctx into
a single bitmask.
2026-05-22 09:05:16 +00:00
Thomas Adam
634e9d6213 Merge branch 'obsd-master' 2026-05-21 12:01:06 +01:00
nicm
9aa9a92f72 Pane resizing code for floating panes, mostly by Michael Grant. 2026-05-21 07:28:51 +00:00
Nicholas Marriott
8c51c0fdbd Merge tag '3.6b'
3.6b
2026-05-20 12:53:07 +01:00
Thomas Adam
24e4a545a2 Merge branch 'obsd-master' 2026-05-20 12:19:23 +01:00
Nicholas Marriott
c5fb5e8bb0 Update CHANGES. 2026-05-20 12:17:30 +01:00
Nicholas Marriott
b8434182c9 Track which list (images or saved_images) each image is on so they can be
removed from the correct list when the total image count is reached. Fixes
crash reported by xlabai at tencent dot com.
2026-05-20 12:15:34 +01:00
Nicholas Marriott
e1f942f26a Add a define for max images and bump to 20 for the moment. 2026-05-20 12:14:22 +01:00
Thomas Adam
91b85efabb Merge branch 'obsd-master' 2026-05-20 12:01:06 +01:00
nicm
0057905ca0 Increase escape delay if the buffer contains a partial paste end, fixes
issues with at least Windows Terminal. From jing dot empty at gmail.com
GitHub issue 5088.
2026-05-20 10:56:46 +00:00
nicm
572e26deb8 Some more trivial floating panes bits. 2026-05-20 08:54:40 +00:00
Thomas Adam
caff9a135f Merge branch 'obsd-master' 2026-05-20 08:47:44 +01:00
nicm
ce24b92758 Floating panes full redraw code, by Michael Grant. 2026-05-19 13:12:45 +00:00
nicm
f5a086ac98 Some more easy floating panes bits. 2026-05-19 12:16:25 +00:00
Thomas Adam
cb1025e0e8 Merge branch 'obsd-master' 2026-05-19 12:25:31 +01:00
nicm
a46cdb8bbc More (currently disabled) bits for creating floating panes, from Michael
Grant and Dane Jensen.
2026-05-19 10:26:03 +00:00
nicm
34e2035bad More bits for pane Z index tracking from floating panes, mostly by
Michael Grant.
2026-05-19 09:48:14 +00:00
Thomas Adam
a9ba7b8ecb Merge branch 'obsd-master' 2026-05-18 09:38:02 +01:00
nicm
962d647993 Remove some leftover debug code. 2026-05-17 16:02:33 +00:00
nicm
a8520ba59e Bring over some of layout and positioning code for floating panes, by
Michael Grant.
2026-05-17 16:01:42 +00:00
nicm
f12d7b4e67 When mode-keys is set to vi, do not allow the cursor to go into the
invisible extra cell to the right of the visible text. This is closer to
what vi(1) does. From Max Vim in GitHub issue 5070.
2026-05-17 13:12:21 +00:00
nicm
bbea6e6375 Add a five second limit on pasting for terminals which mysteriously lose
the end sequence if the paste is too big (that is, Terminal.app).
Reported by Garri Djavadyan in GitHub issue 4527.
2026-05-17 13:01:04 +00:00
nicm
b24b8424b1 Switch to use backing grid not copy mode's grid for copy line, GitHub
issue 4995.
2026-05-17 12:38:04 +00:00
nicm
35bd1a4c02 Do not include unattached clients on sorted list, fixes crash reported
by Marcel Partap.
2026-05-17 11:17:16 +00:00
nicm
818352c3cd Add some more explanatory text about targets, GitHub issue 4850. 2026-05-17 11:12:07 +00:00
nicm
742efe989f Report file open errors more sensibly, reported by Meriel Luna
Mittelbach in GitHub issue 5081.
2026-05-17 10:54:01 +00:00
nicm
4cea1c7189 Add new-pane command, currently this is equivalent to split-window but
it will have minor differences for floating panes in future. Also add
-R/-s/-S/-k/-m flags to control border and style and behaviour, like
popups. GitHub issue 5027 from Dane Jensen.
2026-05-17 10:44:53 +00:00
Nicholas Marriott
18ddda48d2 More. 2026-05-16 12:18:41 +01:00
Nicholas Marriott
5651519f5d Try to make Copilot shut up. 2026-05-16 12:07:09 +01:00
nicm
281e8ff766 Fix a couple of bugs in tty_draw_line: do not loop forever if orphan
padding appears, or if a wide character is trimmed at the right of the
region. Much help with testing from qingliu at alauda dot io in GitHub
issue 5024.
2026-05-13 13:12:23 +00:00
nicm
dbc0710bc9 Update supported features for Foot, from Meriel Luna Mittelbach in
GutHub issue 5079.
2026-05-13 10:24:57 +00:00
nicm
8f28f31b78 Do not crash if set progress bar with no pane, from Dane Jensen. 2026-05-13 08:25:05 +00:00
Thomas Adam
3f651d9fa9 Merge branch 'obsd-master' 2026-05-12 16:01:08 +01:00
nicm
18fced7e7f Make pane offsets signed, needed for floating panes. 2026-05-12 12:05:41 +00:00
Thomas Adam
f1f0158741 Merge branch 'obsd-master' 2026-05-12 12:01:11 +01:00
nicm
3d6c542e34 Turn off the "is this a paste" guessing if the terminal supports bracket
pasting instead, GitHub issue 5031.
2026-05-12 10:28:09 +00:00
Nicholas Marriott
c3036116de Update test output. 2026-05-12 10:38:29 +01:00
nicm
9e3653a612 Check FIONREAD for all panes not just piped panes, fixes issues with
tests, GitHub issue 4807.
2026-05-12 09:37:25 +00:00
nicm
27a00d1bfd Fix infinite loop due to underflow when redrawing scrollbar, from Pavel
Lavrukhin in GitHub issue 4932.
2026-05-12 09:32:49 +00:00
nicm
800837ff3f Fix control mode teardown ordering for queued pane output, GitHub issue
5064 from Aaron Campbell.
2026-05-12 09:27:28 +00:00
Nicholas Marriott
bfbfb6dbf3 Avoid overshooting Sixel height in sixel_parse_write, GitHub issue 5071 from
nincsnevem662 at gmail dot com.
2026-05-11 12:00:06 +01:00
Thomas Adam
8fb1b1d412 Merge branch 'obsd-master' 2026-05-08 12:01:09 +01:00
nicm
af98467d0d Do not cache format for status line because it stores various pointers
that might be stale, instead cache the cmd_find_state and rebuild the
formats every time they are needed. Reported by Marcel Partap in GitHub
isue 5065.
2026-05-08 06:57:38 +00:00
Thomas Adam
f9d50d580c Merge branch 'obsd-master' 2026-05-07 12:01:08 +01:00
nicm
496805476e Require layout prefix to be 5 characters, GitHub issue 5067 from
cglosner at gmail dot com.
2026-05-07 09:21:05 +00:00
nicm
962e565591 Add Emacs-style recentre-top-bottom, GitHub issue 5053 from sinyax75 at
gmail dot com.
2026-05-07 09:15:44 +00:00
Thomas Adam
862625a4bf Merge branch 'obsd-master' 2026-05-06 16:01:09 +01:00
nicm
2d8aade74b Add time limit to a couple of other loops. 2026-05-06 13:43:38 +00:00
Nicholas Marriott
2aad2cfc53 Add a configure flag for ASAN. 2026-05-06 08:23:45 +01:00
Thomas Adam
ad94405dac Merge branch 'obsd-master' 2026-05-05 16:01:08 +01:00
nicm
518fcf7e03 Do not sanitize title when popping it from stack, also add a limit to
number of pushed titles.
2026-05-05 13:18:46 +00:00
nicm
1fbd00e1e8 Add missing flags to screen_mode_to_string and do not write before
before if any are missed, second bit from qingliu at alauda dot io.
2026-05-05 12:06:52 +00:00
nicm
cae229cadc Discard queued data and clear offsets when turning pane off to prevent
later read of data that has been removed. From Aaron Campbell in GitHub
issue 5054.
2026-05-05 12:02:12 +00:00
Thomas Adam
9d3dcdbc38 Merge branch 'obsd-master' 2026-05-03 20:01:09 +01:00
nicm
d52fabce72 Allow the indicator in tree mode to be customized by two new options:
tree-mode-preview-format and tree-mode-preview-style.
2026-05-03 15:02:48 +00:00
nicm
637d4c3083 Fix control client hang on exit after toggling no-output, GitHub issue
5049 from Aaron Campbell.
2026-05-03 15:01:21 +00:00
nicm
1e96d9a87f Do not check for NULL after dereferencing, from alexarama at yahoo dot
com in GitHub issue 5051.
2026-05-03 14:57:09 +00:00
nicm
49172b256c Free working stuff when R formats fail. 2026-05-03 14:55:43 +00:00
Thomas Adam
9a94f48663 Merge branch 'obsd-master' 2026-05-01 16:01:09 +01:00
nicm
2e9c6c2586 Add some checks on line size to avoid underflow, from san65384 at gmail
dot com in GitHub issue 4955.
2026-05-01 11:56:41 +00:00
Thomas Adam
92522473b0 Merge branch 'obsd-master' 2026-05-01 12:01:08 +01:00
nicm
56200ca044 Do not leak cached last result from control subs, from Aaron Campbell in
GitHub issue 5047.
2026-05-01 09:59:42 +00:00
nicm
524f283cc6 Add support for line numbers in copy mode. A new copy-mode-line-numbers
option has the following modes: off, default (tmux's normal line
numbering where 0 is the top visible line), absolute (first line in
history is 1), relative (relative to the cursor) and hybrid (current
line is absolute, others relative). Also adds
copy-mode-line-number-style and copy-mode-current-line-number-style to
set the style of the line numbers. When copy mode is entered with the
mouse, line numbers stay off.

From Leo Henon in GitHub issue 5025.
2026-05-01 09:44:42 +00:00
nicm
a2acf0f7cf Check time inside repeat (R:) loop as well. 2026-05-01 09:34:05 +00:00
Thomas Adam
d85f1b942d Merge branch 'obsd-master' 2026-04-28 16:01:08 +01:00
Thomas Adam
8da41bdc2b Merge branch 'obsd-master' 2026-04-28 12:01:08 +01:00
nicm
4a926e489b Make C-[ have the same bindings as Escape for terminals with extended
keys where they are different, GitHub issue 5035 from Eric NICOLAS.
2026-04-28 10:01:07 +00:00
nicm
746dd91e86 Sanitize paste buffer names in paste_set and paste_rename, GitHub issue
5032 from Barrett Ruth.
2026-04-28 08:52:37 +00:00
nicm
763dec881a Do not leak socketpair fds in pipe-pane when fork fails. From Barrett
Ruth.
2026-04-28 08:47:55 +00:00
nicm
2a5715fad5 Do not deref NULL job in window_copy_pipe_run when job_run fails. From
Barrett Ruth in GitHub issue 5036.
2026-04-28 08:35:21 +00:00
nicm
ec58272b95 Do not hang run-shell when job_run fails, from Barrett Ruth in GitHub
issue 5037.
2026-04-28 08:34:15 +00:00
nicm
f90d3af403 Fix -p for for join-pane, from Dane Jensen. 2026-04-28 08:32:44 +00:00
Thomas Adam
c2659401b0 Merge branch 'obsd-master' 2026-04-27 16:01:09 +01:00
nicm
9840d4a4a3 Add a limit on maximum length of environment variable assignment in
configuration files.
2026-04-27 12:31:11 +00:00
Nicholas Marriott
fc6d94a9f8 Track which list (images or saved_images) each image is on so they can be
removed from the correct list when the total image count is reached. Fixes
crash reported by xlabai at tencent dot com.
2026-04-27 13:09:07 +01:00
Nicholas Marriott
61a7be0eab Turn off regular expressions when fuzzing because there are cases that take too
much time or memory that cannot be fixed.
2026-04-26 21:19:16 +01:00
Thomas Adam
e4a439aa0c Merge branch 'obsd-master' 2026-04-26 21:12:23 +01:00
nicm
b844cafb9c No need to stravis the window name twice. 2026-04-24 10:08:52 +00:00
nicm
1d9522a7a6 Kill client rather than fatalx on bad file handling messages, reported
by Tim Zheng.
2026-04-23 12:36:15 +00:00
nicm
ffe80579df Redraw correctly when a popup is present above pane borders, GitHub
issue 4997 from Manuel Einfalt.
2026-04-23 11:29:23 +00:00
Thomas Adam
bcd17cf99a Merge branch 'obsd-master' 2026-04-23 11:58:17 +01:00
nicm
bc15723f7f Add feature for progress bar and pass to outside terminal, GitHu issue
4972 from Eric Dorland.
2026-04-22 07:25:17 +00:00
nicm
7a0cc03532 Translate keypad keys to text in prompt input. From Barrett Ruth in
GitHub issue 4996.
2026-04-22 07:15:34 +00:00
nicm
d36f6783c7 Add a couple of controls (kill, zoom) to default pane-status-format.
Will be more to come with floating panes. From Dane Jensen in GitHub
issue 4981.
2026-04-22 07:13:26 +00:00
nicm
d339ab51eb Sanitize pane titles and window and session names more consistently and
strictly, prevents C0 characters and other nonvisible characters causing
problems. Reported (with a different fix) by Chris Monardo in GitHub
issue 4999.
2026-04-22 07:10:16 +00:00
nicm
fee70031f6 Make clock visible on terminals without colours, from Manuel Einfalt in
GitHub issue 5001.
2026-04-22 07:05:59 +00:00
nicm
303edb71bd Add a fairly low time limit to format evaluation to stop absurdly nested
formats from making tmux appear to hang.
2026-04-22 07:05:03 +00:00
nicm
314f0ae964 Do not leak hyperlinks in copy mode, from Barrett Ruth in GitHub issue
5020.
2026-04-22 07:03:06 +00:00
nicm
0835980ef1 Remove no longer accurate statement from tmux.1, reported by dkuettel at
gmail dot com.
2026-04-22 06:58:59 +00:00
nicm
baf55e4616 Add a default set of features for WezTerm. 2026-04-22 06:57:08 +00:00
Nicholas Marriott
dd62c2f946 Do not need to create a new window each time for input-keys.sh test. 2026-04-15 16:21:35 +01:00
Thomas Adam
638cecf43c Merge branch 'obsd-master' 2026-04-14 16:01:08 +01:00
nicm
a97cfe513d Do not leak old time format if it is replaced in same format. 2026-04-14 11:25:41 +00:00
Thomas Adam
9de3c0495f Merge branch 'obsd-master' 2026-04-14 12:01:08 +01:00
nicm
67e6f8245b If job_run fails, do not crash but instead free the popup. 2026-04-14 08:39:10 +00:00
nicm
31c93c483a Another check for partially initialized control client, from Matt
Koscica in GitHub issue 5004.
2026-04-14 08:32:30 +00:00
nicm
86d4d5689c Add detach to default session menu, suggested by Przemyslaw Sztoch. 2026-04-14 07:35:17 +00:00
nicm
663e82bc19 Fix key binding conflict in session menu, from Dane Jensen. 2026-04-14 07:28:57 +00:00
nicm
51109e3f72 Include window bits for pane notifications, GitHub issue 5007 from Saul
Nogueras.
2026-04-14 07:26:45 +00:00
nicm
a11363e9e7 Limit precision to 100 to stop silly formats from running out of memory,
reported by z1281552865 at gmail dot com.
2026-04-14 07:24:23 +00:00
nicm
cc57913d7a Add WAYLAND_DISPLAY to default update-environment, GitHub issue 4965 from
wgh at torlan dot ru.
2026-04-14 07:16:02 +00:00
Thomas Adam
057d169492 Merge branch 'obsd-master' 2026-04-13 12:01:08 +01:00
nicm
e5a2a25faf Do not notify clients if not fully initialized, from Ben Maurer in
GitHub issue 4980.
2026-04-13 09:35:20 +00:00
nicm
147521b757 Add -C flag to command-prompt to match display-message -C (do not freeze
panes). From Barrett Ruth in GitHub issue 4978.
2026-04-13 09:33:09 +00:00
Thomas Adam
31d77e29b6 Merge branch 'obsd-master' 2026-04-05 20:01:08 +01:00
nicm
4b0ff07bcb When a cell is cleared after having been moved, we cannot reuse its
extended data, because that may still be in use. Add a flag to
grid_clear_cell to indicate this. Fixes irritating problems with ICH
(CSI @) mostly visible in emacs.
2026-04-05 15:43:17 +00:00
Thomas Adam
0ee18d4421 Merge branch 'obsd-master' 2026-04-05 16:01:08 +01:00
nicm
21da45e5c3 Add extkeys feature to tmux itself so nested tmux works, GitHub issue 4960. 2026-04-05 14:29:04 +00:00
nicm
d5ed967f58 Set less crazy limits (than INT_MAX) for pad and trim, makes ossfuzz happier. 2026-04-05 13:24:02 +00:00
Thomas Adam
42dafcc62a Merge branch 'obsd-master' 2026-04-04 20:01:11 +01:00
nicm
cad282ebb7 Cache user from getpwuid because it can be very expensive on some
platforms. From Ben Maurer in GitHub issue 4973.
2026-04-04 17:13:07 +00:00
nicm
7f2ac9c871 Add remain-on-exit key to keep pane around until a key is pressed, from
Michael Grant.
2026-04-04 17:00:33 +00:00
nicm
87aaff5fae Bring some new formats from the floating panes work: pane_zoomed_flag,
pane_flags, pane_floating_flag. By Michael Grant.
2026-04-04 16:40:27 +00:00
Thomas Adam
b411010237 Merge branch 'obsd-master' 2026-04-04 16:01:09 +01:00
nicm
d2d86ac360 Document control ranges, from Dane Jensen. 2026-04-04 11:48:00 +00:00
nicm
a827725a8d Set up default and last cell before clearing padding, reported by
someone on GitHub.
2026-04-04 11:39:49 +00:00
nicm
3badbc50e0 Do not read off end of buffer if it ends in \ when expanding \c, found
by ossfuzz.
2026-04-04 11:32:05 +00:00
nicm
cd60de443e Clamp width to terminal width, also change calculation of end of screen
(it is OK to be outside the screen). Fixes problem reported by Dane
Jensen in GitHub issue 4969.
2026-04-04 11:20:01 +00:00
Nicholas Marriott
95e40115a2 Don't just resize buffer once, repeat until it is big enough. Also use
reallocarray. Reported by tuannguyenduc228 at gmail dot com.
2026-04-04 12:11:25 +01:00
Thomas Adam
66c324d7ef Merge branch 'obsd-master' 2026-04-03 16:01:08 +01:00
nicm
6b056eb53f Add some new mouse ranges called "control0" to "control9", will be used
for controls on floating panes, from Dane Jensen, with some bits from
Michael Grant.
2026-04-03 10:13:20 +00:00
Thomas Adam
8cfdcf9a6f Merge branch 'obsd-master' 2026-04-03 11:04:15 +01:00
nicm
0917cd7010 Missed a line from previous. 2026-04-03 09:18:24 +00:00
nicm
bdd78ce38e Handle OSC 9;4 progress bar sequence and store in format variables, from
Eric Dorland in GitHub issue 4954.
2026-04-03 09:14:27 +00:00
nicm
7497db6e37 Increase b64_pton buffer to allow for Base64 without padding, from
Michal Majchrowicz.
2026-04-03 08:23:10 +00:00
Thomas Adam
43e2942fa0 Merge branch 'obsd-master' 2026-04-02 16:01:08 +01:00
Thomas Adam
51bc2f3b76 Merge branch 'obsd-master' 2026-04-02 12:01:08 +01:00
nicm
483683c197 Do not leak command in list-keys, reported by tb@. Also tidy up some
function names while here.
2026-04-02 09:58:14 +00:00
nicm
c95d341220 Do not leak trimmed string when expanding, found by ossfuzz. 2026-04-02 09:35:46 +00:00
nicm
be2c6f3b5e Use INT_MIN + 1 as strtonum lower limits in formats so -ve works, found
by ossfuzz.
2026-04-02 09:28:22 +00:00
nicm
2d5736f297 Limit argc to between 0 and 1000 to prevent fatal from MSG_COMMAND, from
Michal Majchrowicz.
2026-04-02 09:11:39 +00:00
nicm
1a51193899 Handle empty regular expression in substitution, found by ossfuzz. 2026-04-02 08:45:35 +00:00
nicm
8b51abef08 Check for \0 after skipping # not before in format_expand1, from ossfuzz. 2026-04-02 08:37:14 +00:00
nicm
cbb49e8c84 Fix NULL dereference in sort.c, from Dane Jensen. 2026-04-02 08:01:47 +00:00
Thomas Adam
dc12da4573 Merge branch 'obsd-master' 2026-04-01 12:01:08 +01:00
nicm
7af58f74b0 Fixed missing drag end detection and mouse button detection with
modifiers. From Dane Jensen, reported by Joseph Tyson and Mark Kelly.
2026-04-01 08:10:02 +00:00
Thomas Adam
1081876810 Merge branch 'obsd-master' 2026-03-31 16:01:08 +01:00
nicm
2ff0dd3fef Reorganize structure of key_code so that it can be built directly by
bitshifts rather than a load of huge switches, from Dane Jensen in
GitHub issue 4953.
2026-03-31 11:46:43 +00:00
Thomas Adam
a30fc69f86 Merge branch 'obsd-master' 2026-03-30 12:01:08 +01:00
nicm
022b5cf193 When in copy mode with a large scroll offset and the window is resized
so that history shrinks, data->oy can exceed screen_hsize causing an
unsigned integer underflow in the py computation. Clamp data->oy in
window_copy_resize and window_copy_cmd_refresh_from_pane before the
subtraction. From futpib at gmail dot com in GitHub issue 4958.
2026-03-30 09:23:40 +00:00
Nicholas Marriott
7620c03b72 Add new fuzzers for command parsing, formats and styles, from David Korczynski
in GitHub issue 4957.
2026-03-30 10:20:16 +01:00
Thomas Adam
6324dae114 Merge branch 'obsd-master' 2026-03-27 12:01:10 +00:00
nicm
e0237c6b8c Ql -> Fl, from arza at arza dot us in GitHub issue 4949. 2026-03-27 08:41:54 +00:00
nicm
1cf3e02bfb Add a format flag for bracket paste, from George Nachman in GitHub issue
4951.
2026-03-27 08:40:26 +00:00
Thomas Adam
6ca5405db9 Merge branch 'obsd-master' 2026-03-25 12:01:07 +00:00
Nicholas Marriott
fa31f2a3eb Do not really need both. 2026-03-25 10:39:03 +00:00
Nicholas Marriott
d03f9454b9 Make COPYING the same. 2026-03-25 10:38:03 +00:00
Nicholas Marriott
616fb1cbcf Add a LICENSE file to see if it makes GitHub happy. 2026-03-25 10:26:15 +00:00
nicm
b7d1e6257c Use \- for hyphens in tmux.1 to cause newer groff versions to render
them correctly (ASCII hyphen rather than Unicode) which aids copy and
paste. From Keith Thompson in GitHub issue 4948.
2026-03-25 10:08:50 +00:00
Thomas Adam
b101ffaed6 Merge branch 'obsd-master' 2026-03-23 12:01:10 +00:00
nicm
f7dad4f38f Check lastgc is not NULL before using it, GitHub issue 4935 from Pavel
Lavrukhin.
2026-03-23 09:05:59 +00:00
nicm
b88c08f860 Fix a NULL dereference and use after free, GitHub issue 4936 from Pavel
Lavrukhin.
2026-03-23 09:03:43 +00:00
Nicholas Marriott
cc47f4d1c2 Use window options for cursor-style to avoid crash when no pane, from
Arden Packeer in GitHub issue 4942.
2026-03-23 09:00:42 +00:00
nicm
3909d7e92f Use window options for cursor-style to avoid crash when no pane, from
Arden Packeer in GitHub issue 4942.
2026-03-23 08:58:39 +00:00
nicm
d70edfa0a0 Fix issue where popup window gets overwritten by background updates,
from Conor Taylor in GitHub issue 4920.
2026-03-23 08:48:32 +00:00
nicm
d22ab85b84 Protect against overflow when scrollbar is off screen, from san65384 at
gmail dot com in GitHub issue 4933.
2026-03-23 08:45:30 +00:00
Thomas Adam
5c30b145df Merge branch 'obsd-master' 2026-03-18 12:01:09 +00:00
nicm
e446cba6a9 Copy hyperlinks when redrawing popup so they do not vanish, from Antoine
Gaudreau Simard in GitHub issue 4925.
2026-03-18 08:49:27 +00:00
Nicholas Marriott
8a0b4e0d8c Work around systemd killing panes early during system shutdown by creating
dependencies from the panes to the service which started tmux, GitHub issue
4926 from Dmitry Torokhov.
2026-03-18 08:46:16 +00:00
nicm
02a9cdff57 Fix buffer to both for get-clipboard, from Seth Girvan. 2026-03-18 08:41:20 +00:00
nicm
f516f8a1d4 Allow codepoint-widths to accept ranges, from san65384 at gmail dot com
in GitHub issue 4930.
2026-03-18 08:38:54 +00:00
Thomas Adam
bef4865d91 Merge branch 'obsd-master' 2026-03-12 16:01:08 +00:00
nicm
fd62f419ac Add a short builtin help text for each mode accessible with C-h,
based on code from Patrick Motard in GitHub issue 4751.
2026-03-12 12:40:40 +00:00
Thomas Adam
7fa6f320ed Merge branch 'obsd-master' 2026-03-12 12:01:09 +00:00
nicm
19f3fb131b Draw message as one format, allowing prompts and messages to occupy only
a portion of the status bar, overlaying the normal status content rather
than replacing the entire line. A new message-format option now controls
the entire message (like status-format). From Conor Taylor in GitHub
issue 4861.
2026-03-12 07:25:13 +00:00
nicm
551e8fcd24 Fix comparison of menu keys, reported by Vladimir Bauer in GitHub issue
4891.
2026-03-12 07:15:26 +00:00
Thomas Adam
04b4952f0e Merge branch 'obsd-master' 2026-03-09 20:01:07 +00:00
Thomas Adam
0d77555c97 Merge branch 'obsd-master' 2026-03-09 16:01:09 +00:00
nicm
67141fb4bb Add next/previous variables for windows in W: loop, from Conor Taylor in
GitHub issue 4856.
2026-03-09 15:46:01 +00:00
tb
2efa762c2b tmux: move block assigning to prefix a bit down to avoid a small leak
ok nicm
2026-03-09 14:37:26 +00:00
Nicholas Marriott
6c2dd193cf Fix memory leak in sixel_parse() on error path, from Renaud Allard in GitHub
issue 4916.
2026-03-09 14:34:58 +00:00
nicm
881bec958e Fix server crash when control client session is destroyed, from Renaud
Allard in GitHub issue 4916.
2026-03-09 14:33:55 +00:00
nicm
8899c751e5 Check for NULL value in tilde expansion before dereferencing, from
Renaud Allard in GitHub issue 4916.
2026-03-09 14:31:41 +00:00
Thomas Adam
d0caf0a322 Merge branch 'obsd-master' 2026-03-06 12:01:08 +00:00
tb
91b5108eae tmux: don't leak menu if the tty is too small to display it
ok nicm
2026-03-06 08:19:22 +00:00
nicm
2c7f73f9c4 Do not use recallocarray because the stored size may have changed during
reflow so may not match what it expects, fixes crash reported by Caspar
Schutijser.
2026-03-06 08:09:58 +00:00
Thomas Adam
5310592967 Merge branch 'obsd-master' 2026-03-06 04:01:09 +00:00
nicm
41bddae907 Fix incorrect condition for creating client name, from Vlad Apostol in
GitHub issue 4911.
2026-03-05 22:50:14 +00:00
Thomas Adam
d9d2b2f1ee Merge branch 'obsd-master' 2026-03-05 12:01:09 +00:00
nicm
1e208abd93 Add pane_pipe_pid with pipe fd and call setpgid to make it easier to kill. 2026-03-05 09:22:08 +00:00
Thomas Adam
d32088b58a Merge branch 'obsd-master' 2026-03-04 12:01:09 +00:00
nicm
6d37cc94a7 Make -c work with new-session -A, from Jody Frankowski in GitHub issue
4906.
2026-03-04 08:16:47 +00:00
nicm
55722a7ed7 Another memory leak, from Huihui Huang. 2026-03-04 08:15:24 +00:00
tb
d2fa20a390 tmux: use VIS_NOSLASH to avoid annoying double escaping on paste
from nicm
2026-03-04 07:19:32 +00:00
tb
bcbad050d4 tmux: tc can be NULL, so check before deref to avoid crashing the server
ok nicm
2026-03-04 07:17:01 +00:00
Thomas Adam
27698c468b Merge branch 'obsd-master' 2026-03-03 16:01:09 +00:00
nicm
49bb43047d Allow copy mode to work for readonly clients, except for copy commands,
from Dane Jensen.
2026-03-03 12:26:14 +00:00
nicm
77ce0a837d Check window is not NULL, from Chema Gonzalez in GitHub issue 4908. 2026-03-03 12:24:18 +00:00
Thomas Adam
bbbfa8f360 Merge branch 'obsd-master' 2026-03-02 12:01:09 +00:00
Nicholas Marriott
d781d2eaa1 Add regression test from Ilya Grigoriev, GitHub issue 4818. 2026-03-02 09:32:45 +00:00
nicm
f33b14bda6 Revert r1.343 for the moment since it breaks behaviour (#() in
status-left) that we need to keep.
2026-03-02 08:48:57 +00:00
nicm
e603549563 Do not leak active/all strings in format_loop_windows; from Huihui Huang
in GitHub issue 4898.
2026-03-02 08:41:43 +00:00
nicm
e80e0c761a Fix incorrect placement of It Xo, from Dane Jensen. 2026-03-02 08:39:44 +00:00
nicm
9e804202b6 Fix memory leak, from Emmanuel Ugwu in GitHub issue 4900. 2026-03-02 08:38:23 +00:00
Thomas Adam
0c678b1e1a mailcap: update entry for Thomas Adam 2026-03-01 17:01:08 +00:00
Thomas Adam
4cb29deb93 Merge branch 'obsd-master' 2026-02-27 12:01:08 +00:00
nicm
5413953d30 Validate -O flags, from Dane Jensen in GitHub issue 4889. 2026-02-27 08:25:12 +00:00
nicm
50e51b4513 Fix memory leak, from Chris Lewis, reported by Huihui Huang. 2026-02-27 08:23:02 +00:00
Thomas Adam
ee0647aba6 Merge branch 'obsd-master' 2026-02-26 16:01:09 +00:00
Nicholas Marriott
35be70f1f0 Add DECRPM test from David Turnbull. 2026-02-26 11:32:15 +00:00
nicm
dbb23d2182 Do not free buffer after adding to paste (since it now owns it). 2026-02-26 11:01:48 +00:00
Nicholas Marriott
478eaea982 Update base64 compat. 2026-02-26 10:55:41 +00:00
Thomas Adam
0800e51d41 Merge branch 'obsd-master' 2026-02-26 10:18:15 +00:00
nicm
5ff385f8a9 Tweak previous to reset cursor again as well. 2026-02-25 07:59:45 +00:00
nicm
5b3c642195 Pass paste buffer through vis(3) when pasting to prevent buffers
containing for example the bracket end sequence causing issues. -S flag
disables. Reported by Mason Davis.
2026-02-25 07:53:41 +00:00
nicm
bd6e201926 Do not expand #() in E: and T:, from Mason Davis. 2026-02-24 18:06:41 +00:00
Nicholas Marriott
08779aa2a3 Fix breakage in fuzzer, from David Korczynski, GitHub issue 4871. 2026-02-24 08:27:59 +00:00
nicm
09bd686327 Add sorting (-O flag) and a custom format (-F) to list-keys, from Dane
Jensen in GitHub issue 4845.
2026-02-24 08:20:52 +00:00
nicm
9316476a73 Add commands to turn exit on scroll on, off, toggle. From xcdnlgd at
hotmail dot com in GitHub issue 4884.
2026-02-24 08:00:43 +00:00
nicm
00946007d8 Respond to DECRQM 2026, from David Turnbull in GitHub issue 4887. 2026-02-24 07:50:27 +00:00
Thomas Adam
bc47bc7c22 Merge branch 'obsd-master' 2026-02-23 12:01:08 +00:00
Thomas Adam
dc693601f4 Merge branch 'obsd-master' 2026-02-23 10:01:08 +00:00
nicm
f1f9c63cdb Free history entries properly, from Huihui Huang in GitHub issue 4870. 2026-02-23 09:12:57 +00:00
nicm
0cc4f0fd76 Use buffer size for b64_pton, from someone in GitHub issue 4882. 2026-02-23 09:08:07 +00:00
nicm
0dc1b5adfb Do not leak list on failure, reported by Huihui Huang. 2026-02-23 08:58:40 +00:00
nicm
a76e6eca6b Another memory leak from Huihui Huang. 2026-02-23 08:54:56 +00:00
nicm
8c7278b53a Memory leaks in cmd_display_menu, from Huihui Huang. 2026-02-23 08:50:00 +00:00
nicm
50a3b4c777 Free format on -a, reported by Huihui Huang. 2026-02-23 08:46:57 +00:00
nicm
3094ca1da5 Fix memory leaks, reported by Huihui Huang in GitHub issue 4872. 2026-02-23 08:45:27 +00:00
nicm
23ad10c849 Set cell to default when off screen to avoid crash when logging it. 2026-02-23 08:29:30 +00:00
Thomas Adam
c9fb989a31 Merge branch 'obsd-master' 2026-02-23 08:11:38 +00:00
Nicholas Marriott
8356578a54 Update tmux bash completion file link in README, from Hyun Seungmin. 2026-02-20 08:43:35 +00:00
nicm
0310404155 Reuse the same extended slot when clearing non-RGB cells as well. From
Michael K Darling in GitHub issue 4865.
2026-02-20 08:41:23 +00:00
nicm
03f8690f9c Pass which clipboard is set through to the terminal, from Axel Lindskog
in GitHub issue 4858.
2026-02-18 09:10:31 +00:00
Thomas Adam
4cc3de4f84 Merge branch 'obsd-master' 2026-02-17 08:51:05 +00:00
nicm
fedd4440f0 Reuse extended entry when clearing RGB cell, to prevent memory growth
when cells are repeatedly cleared (they are only compacted when scrolled
off screen). From Michael K Darling in GitHub issue 4862.
2026-02-17 07:42:58 +00:00
nicm
55cedc24fa Format layout change string once per window in control notify, from
Conor Taylor in GitHub issue 4848.
2026-02-16 08:45:38 +00:00
nicm
c9162837a0 Pull format allocation outside of loop for control subs, from Conor
Taylor in GitHub issue 4848.
2026-02-16 08:42:57 +00:00
nicm
f218463976 grid_peek_cell can return NULL, so check for it. From Conor Taylor in
GitHub issue 4848.
2026-02-16 08:02:04 +00:00
nicm
1f0c54f7ea Initialize bufname, reported by Mark Kelly. 2026-02-15 17:43:26 +00:00
Thomas Adam
d3522c328c Merge branch 'obsd-master' 2026-02-12 11:10:01 +00:00
nicm
7e50eb0e83 Make paste_get_top return a copy of the buffer name which is more
sensible and avoids a double free pointed out by DongHan Kim.
2026-02-11 08:30:37 +00:00
nicm
5b455abecc Do not write before buffer when parsing empty clipboard or palette
replies, or try to allocate zero bytes with an empty clipboard sequence.
Reported by DongHan Kim.
2026-02-11 08:23:21 +00:00
nicm
7d41761e84 Fix clients_calculate_size for manual type when window is NULL. From
Elias Oenal in GitHub issue 4849.
2026-02-10 10:02:11 +00:00
nicm
25f6d8b1e9 Implement some obvious missing sort orders, from Dane Jensen in GitHub
issue 4813.
2026-02-10 09:55:53 +00:00
nicm
5a33616e65 Check for no window when updating clients, GitHub issue 4851. 2026-02-10 09:00:30 +00:00
nicm
1cf17b06ae Batch printable output in control mode, from Conor Taylor in GitHub issue 4848. 2026-02-10 08:40:03 +00:00
nicm
aa03706ed0 Remove redundant call to tty_attributes, from Conor Taylor in GitHub
issue 4848.
2026-02-10 08:34:43 +00:00
nicm
d8794e2b30 Check log level before log_debug in tty_draw_line, from Conor Taylor in
GitHub issue 4848.
2026-02-10 08:31:45 +00:00
nicm
b7939eb266 Don't call event_add again if the event is already pending, from Conor
Taylor in GitHub issue 4848.
2026-02-10 08:30:21 +00:00
nicm
19b9a34c48 Only loop over clients if table actually found, from Conor Taylor in
GitHub issue 4848.
2026-02-10 08:28:53 +00:00
nicm
f016e08153 Fix wrong TAILQ member in input_cancel_requests from Conor Taylor in
GitHub issue 4848.
2026-02-10 08:27:17 +00:00
nicm
7b1c503086 Clear search counts when clearing marks in case of repeated search,
reported by Daniel Pereira in GitHub issue 4817.
2026-02-06 10:28:42 +00:00
nicm
5865001e4a Also check PANE_STATUSREADY for pane_dead format to match
pane_dead_status. GitHub issue 4841 from Joshua Pollack.
2026-02-06 10:23:26 +00:00
nicm
62944da74b Make OSC 52 work in popups, from gogongxt at 163 dot com in GitHub issue
4797.
2026-02-03 09:07:44 +00:00
nicm
588013bb44 Refresh copy mode when style changes, from Josh Cooper in GitHub issue 4830. 2026-02-03 08:53:58 +00:00
Thomas Adam
615c27c117 Merge branch 'obsd-master' 2026-02-02 19:06:25 +00:00
nicm
3c3d9ce3a9 Break sorting out into a common file so formats and modes use the same
code. Also add -O for sorting to the list commands. From Dane Jensen in
GitHub issue 4813.
2026-02-02 10:08:30 +00:00
nicm
66011fe48b Do not treat cells as empty unless the background colour stays the same.
Fixes invisible clock in clock mode, reported by tb@.
2026-01-29 09:08:19 +00:00
Thomas Adam
d7f59eccaf Merge branch 'obsd-master' 2026-01-28 17:25:42 +00:00
Thomas Adam
e652213855 Merge branch 'obsd-master' 2026-01-23 10:50:55 +00:00
nicm
f70150a663 Replace overlay_ranges with visible_ranges which can hold more than
three ranges (will be needed for floating panes); move the visible
ranges checks outside of tty_draw_line and rewrite it to fix issues with
partially-obscured wide characters. With Michael Grant.
2026-01-23 10:45:53 +00:00
Nicholas Marriott
fe52f76913 Fiddle with some more warnings. 2026-01-22 15:02:42 +00:00
Nicholas Marriott
0d9c3c895c Stick the pragmas outside the function (make GCC 4 happier) and under __GNUC__. 2026-01-22 14:29:29 +00:00
Nicholas Marriott
6525bb7cef Line length/style nits. 2026-01-22 14:16:39 +00:00
Michael Grant
0790e74f84 Fix strftime warning in gcc. 2026-01-22 14:11:00 +00:00
nicm
195a9cfd88 When history-limit is changed, apply to existing panes, not just new
ones. GitHub issue 4705.
2026-01-22 08:55:01 +00:00
nicm
ecbf8d76d0 Reevaluate menu and popup styles on each draw to allow them to change
when options change, from Josh Cooper in GitHub issues 4828 and 4829.
2026-01-22 07:52:24 +00:00
nicm
818745a605 Set PANE_STYLECHANGED when user options change, from Josh Cooper in
GitHub issue 4825.
2026-01-22 07:42:30 +00:00
nicm
26aacd0e32 Handle theme keys earlier so they are processed even if a popup is open.
From Josh Cooper in GitHub issue 4827.
2026-01-22 07:39:35 +00:00
nicm
8e06739e26 Fix window-size=latest not resizing on switch-client in session groups.
From Ilya Grigoriev in GitHub issue 4818.
2026-01-20 22:50:08 +00:00
nicm
ab1f2ef71c Add a function to convert a screen to a string, from Michael Grant. 2026-01-20 16:32:05 +00:00
Nicholas Marriott
d2e09cb259 Add a --enable-optimizations configure flag. 2026-01-20 15:42:22 +00:00
Thomas Adam
bf8ea85bd7 Merge branch 'obsd-master' 2026-01-19 10:01:08 +00:00
nicm
9c0aeaff40 Now the copy mode indicator can be changed, we need to redraw it when
the cursor is moved. GitHub issue 4774.
2026-01-19 08:20:51 +00:00
Thomas Adam
95b4bb51c6 Merge branch 'obsd-master' 2026-01-14 22:01:08 +00:00
nicm
1e5f93b7b6 Add -e flag to command-prompt to close if empty, from Dane Jensen in
GitHub issue 4812.
2026-01-14 19:43:43 +00:00
Nicholas Marriott
b89d46bb00 Add a regression test from Ilya Grigoriev. 2026-01-13 20:29:21 +00:00
Thomas Adam
97d61131df Merge branch 'obsd-master' 2026-01-12 10:01:08 +00:00
nicm
5721767921 Correctly draw indicators when pane-border-indicators is set to both.
Reported by Ilya Grigoriev in GitHub issue 4780.
2026-01-12 07:50:16 +00:00
nicm
a443531280 Remember last pane or type of location for double and triple clicks and
correctly handle it changes between first and second or second and
third. GitHub issue 4795 from Shaobo Song.
2026-01-12 07:48:36 +00:00
Thomas Adam
ad083677c5 Merge branch 'obsd-master' 2026-01-11 14:01:08 +00:00
nicm
426467856d Add paste to the default pane menu, GitHub issue 4763. 2026-01-11 11:48:46 +00:00
Thomas Adam
844042744e Merge branch 'obsd-master' 2026-01-09 18:01:07 +00:00
nicm
65b3a95be8 Removing padding needs to go through screen_write_collect_trim or there
may end up multiple items covering the same region.
2026-01-09 15:04:00 +00:00
Thomas Adam
ca95950148 Merge branch 'obsd-master' 2026-01-08 14:01:08 +00:00
nicm
6234d79852 Do not set manual size if no window. 2026-01-08 12:05:02 +00:00
Thomas Adam
a5d8b92012 Merge branch 'obsd-master' 2026-01-08 10:01:08 +00:00
nicm
890cfc6dc3 Copy palette responses before processing them since they are not strings. 2026-01-08 07:54:23 +00:00
Thomas Adam
0e6fc2e88b Merge branch 'obsd-master' 2026-01-07 22:01:07 +00:00
nicm
c8ccd420be Reduce request timeout to 500 milliseconds to match the extended
escape-time, and discard palette requests if receiving a reply for a
different index.
2026-01-07 20:16:32 +00:00
nicm
ff4f6b9066 Extend escape timeout if there are active forwarded requests not just
tmux's own requests. GitHub issue 4793.
2026-01-07 20:03:34 +00:00
Thomas Adam
7cffa2de78 Merge branch 'obsd-master' 2026-01-07 20:01:09 +00:00
nicm
e2afaaea75 Correct redrawing of wide characters when overwritten. Reported by Jake
Stewart in GitHub issue 4737.
2026-01-07 18:29:15 +00:00
Thomas Adam
75d3974c12 Merge branch 'obsd-master' 2026-01-07 10:01:08 +00:00
nicm
583f12ea71 Work out the default command from the queue in case it has been set from
the config file, GitHub issue 4791.
2026-01-07 08:16:20 +00:00
Thomas Adam
3de7a8ad8d Merge branch 'obsd-master' 2026-01-06 22:01:07 +00:00
nicm
f6c9052082 If cannot find a terminator for palette responses, treat as a partial
key not complete. GitHub issue 4749.
2026-01-06 20:09:42 +00:00
nicm
f226804149 Do not send theme unless it has changed, and do not send immediately
when updates are enabled. GitHub issue 5787.
2026-01-06 20:05:57 +00:00
Thomas Adam
cf94486e32 Merge branch 'obsd-master' 2026-01-06 16:01:07 +00:00
nicm
035a2f35d4 Clear trimmed lines after moving, from Antony Raj in GitHub issue 4790. 2026-01-06 14:33:05 +00:00
Thomas Adam
9b7732eac4 Merge branch 'obsd-master' 2026-01-06 12:01:07 +00:00
nicm
5f9dac8abc Do not use ;;s in list-keys output as it is confusing and cannot be
parsed on input, from Patrick Motard in GitHub issue 4750.
2026-01-06 10:17:29 +00:00
nicm
ccd4dd7ff2 Do not log theme if pane is NULL. 2026-01-06 09:11:15 +00:00
Thomas Adam
77417b7cbe Merge branch 'obsd-master' 2026-01-05 10:01:08 +00:00
nicm
6a7cd79a63 Redraw pane borders when entering or leaving alternate screen, from Mike
Jonkmans in GitHub issue 4788.
2026-01-05 08:32:19 +00:00
nicm
baa3b51b3e Do not use client if there isn't one, GitHub issue 4789. 2026-01-05 08:30:30 +00:00
Thomas Adam
abeba4910c Merge branch 'obsd-master' 2026-01-04 10:01:08 +00:00
Nicholas Marriott
356d0aedbd Remove an unused declaration (from OpenBSD, same change also sent by Mike
Jonkmans).
2026-01-04 08:41:38 +00:00
nicm
45f23f3a59 Minor tidying of cmd_list_print from Pavel Roskin. 2026-01-04 08:38:26 +00:00
nicm
6ef7375ade Add some missing logging bits for themes. 2026-01-04 08:05:14 +00:00
Thomas Adam
00030aabf7 Merge branch 'obsd-master' 2025-12-25 20:01:08 +00:00
nicm
f72832cc08 Add focus-follows-mouse option, from Barry Wasdell in GitHub issue 4771. 2025-12-25 18:07:00 +00:00
nicm
b5c33ca2b7 Add selection_mode format variable for copy mode, from Mike Jonkmans in
GitHub issue 4773.
2025-12-25 18:05:15 +00:00
Thomas Adam
d607399850 Merge branch 'obsd-master' 2025-12-22 10:01:08 +00:00
nicm
d005803934 Add prompt-command-cursor-style, from Joshua Cooper in GitHub issue 4765. 2025-12-22 08:41:01 +00:00
nicm
a22ec275b4 With status-keys vi, move the cursor left by one when pressing Escape to
enter command mode, like vi. GitHub issue 4767 from Joshua Cooper.
2025-12-22 08:39:35 +00:00
nicm
188f963fe0 Add {current}/{active} for -t for current window or active pane, from
Manuel Einfalt in GitHub issue 4766.
2025-12-22 08:35:04 +00:00
Thomas Adam
9c5df33325 Merge branch 'obsd-master' 2025-12-19 10:01:07 +00:00
nicm
74f60951cc Do not leak items if not using them because of synchronized update. 2025-12-19 08:46:25 +00:00
Nicholas Marriott
54202fcaad Check image size against available screen size properly, GitHub issue 4739. 2025-12-18 13:44:17 +00:00
Nicholas Marriott
fe645c5bcc Fix image scrolling at exact screen height, and add more logging for images. 2025-12-18 09:05:37 +00:00
Thomas Adam
86c15d7ae9 Merge branch 'obsd-master' 2025-12-17 14:01:08 +00:00
Thomas Adam
3c36d5e433 Merge branch 'obsd-master' 2025-12-17 12:01:07 +00:00
nicm
1c7e164c22 Add support for applications to use synchronized output mode (DECSET
2026) to prevent screen tearing during rapid updates. When an
application sends SM ?2026, tmux buffers output until RM ?2026 is received
or a 1-second timeout expires.

From Chris Lloyd with the assistance of Claude Code, GitHub issue 4744.
2025-12-17 11:49:29 +00:00
nicm
ce7eb22e3a Do not have a default prompt cursor colour because some terminals
(urxvt, st) do not support the reset sequence. GitHub issue 4759.
2025-12-17 10:20:21 +00:00
Thomas Adam
739a97140f Merge branch 'obsd-master' 2025-12-17 10:01:07 +00:00
nicm
99ed397e9c Make clock mode seconds synchronized to the second, GitHub issue 4760
from Joao Pedro.
2025-12-17 08:38:12 +00:00
Thomas Adam
d2c7668bc2 Merge branch 'obsd-master' 2025-12-16 00:01:08 +00:00
nicm
7abf3e8bdf Note that switch-client only changes the key table for one key, pointed
out by Jeenu Viswambharan.
2025-12-15 21:21:25 +00:00
Thomas Adam
506b4db762 Merge branch 'obsd-master' 2025-12-15 15:19:29 +00:00
bket
21c27fdcae Simplify argument move using TAILQ_CONCAT()
Replace the manual loop moving each argument from cmd->arguments to
last->arguments with a single TAILQ_CONCAT() call. This makes the code
clearer and more efficient, while preserving identical behavior.

OK nicm@
2025-12-11 04:17:17 +00:00
nicm
672e89a640 Add a scroll-to-mouse command for copy mode to scroll to the mouse
position and bind to the scrollbar, brings the scrollbar keys into line
with the other mouse keys. From Michael Grant, GitHub issue 4731.
2025-12-10 21:24:43 +00:00
Nicholas Marriott
4b810ae493 Add a define for max images and bump to 20 for the moment. 2025-12-09 08:46:00 +00:00
nicm
52e2a7d990 Fix key code for M-BSpace, GitHub issue 4717. 2025-12-09 08:13:59 +00:00
Nicholas Marriott
2e5e9c0298 Fix calculation of scaled SIXEL size, from nincsnevem662 at gmail dot com in
GitHub issue 4739.
2025-12-08 21:41:30 +00:00
nicm
afa05ae15e Use correct style for bottom line when pane status line is on, GitHub
issue 4732.
2025-12-08 21:32:50 +00:00
nicm
f58b8d0d6a Setting working directory after fork means there is a race with
pane_current_path (especially on platforms with systemd which have to
take time to do some additional faffing around). To avoid this, change
it before fork and back in the parent afterwards. GitHub issue 4719.
2025-12-08 08:04:35 +00:00
Nicholas Marriott
5c73d3878d FIx combine test again... 2025-12-07 22:11:45 +00:00
Nicholas Marriott
ef0a7e328c Merge branch 'tmux-3.6a' 2025-12-05 06:20:32 +00:00
Thomas Adam
8e922ab690 Merge branch 'obsd-master' 2025-12-05 02:01:07 +00:00
Thomas Adam
1a3db6b65e Merge branch 'obsd-master' 2025-12-05 00:01:08 +00:00
nicm
987e05ff31 Allow drag in alternate screen again, GitHub issue 4743 reported by Brad
King.
2025-12-04 22:50:34 +00:00
nicm
bd16b22dac Do not read over buffer if format is a single #, and do not loop forever
if UTF-8 is unfinished in a format. Reported by Giorgi Kobakhia im
GitHub issue 4735.
2025-12-04 20:49:57 +00:00
Thomas Adam
0929d8ddfa Merge branch 'obsd-master' 2025-12-04 16:01:08 +00:00
nicm
9d6c69ebde Fix y offset of mouse if status at top. GitHub issue 4738 from Michael
Grant.
2025-12-04 14:45:32 +00:00
Thomas Adam
1bcd360dfe Merge branch 'obsd-master' 2025-12-04 08:01:07 +00:00
nicm
1f2210a3ce Add a missing skin tone, from Jake Stewart in GitHub issue 4736. 2025-12-04 06:04:21 +00:00
nicm
2fe1378d3a Allow characters to be combined in either order, reported by Jake
Stewart in GitHub issue 4726.
2025-12-04 06:02:27 +00:00
Thomas Adam
796539c60b Merge branch 'obsd-master' 2025-12-03 10:01:09 +00:00
Thomas Adam
700936b2ad Merge branch 'obsd-master' 2025-12-03 08:01:07 +00:00
nicm
a28dbe3a59 Improve code readability in colour_palette_get and colour_palette_set.
GitHub issue 4730 from Pavel Roskin.
2025-12-03 07:41:38 +00:00
nicm
820df0f551 Add check that the pane is not in alternate screen mode when in
copy-mode. From Michael Grant in GitHub issue 4728.
2025-12-03 07:38:46 +00:00
nicm
ee9cf1bbaa Fix the size caluation for left-right windows used to spread out cells
horizontally evenly. From Michael Grant in GitHub issue 4724.
2025-12-03 07:35:32 +00:00
Thomas Adam
02a8e3fd34 Merge branch 'obsd-master' 2025-12-02 10:01:08 +00:00
nicm
322adfbdde Add a get-clipboard option which when enabled (the default is off) uses
the same mechanism as palette requests to request clipboard from the
terminal and forward to the requesting pane. Remove the now-redundant
forward-to-pane ability from "refresh-client -l". GitHub issue 4275.
2025-12-02 08:20:32 +00:00
Thomas Adam
194d0a0e25 Merge branch 'obsd-master' 2025-12-01 10:01:08 +00:00
Nicholas Marriott
b2d6ebaa10 Fix combine-test.result, GitHub issue 4717. 2025-12-01 08:22:12 +00:00
nicm
e4c552f5a5 Change noattr to be an explicit attribute in the style so that it works
correctly and does not delete attributes set in the style itself, GitHub
issue 4713.
2025-12-01 08:14:29 +00:00
nicm
04f32073c0 Add horizontal border case to server_client_check_mouse_in_pane to fix
mouse resizing. GitHub issue 4720 from Michael Grant, reported by
someone in GitHub issue 4715.
2025-12-01 08:04:26 +00:00
Thomas Adam
e9afd2bb5e Merge branch 'obsd-master' 2025-11-28 12:01:09 +00:00
nicm
55d660a548 Do not remove TERM etc for commands run from config file, reported by
Dennis Eriksen.
2025-11-28 09:42:48 +00:00
nicm
dfaf47d97c session_index was never actually implemented, remove from man page. 2025-11-28 09:14:17 +00:00
Thomas Adam
3542bfa5b8 Merge branch 'obsd-master' 2025-11-26 22:01:08 +00:00
nicm
e3a54ed0f4 Newer libevents do not allow event_del on a zero'd event. 2025-11-26 19:02:03 +00:00
nicm
a0dfef3b04 Place cursor on correct line if message-line is not 0, reported by
Alexis Hildebrandt.
2025-11-26 18:57:18 +00:00
Nicholas Marriott
bfa2f73335 Need signal.h for utempter, from Yasuhiro Kimura. 2025-11-26 10:41:17 +00:00
Nicholas Marriott
1decccace7 Update version. 2025-11-26 08:44:41 +00:00
Nicholas Marriott
ee53d49059 Merge branch 'tmux-3.6' 2025-11-26 08:44:18 +00:00
Thomas Adam
f949f9a23a Merge branch 'obsd-master' 2025-11-26 00:01:08 +00:00
nicm
f0dec832b8 Add seconds options for clock mode, from augustus7613 dot mail at pm dot
me in GitHub issue 4697.
2025-11-25 21:24:27 +00:00
156 changed files with 12234 additions and 6369 deletions

2
.github/README.md vendored
View File

@@ -79,7 +79,7 @@ A small example configuration is in `example_tmux.conf`.
And a bash(1) completion file at: And a bash(1) completion file at:
https://github.com/scop/bash-completion/blob/main/completions/tmux https://github.com/scop/bash-completion/blob/main/completions-core/tmux.bash
For debugging, run tmux with `-v` or `-vv` to generate server and client log For debugging, run tmux with `-v` or `-vv` to generate server and client log
files in the current directory. files in the current directory.

11
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,11 @@
For pull request code reviews in this repository, do not leave any review comments under any circumstances.
If asked to review a pull request, produce no comments.
Do not comment on pull requests in this repository.
For pull request code reviews in this repository, do not leave a pull request overview.
Do not leave inline review comments.
If asked to review a pull request, provide no summary, no overview, and no code comments.

View File

@@ -17,7 +17,7 @@ jobs:
action: action:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/lock-threads@v5 - uses: dessant/lock-threads@v6
with: with:
github-token: ${{ github.token }} github-token: ${{ github.token }}
issue-inactive-days: '30' issue-inactive-days: '30'

View File

@@ -29,6 +29,7 @@ Ted Unangst <tedu@openbsd.org> tedu <tedu>
Theo de Raadt <deraadt@openbsd.org> Theo Deraadt <deraadt@openbsd.org> Theo de Raadt <deraadt@openbsd.org> Theo Deraadt <deraadt@openbsd.org>
Theo de Raadt <deraadt@openbsd.org> deraadt <deraadt> Theo de Raadt <deraadt@openbsd.org> deraadt <deraadt>
Thomas Adam <thomas@xteddy.org> Thomas <thomas@xteddy.org> Thomas Adam <thomas@xteddy.org> Thomas <thomas@xteddy.org>
Thomas Adam <thomas@xteddy.org> Thomas Adam <thomas.adam22@gmail.com>
Thomas Adam <thomas@xteddy.org> Thomas Adam <thomas.adam@smoothwall.net> Thomas Adam <thomas@xteddy.org> Thomas Adam <thomas.adam@smoothwall.net>
Thomas Adam <thomas@xteddy.org> n6tadam <n6tadam@xteddy.org> Thomas Adam <thomas@xteddy.org> n6tadam <n6tadam@xteddy.org>
Tim van der Molen <tim@openbsd.org> tim <tim> Tim van der Molen <tim@openbsd.org> tim <tim>

348
CHANGES
View File

@@ -1,3 +1,351 @@
CHANGES FROM 3.7a TO 3.7b
* Fix so that the end of a synchronized update again triggers a redraw.
CHANGES FROM 3.7 TO 3.7a
* Fix crash in break-pane when no name is provided.
* Scrollbar options are now cached rather than being looked up for every redraw
(issue 5298).
* Only forbid #( in names, allow #[, empty names, : and .
CHANGES FROM 3.6b TO 3.7
* Add floating panes. These are panes which sit above the layout ("tiled
panes") like popups but unlike popups are not modal and behave like panes (so
the same escape sequence support). Floating panes are created with the
new-pane command, bound to * by default.
This is an early release of this feature and they are relatively limited.
Currently floating panes can only be moved and resized using the mouse. The
default second status line (if status-format is set to 2) has changed to show
a list of panes. Many obvious features are not yet available for floating
panes (notably the ability to swap floating panes, resize them using
resize-pane, change them between floating and tiles, and restore custom
layouts with floating panes).
Mostly written by Michael Grant with help from Dane Jensen; testing and fixes
from others.
* Allow run-shell arguments after a shell command to be expanded as #{1}, #{2}
and so on (from Rasmus Thystrup Karstensen in issue 5121).
* Add -g to kill-session to kill all sessions in a session group (issue 5157
from github at jiku dot jp).
* Tighten up read-only checks on attach-session, detach-client and
switch-client so that a user should be able to only detach their own client
(reported by John Walker).
* Increase escape delay if the buffer contains a partial paste end, fixes
issues with at least Windows Terminal (from jing dot empty at gmail.com issue
5088).
* When mode-keys is set to vi, do not allow the cursor to go into the invisible
extra cell to the right of the visible text; this is closer to what vi(1)
does (from Max Vim in issue 5070).
* Add a five second limit on pasting for terminals which mysteriously lose the
end sequence if the paste is too big (that is, Terminal.app) (reported by
Garri Djavadyan in issue 4527).
* Show file open errors more sensibly (reported by Meriel Luna Mittelbach in
issue 5081).
* Update supported features list for Foot terminal (from Meriel Luna Mittelbach
in issue 5079).
* Turn off the "is this a paste" guessing if the terminal supports bracket
pasting instead (issue 5031).
* Check FIONREAD for all panes not just piped panes.
* Add emacs-style recentre-top-bottom command to copy mode (issue 5053 from
sinyax75 at gmail dot com).
* Allow the indicator in tree mode to be customized by two new options:
tree-mode-preview-format and tree-mode-preview-style.
* Fix control client hang on exit after toggling no-output (issue 5049 from
Aaron Campbell). Also various other control mode fixes.
* Add support for line numbers in copy mode. There is a new
copy-mode-line-numbers option which may be set to off, default (tmux's normal
line numbering where 0 is the top visible line), absolute (first line in
history is 1), relative (relative to the cursor) and hybrid (current line is
absolute, others relative). Also adds copy-mode-line-number-style and
copy-mode-current-line-number-style to set the style of the line numbers.
When copy mode is entered with the mouse, line numbers stay off. From Leo
Henon in issue 5025.
* Make C-[ have the same bindings as Escape for terminals with extended
keys where they are different (issue 5035 from Eric Nicolas).
* Sanitize paste buffer names in paste_set and paste_rename (issue 5032 from
Barrett Ruth).
* Do not hang in run-shell when job_run fails (from Barrett Ruth in issue
5037).
* Add ability to forward progress bar to outside terminal (issue 4972
from Eric Dorland).
* Translate keypad keys to text in prompt input (from Barrett Ruth in issue
4996).
* Sanitize pane titles and window and session names more consistently and
strictly, prevents C0 characters and other invisible characters causing
problems (reported by Chris Monardo in issue 4999).
* Make clock visible on terminals without colours (from Manuel Einfalt in issue
5001).
* Add detach to default session menu (suggested by Przemyslaw Sztoch).
* Include window format variables for pane notifications (issue 5007 from Saul
Nogueras).
* Limit precision to 100 for formats to stop silly formats from running out of
memory, reported by z1281552865 at gmail dot com. Also various other similar
changes, mostly found by OSS-Fuzz.
* Add WAYLAND_DISPLAY to default update-environment (issue 4965). Also some
additional XDG_* variables (issue 5169).
* Add -C flag to command-prompt to match display-message -C (do not freeze
panes) (from Barrett Ruth in issue 4978).
* Cache user from getpwuid because it can be very expensive on some
platforms (from Ben Maurer in issue 4973).
* Add remain-on-exit key to keep pane around until a key is pressed (from
Michael Grant).
* Add some new mouse ranges called "control0" to "control9" and use to add some
mouse controls to the pane state line (from Dane Jensen with some bits from
Michael Grant).
* Handle OSC 9;4 progress bar sequence and store in format variables (from Eric
Dorland in issue 4954).
* Correctly size buffer used for parsing clipboard sequences (from Michal
Majchrowicz).
* Limit MSG_COMMAND argument to between 0 and 1000 to prevent a misbehaving
client from crashing the server (from Michal Majchrowicz).
* Reorganize host keys are represented internally so they can be built more
easily (from Dane Jensen in issue 4953).
* Add new fuzzers for command parsing, formats and styles (from David
Korczynski in issue 4957). Also fix various issues shown from these.
* Add bracket_paste_flag format flag (from George Nachman in issue 4951).
* Use \- for hyphens in tmux.1 to cause newer groff versions to render them
correctly (from Keith Thompson in issue 4948).
* Various minor code improvements and fixes from Pavel Lavrukhin (issue 4936
and others).
* Use window options for cursor-style to avoid crash when no pane (from Arden
Packeer in issue 4942).
* Fix issue where popup window gets overwritten by background updates (from
Conor Taylor in issue 4920).
* Protect against overflow when scrollbar is off screen (from san65384 at gmail
dot com in issue 4933).
* Copy hyperlinks when redrawing popup so they do not vanish (from Antoine
Gaudreau Simard in issue 4925).
* Work around systemd killing panes early during system shutdown by creating
dependencies from the panes to the service which started tmux (issue 4926
from Dmitry Torokhov).
* Allow codepoint-widths to accept ranges (from san65384 at gmail dot com in
issue 4930).
* Add a short builtin help text for each mode accessible with C-h (based on
code from Patrick Motard in issue 4751).
* Draw message as one format, allowing prompts and messages to occupy only a
portion of the status bar, overlaying the normal status content rather than
replacing the entire line. A new message-format option now controls the
entire message (like status-format). The message-style option now need to
include "fill" in order to cover the whole width (the default has
"fill=yellow"). From Conor Taylor in issue 4861.
* Add next/previous variables for windows in W: loop (from Conor Taylor in
issue 4856).
* Various bug and memory leak fixes from Renaud Allard (issue 4916).
* Add pane_pipe_pid with pipe file descriptor.
* Make -c work with new-session -A (from Jody Frankowski in issue 4906).
* Allow copy mode to work for readonly clients, except for copy commands (from
Dane Jensen).
* Pass paste buffer through vis(3) when pasting to prevent buffers containing
for example the bracket end sequence causing issues, a new -S flag disables
(reported by Mason Davis).
* Add sorting (-O flag) and a custom format (-F) to list-keys (from Dane Jensen
in issue 4845).
* Add scroll-exit-on, scroll-exit-off, scroll-exit-toggle commands to copy mode
(from xcdnlgd at hotmail dot com in issue 4884).
* Respond to DECRQM 2026 (from David Turnbull in issue 4887) and various others
(from Ayman Bagabas in issue 5118).
* Fix various memory leaks reported by Huihui Huang (issue 4872).
* Pass which clipboard is set through to the terminal (from Axel Lindskog in
issue 4858).
* Reuse extended entry when clearing RGB cell, to prevent memory growth when
cells are repeatedly cleared (from Michael K Darling in issue 4862).
* Do not write before buffer when parsing empty clipboard or palette replies,
or try to allocate zero bytes with an empty clipboard sequence (reported by
DongHan Kim).
* Various bug fixes and code improvements from Conor Taylor (issue 4848).
* Clear search counts when clearing marks in case of repeated search (reported
by Daniel Pereira in issue 4817).
* Make OSC 52 work in popups (from gogongxt at 163 dot com in issue 4797).
* Refresh copy mode when style changes (from Josh Cooper in issue 4830).
* Make sorting code common and add -O for sorting to the list commands (from
Dane Jensen in issue 4813).
* Do not treat cells as empty unless the background colour stays the same,
fixes invisible clock in clock mode (reported by Theo Buehler).
* When history-limit is changed, apply to existing panes, not just new
ones (issue 4705).
* Reevaluate menu and popup styles on each draw to allow them to change when
options change (from Josh Cooper in issues 4828 and 4829).
* Handle theme keys earlier so they are processed even if a popup is open (from
Josh Cooper in issue 4827).
* Fix window-size=latest not resizing on switch-client in session groups (from
Ilya Grigoriev in issue 4818).
* Add -e flag to command-prompt to close if empty (from Dane Jensen in issue
4812).
* Correctly draw indicators when pane-border-indicators is set to both
(reported by Ilya Grigoriev in issue 4780).
* Remember last pane or type of location for double and triple clicks and
correctly handle it changes between first and second or second and third
(issue 4795 from Shaobo Song).
* Add paste to the default pane menu (issue 4763).
* Reduce request timeout to 500 milliseconds to match the extended escape-time,
and discard palette requests if receiving a reply for a different index.
* Extend escape timeout if there are active forwarded requests not just
tmux's own requests (issue 4793).
* Correct redrawing of wide characters when overwritten (reported by Jake
Stewart in issue 4737).
* If cannot find a terminator for palette responses, treat as a partial key not
complete (issue 4749).
* Do not send theme unless it has changed, and do not send immediately when
updates are enabled (issue 5787).
* Do not use ;;s in list-keys output as it is confusing and cannot be
parsed on input (from Patrick Motard in issue 4750).
* Redraw pane borders when entering or leaving alternate screen (from Mike
Jonkmans in issue 4788).
* Add focus-follows-mouse option (from Barry Wasdell in issue 4771).
* Add selection_mode format variable for copy mode (from Mike Jonkmans in issue
4773).
* Add prompt-command-cursor-style (from Joshua Cooper in issue 4765).
* With status-keys vi, move the cursor left by one when pressing Escape to
enter command mode, like vi (issue 4767 from Joshua Cooper).
* Add {current}/{active} for -t for current window or active pane (from Manuel
Einfalt, issue 4766).
* Add support for applications to use synchronized output mode (DECSET 2026) to
prevent screen tearing during rapid updates (from Chris Lloyd in issue 4744).
* Do not set a default prompt cursor colour because some terminals (urxvt, st)
do not support the reset sequence (issue 4759).
* Add a scroll-to-mouse command for copy mode to scroll to the mouse position
and bind to the scrollbar, brings the scrollbar keys into line with the other
mouse keys (from Michael Grant in issue 4731).
* Bump the maximum number of SIXEL images to 20.
* Fix key code for M-BSpace (issue 4717).
* Fix calculation of scaled SIXEL size (from nincsnevem662 at gmail dot com in
issue 4739). Also various other fixes and improvements for SIXEL.
* Fix a race between fork and pane_current_path, most noticeable on systems
where starting processes is slow (issue 4719).
* Fix mouse position calculation on scrollbar with pane status line at the top
(issue 4738 from Michael Grant).
* Do not read outside buffer if format is a single #, and do not loop forever
if UTF-8 is unfinished in a format (reported by Giorgi Kobakhia in issue
4735).
* Add a missing skin tone character (from Jake Stewart in issue 4736).
* Allow UTF-8 characters to be combined in either order (reported by Jake
Stewart in issue 4726).
* Do not show scrollbar when entering copy mode from a pane in the alternate
screen (issue 4728 from Michael Grant).
* Fix the size calculation for left-right windows used to spread out cells
horizontally evenly (from Michael Grant in issue 4724).
* Add a get-clipboard option which when enabled (the default is off) and a
clipboard is requested from a pane, requests it from the terminal and
forwards to the requesting pane; also remove the now-redundant
forward-to-pane ability from "refresh-client -l" (issue 4275).
* Fix the noattr attribute in styles, used by the default mode-style (issue
4713).
* Do not remove TERM for commands run from config file (regression reported by
Dennis Eriksen).
* Add seconds options for clock mode (from augustus7613 dot mail at pm dot me,
issue 4697; later improved by Joao Pedro in issue 4760).
CHANGES FROM 3.6a TO 3.6b
* Remove images from the correct list when they are removed while in the
alternate screen (reported by xlabai at tencent dot com).
CHANGES FROM 3.6 TO 3.6a CHANGES FROM 3.6 TO 3.6a
* Fix a buffer overread and an infinite loop in format processing (reported by * Fix a buffer overread and an infinite loop in format processing (reported by

View File

@@ -1,9 +1,4 @@
THIS IS FOR INFORMATION ONLY, CODE IS UNDER THE LICENCE AT THE TOP OF ITS FILE. Copyright (c) Various Authors
The README, CHANGES, FAQ and TODO files are licensed under the ISC license. All
other files have a license and copyright notice at their start, typically:
Copyright (c) <author>
Permission to use, copy, modify, and distribute this software for any Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above

View File

@@ -20,21 +20,31 @@ LDADD = $(LIBOBJS)
# Set flags for gcc. # Set flags for gcc.
if IS_GCC if IS_GCC
AM_CFLAGS += -std=gnu99 -O2 AM_CFLAGS += -std=gnu99
if IS_OPTIMIZED
AM_CFLAGS += -O2
else
AM_CFLAGS += -O0
endif
if IS_DEBUG if IS_DEBUG
AM_CFLAGS += -g AM_CFLAGS += -g3 -ggdb
AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2 AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2 -Wno-use-after-free
AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wno-cast-align
AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes
AM_CFLAGS += -Wno-unused-result -Wno-format-y2k AM_CFLAGS += -Wno-unused-result -Wno-format-y2k -Wno-unknown-warning-option
AM_CFLAGS += -Wno-maybe-uninitialized
if IS_DARWIN if IS_DARWIN
AM_CFLAGS += -Wno-deprecated-declarations -Wno-cast-align -Wno-macro-redefined AM_CFLAGS += -Wno-deprecated-declarations -Wno-macro-redefined
endif endif
AM_CPPFLAGS += -DDEBUG AM_CPPFLAGS += -DDEBUG
endif endif
AM_CPPFLAGS += -iquote. AM_CPPFLAGS += -iquote.
if IS_ASAN
AM_CFLAGS += -fsanitize=address
AM_LDFLAGS += -fsanitize=address
endif
endif endif
# Set flags for Solaris. # Set flags for Solaris.
@@ -100,6 +110,7 @@ dist_tmux_SOURCES = \
cmd-kill-window.c \ cmd-kill-window.c \
cmd-list-buffers.c \ cmd-list-buffers.c \
cmd-list-clients.c \ cmd-list-clients.c \
cmd-list-commands.c \
cmd-list-keys.c \ cmd-list-keys.c \
cmd-list-panes.c \ cmd-list-panes.c \
cmd-list-sessions.c \ cmd-list-sessions.c \
@@ -183,6 +194,7 @@ dist_tmux_SOURCES = \
server-fn.c \ server-fn.c \
server.c \ server.c \
session.c \ session.c \
sort.c \
spawn.c \ spawn.c \
status.c \ status.c \
style.c \ style.c \
@@ -190,6 +202,7 @@ dist_tmux_SOURCES = \
tmux.h \ tmux.h \
tmux-protocol.h \ tmux-protocol.h \
tty-acs.c \ tty-acs.c \
tty-draw.c \
tty-features.c \ tty-features.c \
tty-keys.c \ tty-keys.c \
tty-term.c \ tty-term.c \
@@ -227,10 +240,21 @@ if ENABLE_SIXEL
dist_tmux_SOURCES += image.c image-sixel.c dist_tmux_SOURCES += image.c image-sixel.c
endif endif
# Add bits for fuzzing if enabled.
if NEED_FUZZING if NEED_FUZZING
check_PROGRAMS = fuzz/input-fuzzer check_PROGRAMS = \
fuzz/input-fuzzer \
fuzz/cmd-parse-fuzzer \
fuzz/format-fuzzer \
fuzz/style-fuzzer
fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS) fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)
fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS) fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
fuzz_cmd_parse_fuzzer_LDFLAGS = $(FUZZING_LIBS)
fuzz_cmd_parse_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
fuzz_format_fuzzer_LDFLAGS = $(FUZZING_LIBS)
fuzz_format_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
fuzz_style_fuzzer_LDFLAGS = $(FUZZING_LIBS)
fuzz_style_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
endif endif
# Install tmux.1 in the right format. # Install tmux.1 in the right format.
@@ -245,3 +269,7 @@ install-exec-hook:
$(mkdir_p) $(DESTDIR)$(mandir)/man1 $(mkdir_p) $(DESTDIR)$(mandir)/man1
$(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \ $(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \
$(DESTDIR)$(mandir)/man1/tmux.1 $(DESTDIR)$(mandir)/man1/tmux.1
# And uninstall it.
uninstall-hook:
rm -f $(DESTDIR)$(mandir)/man1/tmux.1

View File

@@ -61,6 +61,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
struct window_pane *wp; struct window_pane *wp;
char *cwd, *cause; char *cwd, *cause;
enum msgtype msgtype; enum msgtype msgtype;
uid_t uid;
if (RB_EMPTY(&sessions)) { if (RB_EMPTY(&sessions)) {
cmdq_error(item, "no sessions"); cmdq_error(item, "no sessions");
@@ -106,8 +107,16 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
} }
if (fflag) if (fflag)
server_client_set_flags(c, fflag); server_client_set_flags(c, fflag);
if (rflag) if (rflag) {
if (c->flags & CLIENT_READONLY) {
uid = proc_get_peer_uid(c->peer);
if (uid != getuid()) {
cmdq_error(item, "client is read-only");
return (CMD_RETURN_ERROR);
}
}
c->flags |= (CLIENT_READONLY|CLIENT_IGNORESIZE); c->flags |= (CLIENT_READONLY|CLIENT_IGNORESIZE);
}
c->last_session = c->session; c->last_session = c->session;
if (c->session != NULL) { if (c->session != NULL) {

View File

@@ -58,9 +58,14 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
struct session *dst_s = target->s; struct session *dst_s = target->s;
struct window_pane *wp = source->wp; struct window_pane *wp = source->wp;
struct window *w = wl->window; struct window *w = wl->window;
char *name, *cause, *cp; char *newname, *cause, *cp;
int idx = target->idx, before; int idx = target->idx, before;
const char *template; const char *template, *name = args_get(args, 'n');
if (name != NULL && !check_name(name)) {
cmdq_error(item, "invalid window name: %s", name);
return (CMD_RETURN_ERROR);
}
before = args_has(args, 'b'); before = args_has(args, 'b');
if (args_has(args, 'a') || before) { if (args_has(args, 'a') || before) {
@@ -73,15 +78,15 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
} }
server_unzoom_window(w); server_unzoom_window(w);
if (window_count_panes(w) == 1) { if (window_count_panes(w, 1) == 1) {
if (server_link_window(src_s, wl, dst_s, idx, 0, if (server_link_window(src_s, wl, dst_s, idx, 0,
!args_has(args, 'd'), &cause) != 0) { !args_has(args, 'd'), &cause) != 0) {
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (args_has(args, 'n')) { if (name != NULL) {
window_set_name(w, args_get(args, 'n')); window_set_name(w, name, 0);
options_set_number(w->options, "automatic-rename", 0); options_set_number(w->options, "automatic-rename", 0);
} }
server_unlink_window(src_s, wl); server_unlink_window(src_s, wl);
@@ -96,6 +101,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
} }
TAILQ_REMOVE(&w->panes, wp, entry); TAILQ_REMOVE(&w->panes, wp, entry);
TAILQ_REMOVE(&w->z_index, wp, zentry);
server_client_remove_pane(wp); server_client_remove_pane(wp);
window_lost_pane(w, wp); window_lost_pane(w, wp);
layout_close_pane(wp); layout_close_pane(wp);
@@ -104,15 +110,16 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
options_set_parent(wp->options, w->options); options_set_parent(wp->options, w->options);
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
TAILQ_INSERT_HEAD(&w->panes, wp, entry); TAILQ_INSERT_HEAD(&w->panes, wp, entry);
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
w->active = wp; w->active = wp;
w->latest = tc; w->latest = tc;
if (!args_has(args, 'n')) { if (name == NULL) {
name = default_window_name(w); newname = default_window_name(w);
window_set_name(w, name); window_set_name(w, newname, 0);
free(name); free(newname);
} else { } else {
window_set_name(w, args_get(args, 'n')); window_set_name(w, name, 0);
options_set_number(w->options, "automatic-rename", 0); options_set_number(w->options, "automatic-rename", 0);
} }

View File

@@ -29,18 +29,21 @@
static enum cmd_retval cmd_capture_pane_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_capture_pane_exec(struct cmd *, struct cmdq_item *);
static char *cmd_capture_pane_append(char *, size_t *, char *, size_t); static char *cmd_capture_pane_append(char *, size_t *, const char *,
size_t);
static char *cmd_capture_pane_pending(struct args *, struct window_pane *, static char *cmd_capture_pane_pending(struct args *, struct window_pane *,
size_t *); size_t *);
static char *cmd_capture_pane_history(struct args *, struct cmdq_item *, static char *cmd_capture_pane_history(struct args *, struct cmdq_item *,
struct window_pane *, size_t *); struct window_pane *, size_t *);
static char *cmd_capture_pane_hyperlinks(struct grid *, struct screen *,
u_int, u_int *, u_int *, size_t *);
const struct cmd_entry cmd_capture_pane_entry = { const struct cmd_entry cmd_capture_pane_entry = {
.name = "capture-pane", .name = "capture-pane",
.alias = "capturep", .alias = "capturep",
.args = { "ab:CeE:JMNpPqS:Tt:", 0, 0, NULL }, .args = { "ab:CeE:FHJLMNpPqS:Tt:", 0, 0, NULL },
.usage = "[-aCeJMNpPqT] " CMD_BUFFER_USAGE " [-E end-line] " .usage = "[-aCeFHJLMNpPqT] " CMD_BUFFER_USAGE " [-E end-line] "
"[-S start-line] " CMD_TARGET_PANE_USAGE, "[-S start-line] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -63,7 +66,8 @@ const struct cmd_entry cmd_clear_history_entry = {
}; };
static char * static char *
cmd_capture_pane_append(char *buf, size_t *len, char *line, size_t linelen) cmd_capture_pane_append(char *buf, size_t *len, const char *line,
size_t linelen)
{ {
buf = xrealloc(buf, *len + linelen + 1); buf = xrealloc(buf, *len + linelen + 1);
memcpy(buf + *len, line, linelen); memcpy(buf + *len, line, linelen);
@@ -103,6 +107,46 @@ cmd_capture_pane_pending(struct args *args, struct window_pane *wp,
return (buf); return (buf);
} }
static char *
cmd_capture_pane_hyperlinks(struct grid *gd, struct screen *s, u_int py,
u_int *links, u_int *nlinks, size_t *len)
{
const struct grid_line *gl = grid_peek_line(gd, py);
struct grid_cell gc;
const char *uri;
char *line = xstrdup("");
u_int i, j;
*len = 0;
if (s->hyperlinks == NULL || (~gl->flags & GRID_LINE_HYPERLINK))
return (line);
for (i = 0; i < gl->cellused; i++) {
grid_get_cell(gd, i, py, &gc);
if (gc.link == 0)
continue;
for (j = 0; j < *nlinks; j++) {
if (links[j] == gc.link)
break;
}
if (j != *nlinks)
continue;
if (!hyperlinks_get(s->hyperlinks, gc.link, &uri, NULL, NULL))
continue;
if (*nlinks == gd->sx)
break;
links[(*nlinks)++] = gc.link;
if (*len != 0)
line = cmd_capture_pane_append(line, len, " ", 1);
line = cmd_capture_pane_append(line, len, uri, strlen(uri));
}
return (line);
}
static char * static char *
cmd_capture_pane_history(struct args *args, struct cmdq_item *item, cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
struct window_pane *wp, size_t *len) struct window_pane *wp, size_t *len)
@@ -112,9 +156,11 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
struct screen *s; struct screen *s;
struct grid_cell *gc = NULL; struct grid_cell *gc = NULL;
struct window_mode_entry *wme; struct window_mode_entry *wme;
int n, join_lines, flags = 0; int n, join_lines, number_lines, flags = 0;
int show_flags, hyperlinks;
u_int *links = NULL, nlinks = 0;
u_int i, sx, top, bottom, tmp; u_int i, sx, top, bottom, tmp;
char *cause, *buf, *line; char *cause, *buf = NULL, *line, b[64], *cp;
const char *Sflag, *Eflag; const char *Sflag, *Eflag;
size_t linelen; size_t linelen;
@@ -152,7 +198,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
if (cause != NULL) { if (cause != NULL) {
top = gd->hsize; top = gd->hsize;
free(cause); free(cause);
} else if (n < 0 && (u_int) -n > gd->hsize) } else if (n < 0 && (u_int)-n > gd->hsize)
top = 0; top = 0;
else else
top = gd->hsize + n; top = gd->hsize + n;
@@ -169,7 +215,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
if (cause != NULL) { if (cause != NULL) {
bottom = gd->hsize + gd->sy - 1; bottom = gd->hsize + gd->sy - 1;
free(cause); free(cause);
} else if (n < 0 && (u_int) -n > gd->hsize) } else if (n < 0 && (u_int)-n > gd->hsize)
bottom = 0; bottom = 0;
else else
bottom = gd->hsize + n; bottom = gd->hsize + n;
@@ -192,12 +238,57 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
flags |= GRID_STRING_EMPTY_CELLS; flags |= GRID_STRING_EMPTY_CELLS;
if (!join_lines && !args_has(args, 'N')) if (!join_lines && !args_has(args, 'N'))
flags |= GRID_STRING_TRIM_SPACES; flags |= GRID_STRING_TRIM_SPACES;
number_lines = args_has(args, 'L');
show_flags = args_has(args, 'F');
hyperlinks = args_has(args, 'H');
if (hyperlinks)
links = xreallocarray(NULL, gd->sx, sizeof *links);
buf = NULL;
for (i = top; i <= bottom; i++) { for (i = top; i <= bottom; i++) {
line = grid_string_cells(gd, 0, i, sx, &gc, flags, s); if (hyperlinks) {
linelen = strlen(line); line = cmd_capture_pane_hyperlinks(gd, s, i, links,
&nlinks, &linelen);
} else {
line = grid_string_cells(gd, 0, i, sx, &gc, flags, s);
linelen = strlen(line);
}
if (hyperlinks && linelen == 0) {
free(line);
continue;
}
if (number_lines) {
if (i >= gd->hsize)
n = i - gd->hsize;
else
n = (int)i - (int)gd->hsize;
n = snprintf(b, sizeof b, "%d ", n);
if (n >= 0)
buf = cmd_capture_pane_append(buf, len, b, n);
}
if (show_flags) {
cp = b;
*cp = '\0';
gl = grid_peek_line(gd, i);
if (gl->flags & GRID_LINE_DEAD)
*cp++ = 'D';
if (gl->flags & GRID_LINE_HYPERLINK)
*cp++ = 'H';
if (gl->flags & GRID_LINE_START_OUTPUT)
*cp++ = 'O';
if (gl->flags & GRID_LINE_START_PROMPT)
*cp++ = 'P';
if (gl->flags & GRID_LINE_WRAPPED)
*cp++ = 'W';
if (gl->flags & GRID_LINE_EXTENDED)
*cp++ = 'X';
if (b == cp)
*cp++ = '-';
*cp++ = ' ';
*cp = '\0';
buf = cmd_capture_pane_append(buf, len, b, strlen (b));
}
buf = cmd_capture_pane_append(buf, len, line, linelen); buf = cmd_capture_pane_append(buf, len, line, linelen);
gl = grid_peek_line(gd, i); gl = grid_peek_line(gd, i);
@@ -206,6 +297,9 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
free(line); free(line);
} }
free(links);
if (buf == NULL)
buf = xstrdup("");
return (buf); return (buf);
} }
@@ -228,7 +322,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
} }
len = 0; len = 0;
if (args_has(args, 'P')) if (args_has(args, 'P') && !args_has(args, 'H'))
buf = cmd_capture_pane_pending(args, wp, &len); buf = cmd_capture_pane_pending(args, wp, &len);
else else
buf = cmd_capture_pane_history(args, item, wp, &len); buf = cmd_capture_pane_history(args, item, wp, &len);

View File

@@ -98,6 +98,13 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
const struct window_mode *mode; const struct window_mode *mode;
enum sort_order order;
order = sort_order_from_string(args_get(args, 'O'));
if (order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
if (cmd_get_entry(self) == &cmd_choose_buffer_entry) { if (cmd_get_entry(self) == &cmd_choose_buffer_entry) {
if (paste_is_empty()) if (paste_is_empty())

View File

@@ -42,8 +42,8 @@ const struct cmd_entry cmd_command_prompt_entry = {
.name = "command-prompt", .name = "command-prompt",
.alias = NULL, .alias = NULL,
.args = { "1bFkliI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse }, .args = { "1CbeFiklI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1bFkliN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE .usage = "[-1CbeFiklN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
" [-T prompt-type] [template]", " [-T prompt-type] [template]",
.flags = CMD_CLIENT_TFLAG, .flags = CMD_CLIENT_TFLAG,
@@ -84,7 +84,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
const char *type, *s, *input; const char *type, *s, *input;
struct cmd_command_prompt_cdata *cdata; struct cmd_command_prompt_cdata *cdata;
char *tmp, *prompts, *prompt, *next_prompt; char *tmp, *prompts, *prompt, *next_prompt;
char *inputs = NULL, *next_input; char *inputs = NULL, *next_input;
u_int count = args_count(args); u_int count = args_count(args);
@@ -163,6 +163,10 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata->flags |= PROMPT_INCREMENTAL; cdata->flags |= PROMPT_INCREMENTAL;
else if (args_has(args, 'k')) else if (args_has(args, 'k'))
cdata->flags |= PROMPT_KEY; cdata->flags |= PROMPT_KEY;
else if (args_has(args, 'e'))
cdata->flags |= PROMPT_BSPACE_EXIT;
if (args_has(args, 'C'))
cdata->flags |= PROMPT_NOFREEZE;
status_prompt_set(tc, target, cdata->prompts[0].prompt, status_prompt_set(tc, target, cdata->prompts[0].prompt,
cdata->prompts[0].input, cmd_command_prompt_callback, cdata->prompts[0].input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags, cdata->prompt_type); cmd_command_prompt_free, cdata, cdata->flags, cdata->prompt_type);
@@ -234,7 +238,7 @@ out:
static void static void
cmd_command_prompt_free(void *data) cmd_command_prompt_free(void *data)
{ {
struct cmd_command_prompt_cdata *cdata = data; struct cmd_command_prompt_cdata *cdata = data;
u_int i; u_int i;
for (i = 0; i < cdata->count; i++) { for (i = 0; i < cdata->count; i++) {

View File

@@ -92,6 +92,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
cdata->confirm_key = confirm_key[0]; cdata->confirm_key = confirm_key[0];
else { else {
cmdq_error(item, "invalid confirm key"); cmdq_error(item, "invalid confirm key");
cmd_list_free(cdata->cmdlist);
free(cdata); free(cdata);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }

View File

@@ -36,7 +36,7 @@ const struct cmd_entry cmd_copy_mode_entry = {
.source = { 's', CMD_FIND_PANE, 0 }, .source = { 's', CMD_FIND_PANE, 0 },
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK|CMD_READONLY,
.exec = cmd_copy_mode_exec .exec = cmd_copy_mode_exec
}; };
@@ -63,6 +63,8 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
struct client *c = cmdq_get_client(item); struct client *c = cmdq_get_client(item);
struct session *s; struct session *s;
struct window_pane *wp = target->wp, *swp; struct window_pane *wp = target->wp, *swp;
u_int tty_ox, tty_oy, tty_sx, tty_sy;
int line_numbers;
if (args_has(args, 'q')) { if (args_has(args, 'q')) {
window_pane_reset_mode_all(wp); window_pane_reset_mode_all(wp);
@@ -85,17 +87,23 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
swp = source->wp; swp = source->wp;
else else
swp = wp; swp = wp;
line_numbers = 1;
if (event != NULL && KEYC_IS_MOUSE(event->key))
line_numbers = 0;
if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) { if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) {
window_copy_set_line_numbers(wp, line_numbers);
if (args_has(args, 'M')) if (args_has(args, 'M'))
window_copy_start_drag(c, &event->m); window_copy_start_drag(c, &event->m);
} } else
window_copy_set_line_numbers(wp, line_numbers);
if (args_has(args, 'u')) if (args_has(args, 'u'))
window_copy_pageup(wp, 0); window_copy_pageup(wp, 0);
if (args_has(args, 'd')) if (args_has(args, 'd'))
window_copy_pagedown(wp, 0, args_has(args, 'e')); window_copy_pagedown(wp, 0, args_has(args, 'e'));
if (args_has(args, 'S')) { if (args_has(args, 'S')) {
tty_window_offset(&c->tty, &tty_ox, &tty_oy, &tty_sx, &tty_sy);
window_copy_scroll(wp, c->tty.mouse_slider_mpos, event->m.y, window_copy_scroll(wp, c->tty.mouse_slider_mpos, event->m.y,
args_has(args, 'e')); tty_oy, args_has(args, 'e'));
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -59,6 +59,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct cmd_find_state *source = cmdq_get_source(item); struct cmd_find_state *source = cmdq_get_source(item);
struct client *c = cmdq_get_client(item);
struct client *tc = cmdq_get_target_client(item), *loop; struct client *tc = cmdq_get_target_client(item), *loop;
struct session *s; struct session *s;
enum msgtype msgtype; enum msgtype msgtype;
@@ -69,6 +70,13 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (c->flags & CLIENT_READONLY) {
if (args_has(args, 's') || args_has(args, 'a') || c != tc) {
cmdq_error(item, "client is read-only");
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'P')) if (args_has(args, 'P'))
msgtype = MSG_DETACHKILL; msgtype = MSG_DETACHKILL;
else else

View File

@@ -294,7 +294,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
const char *border_style = args_get(args, 'S'); const char *border_style = args_get(args, 'S');
const char *selected_style = args_get(args, 'H'); const char *selected_style = args_get(args, 'H');
enum box_lines lines = BOX_LINES_DEFAULT; enum box_lines lines = BOX_LINES_DEFAULT;
char *title, *cause; char *title, *cause = NULL;
int flags = 0, starting_choice = 0; int flags = 0, starting_choice = 0;
u_int px, py, i, count = args_count(args); u_int px, py, i, count = args_count(args);
struct options *o = target->s->curw->window->options; struct options *o = target->s->curw->window->options;
@@ -312,8 +312,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
&cause); &cause);
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "starting choice %s", cause); cmdq_error(item, "starting choice %s", cause);
free(cause); goto fail;
return (CMD_RETURN_ERROR);
} }
} }
} }
@@ -334,8 +333,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
if (count - i < 2) { if (count - i < 2) {
cmdq_error(item, "not enough arguments"); cmdq_error(item, "not enough arguments");
menu_free(menu); goto fail;
return (CMD_RETURN_ERROR);
} }
key = args_string(args, i++); key = args_string(args, i++);
@@ -347,17 +345,13 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
} }
if (menu == NULL) { if (menu == NULL) {
cmdq_error(item, "invalid menu arguments"); cmdq_error(item, "invalid menu arguments");
return (CMD_RETURN_ERROR); goto fail;
}
if (menu->count == 0) {
menu_free(menu);
return (CMD_RETURN_NORMAL);
} }
if (menu->count == 0)
goto out;
if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, menu->width + 4, if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, menu->width + 4,
menu->count + 2)) { menu->count + 2))
menu_free(menu); goto out;
return (CMD_RETURN_NORMAL);
}
value = args_get(args, 'b'); value = args_get(args, 'b');
if (value != NULL) { if (value != NULL) {
@@ -366,8 +360,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
&cause); &cause);
if (lines == -1) { if (lines == -1) {
cmdq_error(item, "menu-border-lines %s", cause); cmdq_error(item, "menu-border-lines %s", cause);
free(cause); goto fail;
return (CMD_RETURN_ERROR);
} }
} }
@@ -377,8 +370,17 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
flags |= MENU_NOMOUSE; flags |= MENU_NOMOUSE;
if (menu_display(menu, flags, starting_choice, item, px, py, tc, lines, if (menu_display(menu, flags, starting_choice, item, px, py, tc, lines,
style, selected_style, border_style, target, NULL, NULL) != 0) style, selected_style, border_style, target, NULL, NULL) != 0)
return (CMD_RETURN_NORMAL); goto out;
return (CMD_RETURN_WAIT); return (CMD_RETURN_WAIT);
out:
menu_free(menu);
return (CMD_RETURN_NORMAL);
fail:
free(cause);
menu_free(menu);
return (CMD_RETURN_ERROR);
} }
static enum cmd_retval static enum cmd_retval
@@ -393,7 +395,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
const char *style = args_get(args, 's'); const char *style = args_get(args, 's');
const char *border_style = args_get(args, 'S'); const char *border_style = args_get(args, 'S');
char *cwd = NULL, *cause = NULL, **argv = NULL; char *cwd = NULL, *cause = NULL, **argv = NULL;
char *title; char *title = NULL;
int modify = popup_present(tc); int modify = popup_present(tc);
int flags = -1, argc = 0; int flags = -1, argc = 0;
enum box_lines lines = BOX_LINES_DEFAULT; enum box_lines lines = BOX_LINES_DEFAULT;
@@ -417,8 +419,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
&cause); &cause);
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "height %s", cause); cmdq_error(item, "height %s", cause);
free(cause); goto fail;
return (CMD_RETURN_ERROR);
} }
} }
@@ -428,8 +429,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
&cause); &cause);
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "width %s", cause); cmdq_error(item, "width %s", cause);
free(cause); goto fail;
return (CMD_RETURN_ERROR);
} }
} }
@@ -438,7 +438,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
if (h > tty->sy) if (h > tty->sy)
h = tty->sy; h = tty->sy;
if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, w, h)) if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, w, h))
return (CMD_RETURN_NORMAL); goto out;
value = args_get(args, 'd'); value = args_get(args, 'd');
if (value != NULL) if (value != NULL)
@@ -478,8 +478,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
&cause); &cause);
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "popup-border-lines %s", cause); cmdq_error(item, "popup-border-lines %s", cause);
free(cause); goto fail;
return (CMD_RETURN_ERROR);
} }
} }
@@ -507,22 +506,29 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
if (modify) { if (modify) {
popup_modify(tc, title, style, border_style, lines, flags); popup_modify(tc, title, style, border_style, lines, flags);
free(title); goto out;
return (CMD_RETURN_NORMAL);
} }
if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc, if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc,
argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0) { argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0)
cmd_free_argv(argc, argv); goto out;
if (env != NULL) environ_free(env);
environ_free(env);
free(cwd);
free(title);
return (CMD_RETURN_NORMAL);
}
if (env != NULL)
environ_free(env);
free(cwd); free(cwd);
free(title); free(title);
cmd_free_argv(argc, argv); cmd_free_argv(argc, argv);
return (CMD_RETURN_WAIT); return (CMD_RETURN_WAIT);
out:
cmd_free_argv(argc, argv);
environ_free(env);
free(cwd);
free(title);
return (CMD_RETURN_NORMAL);
fail:
free(cause);
cmd_free_argv(argc, argv);
environ_free(env);
free(cwd);
free(title);
return (CMD_RETURN_ERROR);
} }

View File

@@ -131,6 +131,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'a')) { if (args_has(args, 'a')) {
format_each(ft, cmd_display_message_each, item); format_each(ft, cmd_display_message_each, item);
format_free(ft);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -155,6 +156,5 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
free(msg); free(msg);
format_free(ft); format_free(ft);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -55,6 +55,27 @@ cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx,
return (ARGS_PARSE_COMMANDS_OR_STRING); return (ARGS_PARSE_COMMANDS_OR_STRING);
} }
static void
cmd_display_panes_put(struct screen_redraw_ctx *ctx,
struct window_pane *wp, u_int cx, u_int cy, const char *buf, size_t len)
{
struct client *c = ctx->c;
struct tty *tty = &c->tty;
struct visible_ranges *r;
struct visible_range *ri;
u_int i, j;
r = screen_redraw_get_visible_ranges(wp, ctx->ox + cx, ctx->oy + cy,
len, NULL);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
for (j = ri->px; j < ri->px + ri->nx; j++) {
tty_cursor(tty, j - ctx->ox, cy);
tty_putn(tty, &buf[j - ri->px], 1, 1);
}
}
}
static void static void
cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
struct window_pane *wp) struct window_pane *wp)
@@ -66,14 +87,15 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
struct window *w = wp->window; struct window *w = wp->window;
struct grid_cell fgc, bgc; struct grid_cell fgc, bgc;
u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy; u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy;
u_int cx, cy;
int colour, active_colour; int colour, active_colour;
char buf[16], lbuf[16], rbuf[16], *ptr; char buf[16], lbuf[16], rbuf[16], *ptr;
size_t len, llen, rlen; size_t len, llen, rlen;
if (wp->xoff + wp->sx <= ctx->ox || if (wp->xoff + (int)wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + ctx->sx || wp->xoff >= ctx->ox + (int)ctx->sx ||
wp->yoff + wp->sy <= ctx->oy || wp->yoff + (int)wp->sy <= ctx->oy ||
wp->yoff >= ctx->oy + ctx->sy) wp->yoff >= ctx->oy + (int)ctx->sy)
return; return;
if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) { if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
@@ -147,13 +169,17 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL); tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (sx >= len + llen + 1) { if (sx >= len + llen + 1) {
len += llen + 1; len += llen + 1;
tty_cursor(tty, xoff + px - len / 2, yoff + py); cx = xoff + px - len / 2;
tty_putn(tty, buf, len, len); cy = yoff + py;
tty_putn(tty, " ", 1, 1); cmd_display_panes_put(ctx, wp, cx, cy, buf, len);
tty_putn(tty, lbuf, llen, llen); cx += len;
cmd_display_panes_put(ctx, wp, cx, cy, " ", 1);
cx++;
cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen);
} else { } else {
tty_cursor(tty, xoff + px - len / 2, yoff + py); cx = xoff + px - len / 2;
tty_putn(tty, buf, len, len); cy = yoff + py;
cmd_display_panes_put(ctx, wp, cx, cy, buf, len);
} }
goto out; goto out;
} }
@@ -169,9 +195,11 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
for (j = 0; j < 5; j++) { for (j = 0; j < 5; j++) {
for (i = px; i < px + 5; i++) { for (i = px; i < px + 5; i++) {
tty_cursor(tty, xoff + i, yoff + py + j); if (!window_clock_table[idx][j][i - px])
if (window_clock_table[idx][j][i - px]) continue;
tty_putc(tty, ' '); cx = xoff + i;
cy = yoff + py + j;
cmd_display_panes_put(ctx, wp, cx, cy, " ", 1);
} }
} }
px += 6; px += 6;
@@ -181,13 +209,14 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
goto out; goto out;
tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL); tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (rlen != 0 && sx >= rlen) { if (rlen != 0 && sx >= rlen) {
tty_cursor(tty, xoff + sx - rlen, yoff); cx = xoff + sx - rlen;
tty_putn(tty, rbuf, rlen, rlen); cy = yoff;
cmd_display_panes_put(ctx, wp, cx, cy, rbuf, rlen);
} }
if (llen != 0) { if (llen != 0) {
tty_cursor(tty, xoff + sx / 2 + len * 3 - llen - 1, cx = xoff + sx / 2 + len * 3 - llen - 1;
yoff + py + 5); cy = yoff + py + 5;
tty_putn(tty, lbuf, llen, llen); cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen);
} }
out: out:

View File

@@ -924,6 +924,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
const char *target, enum cmd_find_type type, int flags) const char *target, enum cmd_find_type type, int flags)
{ {
struct mouse_event *m; struct mouse_event *m;
struct client *c;
struct cmd_find_state current; struct cmd_find_state current;
char *colon, *period, *copy = NULL, tmp[256]; char *colon, *period, *copy = NULL, tmp[256];
const char *session, *window, *pane, *s; const char *session, *window, *pane, *s;
@@ -990,6 +991,20 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
if (target == NULL || *target == '\0') if (target == NULL || *target == '\0')
goto current; goto current;
if (strcmp(target, "@") == 0 ||
strcmp(target, "{active}") == 0 ||
strcmp(target, "{current}") == 0) {
c = cmdq_get_client(item);
if (c == NULL) {
cmdq_error(item, "no current client");
goto error;
}
fs->wl = c->session->curw;
fs->wp = c->session->curw->window->active;
fs->w = c->session->curw->window;
goto found;
}
/* Mouse target is a plain = or {mouse}. */ /* Mouse target is a plain = or {mouse}. */
if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) { if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
m = &cmdq_get_event(item)->m; m = &cmdq_get_event(item)->m;

View File

@@ -71,11 +71,8 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window *src_w, *dst_w; struct window *src_w, *dst_w;
struct window_pane *src_wp, *dst_wp; struct window_pane *src_wp, *dst_wp;
char *cause = NULL; char *cause = NULL;
int size, dst_idx; int flags = 0, dst_idx;
int flags;
enum layout_type type;
struct layout_cell *lc; struct layout_cell *lc;
u_int curval = 0;
dst_s = target->s; dst_s = target->s;
dst_wl = target->wl; dst_wl = target->wl;
@@ -94,66 +91,30 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
type = LAYOUT_TOPBOTTOM; lc = layout_get_tiled_cell(item, args, dst_w, dst_wp, flags, &cause);
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT;
/* If the 'p' flag is dropped then this bit can be moved into 'l'. */
if (args_has(args, 'l') || args_has(args, 'p')) {
if (args_has(args, 'f')) {
if (type == LAYOUT_TOPBOTTOM)
curval = dst_w->sy;
else
curval = dst_w->sx;
} else {
if (type == LAYOUT_TOPBOTTOM)
curval = dst_wp->sy;
else
curval = dst_wp->sx;
}
}
size = -1;
if (args_has(args, 'l')) {
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
item, &cause);
} else if (args_has(args, 'p')) {
size = args_strtonum_and_expand(args, 'l', 0, 100, item,
&cause);
if (cause == NULL)
size = curval * size / 100;
}
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "size %s", cause); cmdq_error(item, "size or position %s", cause);
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
flags = 0;
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
lc = layout_split_pane(dst_wp, type, size, flags);
if (lc == NULL) {
cmdq_error(item, "create pane failed: pane too small");
return (CMD_RETURN_ERROR);
}
layout_close_pane(src_wp); layout_close_pane(src_wp);
server_client_remove_pane(src_wp); server_client_remove_pane(src_wp);
window_lost_pane(src_w, src_wp); window_lost_pane(src_w, src_wp);
TAILQ_REMOVE(&src_w->panes, src_wp, entry); TAILQ_REMOVE(&src_w->panes, src_wp, entry);
TAILQ_REMOVE(&src_w->z_index, src_wp, zentry);
src_wp->window = dst_w; src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options); options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
if (flags & SPAWN_BEFORE) if (flags & SPAWN_BEFORE) {
TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry); TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry);
else TAILQ_INSERT_BEFORE(dst_wp, src_wp, zentry);
} else {
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
TAILQ_INSERT_AFTER(&dst_w->z_index, dst_wp, src_wp, zentry);
}
layout_assign_pane(lc, src_wp, 0); layout_assign_pane(lc, src_wp, 0);
colour_palette_from_option(&src_wp->palette, src_wp->options); colour_palette_from_option(&src_wp->palette, src_wp->options);
@@ -170,7 +131,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
} else } else
server_status_session(dst_s); server_status_session(dst_s);
if (window_count_panes(src_w) == 0) if (window_count_panes(src_w, 1) == 0)
server_kill_window(src_w, 1); server_kill_window(src_w, 1);
else else
notify_window("window-layout-changed", src_w); notify_window("window-layout-changed", src_w);

View File

@@ -62,6 +62,10 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (wp == NULL) {
cmdq_error(item, "no active pane to kill");
return (CMD_RETURN_ERROR);
}
server_kill_pane(wp); server_kill_pane(wp);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -33,8 +33,8 @@ const struct cmd_entry cmd_kill_session_entry = {
.name = "kill-session", .name = "kill-session",
.alias = NULL, .alias = NULL,
.args = { "aCt:", 0, 0, NULL }, .args = { "aCgt:", 0, 0, NULL },
.usage = "[-aC] " CMD_TARGET_SESSION_USAGE, .usage = "[-aCg] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 }, .target = { 't', CMD_FIND_SESSION, 0 },
@@ -48,6 +48,7 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s, *sloop, *stmp; struct session *s = target->s, *sloop, *stmp;
struct session_group *sg;
struct winlink *wl; struct winlink *wl;
if (args_has(args, 'C')) { if (args_has(args, 'C')) {
@@ -63,6 +64,12 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
session_destroy(sloop, 1, __func__); session_destroy(sloop, 1, __func__);
} }
} }
} else if (args_has(args, 'g') &&
(sg = session_group_contains(s)) != NULL) {
TAILQ_FOREACH_SAFE(sloop, &sg->sessions, gentry, stmp) {
server_destroy_session(sloop);
session_destroy(sloop, 1, __func__);
}
} else { } else {
server_destroy_session(s); server_destroy_session(s);
session_destroy(s, 1, __func__); session_destroy(s, 1, __func__);

View File

@@ -36,8 +36,8 @@ const struct cmd_entry cmd_list_buffers_entry = {
.name = "list-buffers", .name = "list-buffers",
.alias = "lsb", .alias = "lsb",
.args = { "F:f:", 0, 0, NULL }, .args = { "F:f:O:r", 0, 0, NULL },
.usage = "[-F format] [-f filter]", .usage = "[-F format] [-f filter] [-O order]",
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK,
.exec = cmd_list_buffers_exec .exec = cmd_list_buffers_exec
@@ -46,21 +46,30 @@ const struct cmd_entry cmd_list_buffers_entry = {
static enum cmd_retval static enum cmd_retval
cmd_list_buffers_exec(struct cmd *self, struct cmdq_item *item) cmd_list_buffers_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct paste_buffer *pb; struct paste_buffer **l;
struct format_tree *ft; struct format_tree *ft;
const char *template, *filter; const char *template, *filter;
char *line, *expanded; char *line, *expanded;
int flag; int flag;
u_int i, n;
struct sort_criteria sort_crit;
if ((template = args_get(args, 'F')) == NULL) if ((template = args_get(args, 'F')) == NULL)
template = LIST_BUFFERS_TEMPLATE; template = LIST_BUFFERS_TEMPLATE;
filter = args_get(args, 'f'); filter = args_get(args, 'f');
pb = NULL; sort_crit.order = sort_order_from_string(args_get(args, 'O'));
while ((pb = paste_walk(pb)) != NULL) { if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
l = sort_get_buffers(&n, &sort_crit);
for (i = 0; i < n; i++) {
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults_paste_buffer(ft, pb); format_defaults_paste_buffer(ft, l[i]);
if (filter != NULL) { if (filter != NULL) {
expanded = format_expand(ft, filter); expanded = format_expand(ft, filter);

View File

@@ -41,8 +41,8 @@ const struct cmd_entry cmd_list_clients_entry = {
.name = "list-clients", .name = "list-clients",
.alias = "lsc", .alias = "lsc",
.args = { "F:f:t:", 0, 0, NULL }, .args = { "F:f:O:rt:", 0, 0, NULL },
.usage = "[-F format] [-f filter] " CMD_TARGET_SESSION_USAGE, .usage = "[-F format] [-f filter] [-O order]" CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 }, .target = { 't', CMD_FIND_SESSION, 0 },
@@ -53,15 +53,16 @@ const struct cmd_entry cmd_list_clients_entry = {
static enum cmd_retval static enum cmd_retval
cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item) cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct client *c; struct client **l;
struct session *s; struct session *s;
struct format_tree *ft; struct format_tree *ft;
const char *template, *filter; const char *template, *filter;
u_int idx; u_int i, n;
char *line, *expanded; char *line, *expanded;
int flag; int flag;
struct sort_criteria sort_crit;
if (args_has(args, 't')) if (args_has(args, 't'))
s = target->s; s = target->s;
@@ -72,14 +73,21 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
template = LIST_CLIENTS_TEMPLATE; template = LIST_CLIENTS_TEMPLATE;
filter = args_get(args, 'f'); filter = args_get(args, 'f');
idx = 0; sort_crit.order = sort_order_from_string(args_get(args, 'O'));
TAILQ_FOREACH(c, &clients, entry) { if (sort_crit.order == SORT_END && args_has(args, 'O')) {
if (c->session == NULL || (s != NULL && s != c->session)) cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
l = sort_get_clients(&n, &sort_crit);
for (i = 0; i < n; i++) {
if (l[i]->session == NULL || (s != NULL && s != l[i]->session))
continue; continue;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", idx); format_add(ft, "line", "%u", i);
format_defaults(ft, c, NULL, NULL, NULL); format_defaults(ft, l[i], NULL, NULL, NULL);
if (filter != NULL) { if (filter != NULL) {
expanded = format_expand(ft, filter); expanded = format_expand(ft, filter);
@@ -94,8 +102,6 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
} }
format_free(ft); format_free(ft);
idx++;
} }
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

107
cmd-list-commands.c Normal file
View File

@@ -0,0 +1,107 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* List all commands.
*/
#define LIST_COMMANDS_TEMPLATE \
"#{command_list_name}" \
"#{?command_list_alias, (#{command_list_alias}),} " \
"#{command_list_usage}"
static enum cmd_retval cmd_list_commands(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_list_commands_entry = {
.name = "list-commands",
.alias = "lscm",
.args = { "F:", 0, 1, NULL },
.usage = "[-F format] [command]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_commands
};
static void
cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
const char *template, struct cmdq_item *item)
{
const char *s;
char *line;
format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL)
s = entry->alias;
else
s = "";
format_add(ft, "command_list_alias", "%s", s);
if (entry->usage != NULL)
s = entry->usage;
else
s = "";
format_add(ft, "command_list_usage", "%s", s);
line = format_expand(ft, template);
if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
}
static enum cmd_retval
cmd_list_commands(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
const struct cmd_entry **entryp;
const struct cmd_entry *entry;
struct format_tree *ft;
const char *template, *command;
char *cause;
if ((template = args_get(args, 'F')) == NULL)
template = LIST_COMMANDS_TEMPLATE;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
command = args_string(args, 0);
if (command == NULL) {
for (entryp = cmd_table; *entryp != NULL; entryp++)
cmd_list_single_command(*entryp, ft, template, item);
} else {
entry = cmd_find(command, &cause);
if (entry != NULL)
cmd_list_single_command(entry, ft, template, item);
else {
cmdq_error(item, "%s", cause);
free(cause);
format_free(ft);
return (CMD_RETURN_ERROR);
}
}
format_free(ft);
return (CMD_RETURN_NORMAL);
}

View File

@@ -27,121 +27,140 @@
* List key bindings. * List key bindings.
*/ */
static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *); #define LIST_KEYS_TEMPLATE \
"#{?notes_only," \
"#{key_prefix} " \
"#{p|#{key_string_width}:key_string} " \
"#{?key_note,#{key_note},#{key_command}}" \
"," \
"bind-key #{?key_has_repeat,#{?key_repeat,-r, },} " \
"-T #{p|#{key_table_width}:key_table} " \
"#{p|#{key_string_width}:#{q|a:key_string}} " \
"#{key_command}}"
static enum cmd_retval cmd_list_keys_commands(struct cmd *, static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
struct cmdq_item *);
const struct cmd_entry cmd_list_keys_entry = { const struct cmd_entry cmd_list_keys_entry = {
.name = "list-keys", .name = "list-keys",
.alias = "lsk", .alias = "lsk",
.args = { "1aNP:T:", 0, 1, NULL }, .args = { "1aF:NO:P:rT:", 0, 1, NULL },
.usage = "[-1aN] [-P prefix-string] [-T key-table] [key]", .usage = "[-1aNr] [-F format] [-O order] [-P prefix-string]"
"[-T key-table] [key]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK, .flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec .exec = cmd_list_keys_exec
}; };
const struct cmd_entry cmd_list_commands_entry = { static char *
.name = "list-commands", cmd_list_keys_get_prefix(struct args *args)
.alias = "lscm", {
key_code prefix;
.args = { "F:", 0, 1, NULL }, if (args_has(args, 'P'))
.usage = "[-F format] [command]", return (xstrdup(args_get(args, 'P')));
.flags = CMD_STARTSERVER|CMD_AFTERHOOK, prefix = options_get_number(global_s_options, "prefix");
.exec = cmd_list_keys_exec if (prefix == KEYC_NONE)
}; return (xstrdup(""));
return (xstrdup(key_string_lookup_key(prefix, 0)));
}
static u_int static u_int
cmd_list_keys_get_width(const char *tablename, key_code only) cmd_list_keys_get_width(struct key_binding **l, u_int n)
{ {
struct key_table *table; u_int i, width, keywidth = 0;
struct key_binding *bd;
u_int width, keywidth = 0;
table = key_bindings_get_table(tablename, 0); for (i = 0; i < n; i++) {
if (table == NULL) width = utf8_cstrwidth(key_string_lookup_key(l[i]->key, 0));
return (0);
bd = key_bindings_first(table);
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
bd->note == NULL ||
*bd->note == '\0') {
bd = key_bindings_next(table, bd);
continue;
}
width = utf8_cstrwidth(key_string_lookup_key(bd->key, 0));
if (width > keywidth) if (width > keywidth)
keywidth = width; keywidth = width;
bd = key_bindings_next(table, bd);
} }
return (keywidth); return (keywidth);
} }
static int static u_int
cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, cmd_list_keys_get_table_width(struct key_binding **l, u_int n)
const char *tablename, u_int keywidth, key_code only, const char *prefix)
{ {
struct client *tc = cmdq_get_target_client(item); u_int i, width, tablewidth = 0;
struct key_table *table;
struct key_binding *bd;
const char *key;
char *tmp, *note;
int found = 0;
table = key_bindings_get_table(tablename, 0); for (i = 0; i < n; i++) {
if (table == NULL) width = utf8_cstrwidth(l[i]->tablename);
return (0); if (width > tablewidth)
bd = key_bindings_first(table); tablewidth = width;
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
((bd->note == NULL || *bd->note == '\0') &&
!args_has(args, 'a'))) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = key_string_lookup_key(bd->key, 0);
if (bd->note == NULL || *bd->note == '\0')
note = cmd_list_print(bd->cmdlist, 1);
else
note = xstrdup(bd->note);
tmp = utf8_padcstr(key, keywidth + 1);
if (args_has(args, '1') && tc != NULL) {
status_message_set(tc, -1, 1, 0, 0, "%s%s%s", prefix,
tmp, note);
} else
cmdq_print(item, "%s%s%s", prefix, tmp, note);
free(tmp);
free(note);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd);
} }
return (found); return (tablewidth);
} }
static char * static struct key_binding **
cmd_list_keys_get_prefix(struct args *args, key_code *prefix) cmd_list_keys_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit)
{
const char *tables[] = { "prefix", "root" };
struct key_table *t;
struct key_binding **lt;
u_int i, ltsz, len = 0, offset = 0;
static struct key_binding **l = NULL;
static u_int lsz = 0;
for (i = 0; i < nitems(tables); i++) {
t = key_bindings_get_table(tables[i], 0);
lt = sort_get_key_bindings_table(t, &ltsz, sort_crit);
len += ltsz;
if (lsz <= len) {
lsz = len + 100;
l = xreallocarray(l, lsz, sizeof *l);
}
memcpy(l + offset, lt, ltsz * sizeof *l);
offset += ltsz;
}
*n = len;
return (l);
}
static void
cmd_list_keys_filter_key_list(int filter_notes, int filter_key, key_code only,
struct key_binding **l, u_int *n)
{
key_code key;
u_int i, j = 0;
for (i = 0; i < *n; i++) {
key = l[i]->key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
if (filter_key && only != key)
continue;
if (filter_notes && l[i]->note == NULL)
continue;
l[j++] = l[i];
}
*n = j;
}
static void
cmd_list_keys_format_add_key_binding(struct format_tree *ft,
const struct key_binding *bd, const char *prefix)
{ {
char *s; char *s;
*prefix = options_get_number(global_s_options, "prefix"); if (bd->flags & KEY_BINDING_REPEAT)
if (!args_has(args, 'P')) { format_add(ft, "key_repeat", "1");
if (*prefix != KEYC_NONE) else
xasprintf(&s, "%s ", key_string_lookup_key(*prefix, 0)); format_add(ft, "key_repeat", "0");
else
s = xstrdup(""); if (bd->note != NULL)
} else format_add(ft, "key_note", "%s", bd->note);
s = xstrdup(args_get(args, 'P')); else
return (s); format_add(ft, "key_note", "%s", "");
format_add(ft, "key_prefix", "%s", prefix);
format_add(ft, "key_table", "%s", bd->tablename);
format_add(ft, "key_string", "%s", key_string_lookup_key(bd->key, 0));
s = cmd_list_print(bd->cmdlist, CMD_LIST_PRINT_ESCAPED|
CMD_LIST_PRINT_NO_GROUPS);
format_add(ft, "key_command", "%s", s);
free(s);
} }
static enum cmd_retval static enum cmd_retval
@@ -149,16 +168,16 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct key_table *table; struct format_tree *ft;
struct key_binding *bd; struct key_table *table = NULL;
const char *tablename, *r, *keystr; struct key_binding **l;
char *key, *cp, *tmp, *start, *empty; key_code only = KEYC_UNKNOWN;
key_code prefix, only = KEYC_UNKNOWN; const char *template, *tablename, *keystr;
int repeat, width, tablewidth, keywidth, found = 0; char *line;
size_t tmpsize, tmpused, cplen; char *prefix = NULL;
u_int i, n;
if (cmd_get_entry(self) == &cmd_list_commands_entry) int single, notes_only, filter_notes, filter_key;
return (cmd_list_keys_commands(self, item)); struct sort_criteria sort_crit;
if ((keystr = args_string(args, 0)) != NULL) { if ((keystr = args_string(args, 0)) != NULL) {
only = key_string_lookup_string(keystr); only = key_string_lookup_string(keystr);
@@ -169,218 +188,66 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS); only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
} }
tablename = args_get(args, 'T'); sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) { if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "table %s doesn't exist", tablename); cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
sort_crit.reversed = args_has(args, 'r');
if (args_has(args, 'N')) { if ((tablename = args_get(args, 'T')) != NULL) {
if (tablename == NULL) { table = key_bindings_get_table(tablename, 0);
start = cmd_list_keys_get_prefix(args, &prefix); if (table == NULL) {
keywidth = cmd_list_keys_get_width("root", only); cmdq_error(item, "table %s doesn't exist", tablename);
if (prefix != KEYC_NONE) {
width = cmd_list_keys_get_width("prefix", only);
if (width == 0)
prefix = KEYC_NONE;
else if (width > keywidth)
keywidth = width;
}
empty = utf8_padcstr("", utf8_cstrwidth(start));
found = cmd_list_keys_print_notes(item, args, "root",
keywidth, only, empty);
if (prefix != KEYC_NONE) {
if (cmd_list_keys_print_notes(item, args,
"prefix", keywidth, only, start))
found = 1;
}
free(empty);
} else {
if (args_has(args, 'P'))
start = xstrdup(args_get(args, 'P'));
else
start = xstrdup("");
keywidth = cmd_list_keys_get_width(tablename, only);
found = cmd_list_keys_print_notes(item, args, tablename,
keywidth, only, start);
}
free(start);
goto out;
}
repeat = 0;
tablewidth = keywidth = 0;
table = key_bindings_first_table();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
table = key_bindings_next_table(table);
continue;
}
bd = key_bindings_first(table);
while (bd != NULL) {
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
key = args_escape(key_string_lookup_key(bd->key, 0));
if (bd->flags & KEY_BINDING_REPEAT)
repeat = 1;
width = utf8_cstrwidth(table->name);
if (width > tablewidth)
tablewidth = width;
width = utf8_cstrwidth(key);
if (width > keywidth)
keywidth = width;
free(key);
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
tmpsize = 256;
tmp = xmalloc(tmpsize);
table = key_bindings_first_table();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
table = key_bindings_next_table(table);
continue;
}
bd = key_bindings_first(table);
while (bd != NULL) {
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = args_escape(key_string_lookup_key(bd->key, 0));
if (!repeat)
r = "";
else if (bd->flags & KEY_BINDING_REPEAT)
r = "-r ";
else
r = " ";
tmpused = xsnprintf(tmp, tmpsize, "%s-T ", r);
cp = utf8_padcstr(table->name, tablewidth);
cplen = strlen(cp) + 1;
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp);
cp = utf8_padcstr(key, keywidth);
cplen = strlen(cp) + 1;
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp);
cp = cmd_list_print(bd->cmdlist, 1);
cplen = strlen(cp);
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
free(cp);
if (args_has(args, '1') && tc != NULL) {
status_message_set(tc, -1, 1, 0, 0,
"bind-key %s", tmp);
} else
cmdq_print(item, "bind-key %s", tmp);
free(key);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
free(tmp);
out:
if (only != KEYC_UNKNOWN && !found) {
cmdq_error(item, "unknown key: %s", args_string(args, 0));
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_NORMAL);
}
static void
cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
const char *template, struct cmdq_item *item)
{
const char *s;
char *line;
format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL)
s = entry->alias;
else
s = "";
format_add(ft, "command_list_alias", "%s", s);
if (entry->usage != NULL)
s = entry->usage;
else
s = "";
format_add(ft, "command_list_usage", "%s", s);
line = format_expand(ft, template);
if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
}
static enum cmd_retval
cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
const struct cmd_entry **entryp;
const struct cmd_entry *entry;
struct format_tree *ft;
const char *template, *command;
char *cause;
if ((template = args_get(args, 'F')) == NULL) {
template = "#{command_list_name}"
"#{?command_list_alias, (#{command_list_alias}),} "
"#{command_list_usage}";
}
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
command = args_string(args, 0);
if (command == NULL) {
for (entryp = cmd_table; *entryp != NULL; entryp++)
cmd_list_single_command(*entryp, ft, template, item);
} else {
entry = cmd_find(command, &cause);
if (entry != NULL)
cmd_list_single_command(entry, ft, template, item);
else {
cmdq_error(item, "%s", cause);
free(cause);
format_free(ft);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
} }
prefix = cmd_list_keys_get_prefix(args);
single = args_has(args, '1');
notes_only = args_has(args, 'N');
if ((template = args_get(args, 'F')) == NULL)
template = LIST_KEYS_TEMPLATE;
if (table)
l = sort_get_key_bindings_table(table, &n, &sort_crit);
else if (notes_only)
l = cmd_list_keys_get_root_and_prefix(&n, &sort_crit);
else
l = sort_get_key_bindings(&n, &sort_crit);
filter_notes = notes_only && !args_has(args, 'a');
filter_key = only != KEYC_UNKNOWN;
if (filter_notes || filter_key) {
cmd_list_keys_filter_key_list(filter_notes, filter_key, only, l,
&n);
}
if (single)
n = 1;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
format_add(ft, "notes_only", "%d", notes_only);
format_add(ft, "key_has_repeat", "%d", key_bindings_has_repeat(l, n));
format_add(ft, "key_string_width", "%u", cmd_list_keys_get_width(l, n));
format_add(ft, "key_table_width", "%u",
cmd_list_keys_get_table_width(l, n));
for (i = 0; i < n; i++) {
cmd_list_keys_format_add_key_binding(ft, l[i], prefix);
line = format_expand(ft, template);
if ((single && tc != NULL) || n == 1)
status_message_set(tc, -1, 1, 0, 0, "%s", line);
else if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
if (single)
break;
}
format_free(ft); format_free(ft);
free(prefix);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -38,8 +38,9 @@ const struct cmd_entry cmd_list_panes_entry = {
.name = "list-panes", .name = "list-panes",
.alias = "lsp", .alias = "lsp",
.args = { "asF:f:t:", 0, 0, NULL }, .args = { "aF:f:O:rst:", 0, 0, NULL },
.usage = "[-as] [-F format] [-f filter] " CMD_TARGET_WINDOW_USAGE, .usage = "[-asr] [-F format] [-f filter] [-O order]"
CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 }, .target = { 't', CMD_FIND_WINDOW, 0 },
@@ -54,6 +55,13 @@ cmd_list_panes_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s; struct session *s = target->s;
struct winlink *wl = target->wl; struct winlink *wl = target->wl;
enum sort_order order;
order = sort_order_from_string(args_get(args, 'O'));
if (order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'a')) if (args_has(args, 'a'))
cmd_list_panes_server(self, item); cmd_list_panes_server(self, item);
@@ -89,34 +97,41 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
struct cmdq_item *item, int type) struct cmdq_item *item, int type)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct window_pane *wp; struct window_pane *wp, **l;
u_int n; u_int i, n;
struct format_tree *ft; struct format_tree *ft;
const char *template, *filter; const char *template, *filter;
char *line, *expanded; char *line, *expanded;
int flag; int flag;
struct sort_criteria sort_crit;
template = args_get(args, 'F'); template = args_get(args, 'F');
if (template == NULL) { if (template == NULL) {
switch (type) { switch (type) {
case 0: case 0:
template = "#{pane_index}: " template = "#{pane_index}: "
"[#{pane_width}x#{pane_height}] [history " "[#{pane_width}x#{pane_height}"
"#{?pane_floating_flag, "
"#{pane_x}#,#{pane_y}#,#{pane_z}}] [history "
"#{history_size}/#{history_limit}, " "#{history_size}/#{history_limit}, "
"#{history_bytes} bytes] #{pane_id}" "#{history_bytes} bytes] #{pane_id}"
"#{?pane_active, (active),}#{?pane_dead, (dead),}"; "#{?pane_active, (active),}#{?pane_dead, (dead),}";
break; break;
case 1: case 1:
template = "#{window_index}.#{pane_index}: " template = "#{window_index}.#{pane_index}: "
"[#{pane_width}x#{pane_height}] [history " "[#{pane_width}x#{pane_height}"
"#{?pane_floating_flag, "
"#{pane_x}#,#{pane_y}#,#{pane_z}}] [history "
"#{history_size}/#{history_limit}, " "#{history_size}/#{history_limit}, "
"#{history_bytes} bytes] #{pane_id}" "#{history_bytes} bytes] #{pane_id}"
"#{?pane_active, (active),}#{?pane_dead, (dead),}"; "#{?pane_active, (active),}#{?pane_dead, (dead),}";
break; break;
case 2: case 2:
template = "#{session_name}:#{window_index}." template = "#{session_name}:#{window_index}."
"#{pane_index}: [#{pane_width}x#{pane_height}] " "#{pane_index}: [#{pane_width}x#{pane_height}"
"[history #{history_size}/#{history_limit}, " "#{?pane_floating_flag, "
"#{pane_x}#,#{pane_y}#,#{pane_z}}] [history "
"#{history_size}/#{history_limit}, "
"#{history_bytes} bytes] #{pane_id}" "#{history_bytes} bytes] #{pane_id}"
"#{?pane_active, (active),}#{?pane_dead, (dead),}"; "#{?pane_active, (active),}#{?pane_dead, (dead),}";
break; break;
@@ -124,8 +139,12 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
} }
filter = args_get(args, 'f'); filter = args_get(args, 'f');
n = 0; sort_crit.order = sort_order_from_string(args_get(args, 'O'));
TAILQ_FOREACH(wp, &wl->window->panes, entry) { sort_crit.reversed = args_has(args, 'r');
l = sort_get_panes_window(wl->window, &n, &sort_crit);
for (i = 0; i < n; i++) {
wp = l[i];
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n); format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, wl, wp); format_defaults(ft, NULL, s, wl, wp);
@@ -143,6 +162,5 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
} }
format_free(ft); format_free(ft);
n++;
} }
} }

View File

@@ -42,8 +42,8 @@ const struct cmd_entry cmd_list_sessions_entry = {
.name = "list-sessions", .name = "list-sessions",
.alias = "ls", .alias = "ls",
.args = { "F:f:", 0, 0, NULL }, .args = { "F:f:O:r", 0, 0, NULL },
.usage = "[-F format] [-f filter]", .usage = "[-r] [-F format] [-f filter] [-O order]",
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK,
.exec = cmd_list_sessions_exec .exec = cmd_list_sessions_exec
@@ -52,23 +52,31 @@ const struct cmd_entry cmd_list_sessions_entry = {
static enum cmd_retval static enum cmd_retval
cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item) cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct session *s; struct session **l;
u_int n; u_int n, i;
struct format_tree *ft; struct format_tree *ft;
const char *template, *filter; const char *template, *filter;
char *line, *expanded; char *line, *expanded;
int flag; int flag;
struct sort_criteria sort_crit;
if ((template = args_get(args, 'F')) == NULL) if ((template = args_get(args, 'F')) == NULL)
template = LIST_SESSIONS_TEMPLATE; template = LIST_SESSIONS_TEMPLATE;
filter = args_get(args, 'f'); filter = args_get(args, 'f');
n = 0; sort_crit.order = sort_order_from_string(args_get(args, 'O'));
RB_FOREACH(s, sessions, &sessions) { if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
l = sort_get_sessions(&n, &sort_crit);
for (i = 0; i < n; i++) {
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n); format_add(ft, "line", "%u", i);
format_defaults(ft, NULL, s, NULL, NULL); format_defaults(ft, NULL, l[i], NULL, NULL);
if (filter != NULL) { if (filter != NULL) {
expanded = format_expand(ft, filter); expanded = format_expand(ft, filter);
@@ -83,7 +91,6 @@ cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
} }
format_free(ft); format_free(ft);
n++;
} }
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

@@ -41,16 +41,13 @@
static enum cmd_retval cmd_list_windows_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_list_windows_exec(struct cmd *, struct cmdq_item *);
static void cmd_list_windows_server(struct cmd *, struct cmdq_item *);
static void cmd_list_windows_session(struct cmd *, struct session *,
struct cmdq_item *, int);
const struct cmd_entry cmd_list_windows_entry = { const struct cmd_entry cmd_list_windows_entry = {
.name = "list-windows", .name = "list-windows",
.alias = "lsw", .alias = "lsw",
.args = { "F:f:at:", 0, 0, NULL }, .args = { "aF:f:O:rt:", 0, 0, NULL },
.usage = "[-a] [-F format] [-f filter] " CMD_TARGET_SESSION_USAGE, .usage = "[-ar] [-F format] [-f filter] [-O order]"
CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 }, .target = { 't', CMD_FIND_SESSION, 0 },
@@ -63,51 +60,38 @@ cmd_list_windows_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl, **l;
if (args_has(args, 'a')) struct session *s;
cmd_list_windows_server(self, item); u_int i, n;
else
cmd_list_windows_session(self, target->s, item, 0);
return (CMD_RETURN_NORMAL);
}
static void
cmd_list_windows_server(struct cmd *self, struct cmdq_item *item)
{
struct session *s;
RB_FOREACH(s, sessions, &sessions)
cmd_list_windows_session(self, s, item, 1);
}
static void
cmd_list_windows_session(struct cmd *self, struct session *s,
struct cmdq_item *item, int type)
{
struct args *args = cmd_get_args(self);
struct winlink *wl;
u_int n;
struct format_tree *ft; struct format_tree *ft;
const char *template, *filter; const char *template, *filter;
char *line, *expanded; char *line, *expanded;
int flag; int flag;
struct sort_criteria sort_crit;
template = args_get(args, 'F'); template = args_get(args, 'F');
if (template == NULL) {
switch (type) {
case 0:
template = LIST_WINDOWS_TEMPLATE;
break;
case 1:
template = LIST_WINDOWS_WITH_SESSION_TEMPLATE;
break;
}
}
filter = args_get(args, 'f'); filter = args_get(args, 'f');
n = 0; sort_crit.order = sort_order_from_string(args_get(args, 'O'));
RB_FOREACH(wl, winlinks, &s->windows) { if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
if (args_has(args, 'a')) {
l = sort_get_winlinks(&n, &sort_crit);
if (template == NULL)
template = LIST_WINDOWS_WITH_SESSION_TEMPLATE;
} else {
l = sort_get_winlinks_session(target->s, &n, &sort_crit);
if (template == NULL)
template = LIST_WINDOWS_TEMPLATE;
}
for (i = 0; i < n; i++) {
wl = l[i];
s = wl->session;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n); format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, wl, NULL); format_defaults(ft, NULL, s, wl, NULL);
@@ -125,6 +109,7 @@ cmd_list_windows_session(struct cmd *self, struct session *s,
} }
format_free(ft); format_free(ft);
n++;
} }
return (CMD_RETURN_NORMAL);
} }

View File

@@ -77,8 +77,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
struct termios tio, *tiop; struct termios tio, *tiop;
struct session_group *sg = NULL; struct session_group *sg = NULL;
const char *errstr, *template, *group, *tmp; const char *errstr, *template, *group, *tmp;
char *cause, *cwd = NULL, *cp, *newname = NULL; char *cause, *cwd = NULL, *cp, *ename;
char *name, *prefix = NULL; char *wname = NULL, *sname = NULL, *prefix = NULL;
int detached, already_attached, is_control = 0; int detached, already_attached, is_control = 0;
u_int sx, sy, dsx, dsy, count = args_count(args); u_int sx, sy, dsx, dsy, count = args_count(args);
struct spawn_context sc = { 0 }; struct spawn_context sc = { 0 };
@@ -99,32 +99,43 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
tmp = args_get(args, 's'); if ((tmp = args_get(args, 'n')) != NULL) {
if (tmp != NULL) { ename = format_single(item, tmp, c, NULL, NULL, NULL);
name = format_single(item, tmp, c, NULL, NULL, NULL); if (!check_name(ename)) {
newname = session_check_name(name); cmdq_error(item, "invalid window name: %s", ename);
if (newname == NULL) { free(ename);
cmdq_error(item, "invalid session: %s", name);
free(name);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
free(name); wname = clean_name(ename, 0);
free(ename);
}
if ((tmp = args_get(args, 's')) != NULL) {
ename = format_single(item, tmp, c, NULL, NULL, NULL);
if (!check_name(ename)) {
cmdq_error(item, "invalid session name: %s", ename);
free(ename);
goto fail;
}
sname = clean_name(ename, 0);
free(ename);
} }
if (args_has(args, 'A')) { if (args_has(args, 'A')) {
if (newname != NULL) if (sname != NULL)
as = session_find(newname); as = session_find(sname);
else else
as = target->s; as = target->s;
if (as != NULL) { if (as != NULL) {
retval = cmd_attach_session(item, as->name, retval = cmd_attach_session(item, as->name,
args_has(args, 'D'), args_has(args, 'X'), 0, NULL, args_has(args, 'D'), args_has(args, 'X'), 0,
args_has(args, 'E'), args_get(args, 'f')); args_get(args, 'c'), args_has(args, 'E'),
free(newname); args_get(args, 'f'));
free(wname);
free(sname);
return (retval); return (retval);
} }
} }
if (newname != NULL && session_find(newname) != NULL) { if (sname != NULL && session_find(sname) != NULL) {
cmdq_error(item, "duplicate session: %s", newname); cmdq_error(item, "duplicate session: %s", sname);
goto fail; goto fail;
} }
@@ -141,12 +152,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
else if (groupwith != NULL) else if (groupwith != NULL)
prefix = xstrdup(groupwith->name); prefix = xstrdup(groupwith->name);
else { else {
prefix = session_check_name(group); if (!check_name(group)) {
if (prefix == NULL) { cmdq_error(item,
cmdq_error(item, "invalid session group: %s", "invalid session group name: %s", group);
group);
goto fail; goto fail;
} }
prefix = clean_name(group, 0);
} }
} }
@@ -275,7 +286,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
environ_put(env, av->string, 0); environ_put(env, av->string, 0);
av = args_next_value(av); av = args_next_value(av);
} }
s = session_create(prefix, newname, cwd, env, oo, tiop); s = session_create(prefix, sname, cwd, env, oo, tiop);
/* Spawn the initial window. */ /* Spawn the initial window. */
sc.item = item; sc.item = item;
@@ -283,7 +294,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (!detached) if (!detached)
sc.tc = c; sc.tc = c;
sc.name = args_get(args, 'n'); sc.name = wname;
args_to_vector(args, &sc.argc, &sc.argv); args_to_vector(args, &sc.argc, &sc.argv);
sc.idx = -1; sc.idx = -1;
@@ -356,7 +367,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (sc.argv != NULL) if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv); cmd_free_argv(sc.argc, sc.argv);
free(cwd); free(cwd);
free(newname); free(wname);
free(sname);
free(prefix); free(prefix);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
@@ -364,7 +376,8 @@ fail:
if (sc.argv != NULL) if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv); cmd_free_argv(sc.argc, sc.argv);
free(cwd); free(cwd);
free(newname); free(wname);
free(sname);
free(prefix); free(prefix);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }

View File

@@ -61,7 +61,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = target->s; struct session *s = target->s;
struct winlink *wl = target->wl, *new_wl = NULL; struct winlink *wl = target->wl, *new_wl = NULL;
int idx = target->idx, before; int idx = target->idx, before;
char *cause = NULL, *cp, *expanded; char *cause = NULL, *cp, *expanded, *wname = NULL;
const char *template, *name; const char *template, *name;
struct cmd_find_state fs; struct cmd_find_state fs;
struct args_value *av; struct args_value *av;
@@ -71,8 +71,18 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
* name already exists, select it. * name already exists, select it.
*/ */
name = args_get(args, 'n'); name = args_get(args, 'n');
if (args_has(args, 'S') && name != NULL && target->idx == -1) { if (name != NULL) {
expanded = format_single(item, name, c, s, NULL, NULL); expanded = format_single(item, name, c, s, NULL, NULL);
if (!check_name(expanded)) {
cmdq_error(item, "invalid window name: %s", expanded);
free(expanded);
return (CMD_RETURN_ERROR);
}
wname = clean_name(expanded, 0);
free(expanded);
}
if (args_has(args, 'S') && wname != NULL && target->idx == -1) {
expanded = format_single(item, wname, c, s, NULL, NULL);
RB_FOREACH(wl, winlinks, &s->windows) { RB_FOREACH(wl, winlinks, &s->windows) {
if (strcmp(wl->window->name, expanded) != 0) if (strcmp(wl->window->name, expanded) != 0)
continue; continue;
@@ -80,12 +90,14 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
new_wl = wl; new_wl = wl;
continue; continue;
} }
cmdq_error(item, "multiple windows named %s", name); cmdq_error(item, "multiple windows named %s", wname);
free(wname);
free(expanded); free(expanded);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
free(expanded); free(expanded);
if (new_wl != NULL) { if (new_wl != NULL) {
free(wname);
if (args_has(args, 'd')) if (args_has(args, 'd'))
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
if (session_set_current(s, new_wl) == 0) if (session_set_current(s, new_wl) == 0)
@@ -108,7 +120,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
sc.s = s; sc.s = s;
sc.tc = tc; sc.tc = tc;
sc.name = args_get(args, 'n'); sc.name = wname;
args_to_vector(args, &sc.argc, &sc.argv); args_to_vector(args, &sc.argc, &sc.argv);
sc.environ = environ_create(); sc.environ = environ_create();
@@ -130,10 +142,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
if ((new_wl = spawn_window(&sc, &cause)) == NULL) { if ((new_wl = spawn_window(&sc, &cause)) == NULL) {
cmdq_error(item, "create window failed: %s", cause); cmdq_error(item, "create window failed: %s", cause);
free(cause); free(cause);
if (sc.argv != NULL) goto fail;
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
} }
if (!args_has(args, 'd') || new_wl == s->curw) { if (!args_has(args, 'd') || new_wl == s->curw) {
cmd_find_from_winlink(current, new_wl, 0); cmd_find_from_winlink(current, new_wl, 0);
@@ -156,5 +165,13 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
if (sc.argv != NULL) if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv); cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ); environ_free(sc.environ);
free(wname);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
fail:
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
free(wname);
return (CMD_RETURN_ERROR);
} }

View File

@@ -37,6 +37,8 @@ static void printflike(1,2) yyerror(const char *, ...);
static char *yylex_token(int); static char *yylex_token(int);
static char *yylex_format(void); static char *yylex_format(void);
#define CMD_PARSE_MAX_ENVIRON_LEN 16384
struct cmd_parse_scope { struct cmd_parse_scope {
int flag; int flag;
TAILQ_ENTRY (cmd_parse_scope) entry; TAILQ_ENTRY (cmd_parse_scope) entry;
@@ -232,6 +234,10 @@ assignment : EQUALS
flag = flag && scope->flag; flag = flag && scope->flag;
} }
if (strlen($1) > CMD_PARSE_MAX_ENVIRON_LEN) {
yyerror("environment variable is too long");
YYABORT;
}
if ((~flags & CMD_PARSE_PARSEONLY) && flag) if ((~flags & CMD_PARSE_PARSEONLY) && flag)
environ_put(global_environ, $1, 0); environ_put(global_environ, $1, 0);
free($1); free($1);
@@ -250,6 +256,10 @@ hidden_assignment : HIDDEN EQUALS
flag = flag && scope->flag; flag = flag && scope->flag;
} }
if (strlen($2) > CMD_PARSE_MAX_ENVIRON_LEN) {
yyerror("environment variable is too long");
YYABORT;
}
if ((~flags & CMD_PARSE_PARSEONLY) && flag) if ((~flags & CMD_PARSE_PARSEONLY) && flag)
environ_put(global_environ, $2, ENVIRON_HIDDEN); environ_put(global_environ, $2, ENVIRON_HIDDEN);
free($2); free($2);
@@ -758,7 +768,7 @@ static int
cmd_parse_expand_alias(struct cmd_parse_command *cmd, cmd_parse_expand_alias(struct cmd_parse_command *cmd,
struct cmd_parse_input *pi, struct cmd_parse_result *pr) struct cmd_parse_input *pi, struct cmd_parse_result *pr)
{ {
struct cmd_parse_argument *arg, *arg1, *first; struct cmd_parse_argument *first;
struct cmd_parse_commands *cmds; struct cmd_parse_commands *cmds;
struct cmd_parse_command *last; struct cmd_parse_command *last;
char *alias, *name, *cause; char *alias, *name, *cause;
@@ -798,10 +808,7 @@ cmd_parse_expand_alias(struct cmd_parse_command *cmd,
TAILQ_REMOVE(&cmd->arguments, first, entry); TAILQ_REMOVE(&cmd->arguments, first, entry);
cmd_parse_free_argument(first); cmd_parse_free_argument(first);
TAILQ_FOREACH_SAFE(arg, &cmd->arguments, entry, arg1) { TAILQ_CONCAT(&last->arguments, &cmd->arguments, entry);
TAILQ_REMOVE(&cmd->arguments, arg, entry);
TAILQ_INSERT_TAIL(&last->arguments, arg, entry);
}
cmd_parse_log_commands(cmds, __func__); cmd_parse_log_commands(cmds, __func__);
pi->flags |= CMD_PARSE_NOALIAS; pi->flags |= CMD_PARSE_NOALIAS;
@@ -1607,7 +1614,9 @@ yylex_token_tilde(char **buf, size_t *len)
if (*name == '\0') { if (*name == '\0') {
envent = environ_find(global_environ, "HOME"); envent = environ_find(global_environ, "HOME");
if (envent != NULL && *envent->value != '\0') if (envent != NULL &&
envent->value != NULL &&
*envent->value != '\0')
home = envent->value; home = envent->value;
else if ((pw = getpwuid(getuid())) != NULL) else if ((pw = getpwuid(getuid())) != NULL)
home = pw->pw_dir; home = pw->pw_dir;

View File

@@ -33,8 +33,8 @@ const struct cmd_entry cmd_paste_buffer_entry = {
.name = "paste-buffer", .name = "paste-buffer",
.alias = "pasteb", .alias = "pasteb",
.args = { "db:prs:t:", 0, 0, NULL }, .args = { "db:prSs:t:", 0, 0, NULL },
.usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " .usage = "[-dprS] [-s separator] " CMD_BUFFER_USAGE " "
CMD_TARGET_PANE_USAGE, CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -43,6 +43,17 @@ const struct cmd_entry cmd_paste_buffer_entry = {
.exec = cmd_paste_buffer_exec .exec = cmd_paste_buffer_exec
}; };
static void
cmd_paste_buffer_paste(struct window_pane *wp, const char *buf, size_t len)
{
char *cp;
size_t n;
n = utf8_stravisx(&cp, buf, len, VIS_SAFE|VIS_NOSLASH);
bufferevent_write(wp->event, cp, n);
free(cp);
}
static enum cmd_retval static enum cmd_retval
cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item) cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
{ {
@@ -51,7 +62,7 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
struct paste_buffer *pb; struct paste_buffer *pb;
const char *sepstr, *bufname, *bufdata, *bufend, *line; const char *sepstr, *bufname, *bufdata, *bufend, *line;
size_t seplen, bufsize; size_t seplen, bufsize, len;
int bracket = args_has(args, 'p'); int bracket = args_has(args, 'p');
if (window_pane_exited(wp)) { if (window_pane_exited(wp)) {
@@ -93,14 +104,22 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
line = memchr(bufdata, '\n', bufend - bufdata); line = memchr(bufdata, '\n', bufend - bufdata);
if (line == NULL) if (line == NULL)
break; break;
len = line - bufdata;
bufferevent_write(wp->event, bufdata, line - bufdata); if (args_has(args, 'S'))
bufferevent_write(wp->event, bufdata, len);
else
cmd_paste_buffer_paste(wp, bufdata, len);
bufferevent_write(wp->event, sepstr, seplen); bufferevent_write(wp->event, sepstr, seplen);
bufdata = line + 1; bufdata = line + 1;
} }
if (bufdata != bufend) if (bufdata != bufend) {
bufferevent_write(wp->event, bufdata, bufend - bufdata); len = bufend - bufdata;
if (args_has(args, 'S'))
bufferevent_write(wp->event, bufdata, len);
else
cmd_paste_buffer_paste(wp, bufdata, len);
}
if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) if (bracket && (wp->screen->mode & MODE_BRACKETPASTE))
bufferevent_write(wp->event, "\033[201~", 6); bufferevent_write(wp->event, "\033[201~", 6);

View File

@@ -123,11 +123,13 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
/* Fork the child. */ /* Fork the child. */
sigfillset(&set); sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset); sigprocmask(SIG_BLOCK, &set, &oldset);
switch (fork()) { switch ((wp->pipe_pid = fork())) {
case -1: case -1:
sigprocmask(SIG_SETMASK, &oldset, NULL); sigprocmask(SIG_SETMASK, &oldset, NULL);
cmdq_error(item, "fork error: %s", strerror(errno)); cmdq_error(item, "fork error: %s", strerror(errno));
close(pipe_fd[0]);
close(pipe_fd[1]);
free(cmd); free(cmd);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
case 0: case 0:
@@ -136,6 +138,9 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
sigprocmask(SIG_SETMASK, &oldset, NULL); sigprocmask(SIG_SETMASK, &oldset, NULL);
close(pipe_fd[0]); close(pipe_fd[0]);
if (setpgid(0, 0) == -1)
_exit(1);
null_fd = open(_PATH_DEVNULL, O_WRONLY); null_fd = open(_PATH_DEVNULL, O_WRONLY);
if (out) { if (out) {
if (dup2(pipe_fd[1], STDIN_FILENO) == -1) if (dup2(pipe_fd[1], STDIN_FILENO) == -1)

View File

@@ -34,7 +34,7 @@ const struct cmd_entry cmd_refresh_client_entry = {
.name = "refresh-client", .name = "refresh-client",
.alias = "refresh", .alias = "refresh",
.args = { "A:B:cC:Df:r:F:l::LRSt:U", 0, 1, NULL }, .args = { "A:B:cC:Df:r:F:lLRSt:U", 0, 1, NULL },
.usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] " .usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] "
"[-C XxY] [-f flags] [-r pane:report] " CMD_TARGET_CLIENT_USAGE "[-C XxY] [-f flags] [-r pane:report] " CMD_TARGET_CLIENT_USAGE
" [adjustment]", " [adjustment]",
@@ -163,37 +163,6 @@ out:
free(copy); free(copy);
} }
static enum cmd_retval
cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
const char *p;
u_int i;
struct cmd_find_state fs;
p = args_get(args, 'l');
if (p == NULL) {
if (tc->flags & CLIENT_CLIPBOARDBUFFER)
return (CMD_RETURN_NORMAL);
tc->flags |= CLIENT_CLIPBOARDBUFFER;
} else {
if (cmd_find_target(&fs, item, p, CMD_FIND_PANE, 0) != 0)
return (CMD_RETURN_ERROR);
for (i = 0; i < tc->clipboard_npanes; i++) {
if (tc->clipboard_panes[i] == fs.wp->id)
break;
}
if (i != tc->clipboard_npanes)
return (CMD_RETURN_NORMAL);
tc->clipboard_panes = xreallocarray(tc->clipboard_panes,
tc->clipboard_npanes + 1, sizeof *tc->clipboard_panes);
tc->clipboard_panes[tc->clipboard_npanes++] = fs.wp->id;
}
tty_clipboard_query(&tc->tty);
return (CMD_RETURN_NORMAL);
}
static void static void
cmd_refresh_report(struct tty *tty, const char *value) cmd_refresh_report(struct tty *tty, const char *value)
{ {
@@ -284,8 +253,10 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (args_has(args, 'l')) if (args_has(args, 'l')) {
return (cmd_refresh_client_clipboard(self, item)); tty_clipboard_query(&tc->tty);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'F')) /* -F is an alias for -f */ if (args_has(args, 'F')) /* -F is an alias for -f */
server_client_set_flags(tc, args_get(args, 'F')); server_client_set_flags(tc, args_get(args, 'F'));

View File

@@ -52,12 +52,12 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item)
char *newname, *tmp; char *newname, *tmp;
tmp = format_single_from_target(item, args_string(args, 0)); tmp = format_single_from_target(item, args_string(args, 0));
newname = session_check_name(tmp); if (!check_name(tmp)) {
if (newname == NULL) { cmdq_error(item, "invalid session name: %s", tmp);
cmdq_error(item, "invalid session: %s", tmp);
free(tmp); free(tmp);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
newname = clean_name(tmp, 0);
free(tmp); free(tmp);
if (strcmp(newname, s->name) == 0) { if (strcmp(newname, s->name) == 0) {
free(newname); free(newname);

View File

@@ -48,15 +48,21 @@ cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl; struct winlink *wl = target->wl;
char *newname; char *name;
newname = format_single_from_target(item, args_string(args, 0)); name = format_single_from_target(item, args_string(args, 0));
window_set_name(wl->window, newname); if (!check_name(name)) {
cmdq_error(item, "invalid window name: %s", name);
free(name);
return (CMD_RETURN_ERROR);
}
window_set_name(wl->window, name, 0);
options_set_number(wl->window->options, "automatic-rename", 0); options_set_number(wl->window->options, "automatic-rename", 0);
free(name);
server_redraw_window_borders(wl->window); server_redraw_window_borders(wl->window);
server_status_window(wl->window); server_status_window(wl->window);
free(newname);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -29,8 +29,12 @@
static enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmdq_item *);
static void cmd_resize_pane_mouse_update(struct client *, static enum cmd_retval cmd_resize_pane_mouse_update(struct cmd *,
struct mouse_event *); struct cmdq_item *);
static void cmd_resize_pane_mouse_update_floating(struct client *,
struct mouse_event *);
static void cmd_resize_pane_mouse_update_tiled(struct client *,
struct mouse_event *);
const struct cmd_entry cmd_resize_pane_entry = { const struct cmd_entry cmd_resize_pane_entry = {
.name = "resize-pane", .name = "resize-pane",
@@ -51,13 +55,10 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct key_event *event = cmdq_get_event(item);
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
struct winlink *wl = target->wl; struct winlink *wl = target->wl;
struct window *w = wl->window; struct window *w = wl->window;
struct client *c = cmdq_get_client(item); const char *errstr;
struct session *s = target->s;
const char *errstr;
char *cause; char *cause;
u_int adjust; u_int adjust;
int x, y, status; int x, y, status;
@@ -75,15 +76,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (args_has(args, 'M')) { if (args_has(args, 'M'))
if (!event->m.valid || cmd_mouse_window(&event->m, &s) == NULL) return (cmd_resize_pane_mouse_update(self, item));
return (CMD_RETURN_NORMAL);
if (c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update;
cmd_resize_pane_mouse_update(c, &event->m);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'Z')) { if (args_has(args, 'Z')) {
if (w->flags & WINDOW_ZOOMED) if (w->flags & WINDOW_ZOOMED)
@@ -148,13 +142,168 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
static enum cmd_retval
cmd_resize_pane_mouse_update(__unused struct cmd *self, struct cmdq_item *item)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct key_event *event = cmdq_get_event(item);
struct window_pane *wp = target->wp;
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct client *c = cmdq_get_client(item);
struct session *s = target->s;
if (!event->m.valid)
return (CMD_RETURN_NORMAL);
wp = cmd_mouse_pane(&event->m, &s, NULL);
if (wp == NULL || c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
if (!window_pane_is_floating(wp)) {
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update_tiled;
cmd_resize_pane_mouse_update_tiled(c, &event->m);
return (CMD_RETURN_NORMAL);
}
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update_floating;
cmd_resize_pane_mouse_update_floating(c, &event->m);
return (CMD_RETURN_NORMAL);
}
static void static void
cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m) cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m)
{
struct winlink *wl;
struct window *w;
struct window_pane *wp;
struct layout_cell *lc;
int y, ly, x, lx, sx, sy, new_sx, new_sy;
int left, right;
int new_xoff, new_yoff, resizes = 0;
wp = cmd_mouse_pane(m, NULL, &wl);
if (wp == NULL) {
c->tty.mouse_drag_update = NULL;
return;
}
w = wl->window;
lc = wp->layout_cell;
sx = wp->sx;
sy = wp->sy;
left = wp->xoff - 1;
right = wp->xoff + sx;
if (window_pane_show_scrollbar(wp) &&
w->sb_pos == PANE_SCROLLBARS_LEFT) {
left -= wp->scrollbar_style.width + wp->scrollbar_style.pad;
} else if (window_pane_show_scrollbar(wp) &&
w->sb_pos == PANE_SCROLLBARS_RIGHT) {
right += wp->scrollbar_style.width + wp->scrollbar_style.pad;
}
y = m->y + m->oy; x = m->x + m->ox;
if (m->statusat == 0 && y >= (int)m->statuslines)
y -= m->statuslines;
else if (m->statusat > 0 && y >= m->statusat)
y = m->statusat - 1;
ly = m->ly + m->oy; lx = m->lx + m->ox;
if (m->statusat == 0 && ly >= (int)m->statuslines)
ly -= m->statuslines;
else if (m->statusat > 0 && ly >= m->statusat)
ly = m->statusat - 1;
if ((lx == left || lx == left + 1) && ly == wp->yoff - 1) {
/* Top left corner. */
new_sx = lc->sx + (lx - x);
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = lc->sy + (ly - y);
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
new_xoff = x + 1; /* because mouse is on border at xoff - 1 */
new_yoff = y + 1;
layout_set_size(lc, new_sx, new_sy, new_xoff, new_yoff);
resizes++;
} else if ((lx == right + 1 || lx == right) &&
ly == wp->yoff - 1) {
/* Top right corner. */
new_sx = x - lc->xoff;
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = lc->sy + (ly - y);
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
new_yoff = y + 1;
layout_set_size(lc, new_sx, new_sy, lc->xoff, new_yoff);
resizes++;
} else if ((lx == left || lx == left + 1) &&
ly == wp->yoff + sy) {
/* Bottom left corner. */
new_sx = lc->sx + (lx - x);
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = y - lc->yoff;
if (new_sy < PANE_MINIMUM)
return;
new_xoff = x + 1;
layout_set_size(lc, new_sx, new_sy, new_xoff, lc->yoff);
resizes++;
} else if ((lx == right + 1 || lx == right) &&
ly == wp->yoff + sy) {
/* Bottom right corner. */
new_sx = x - lc->xoff;
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = y - lc->yoff;
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
layout_set_size(lc, new_sx, new_sy, lc->xoff, lc->yoff);
resizes++;
} else if (lx == right) {
/* Right border. */
new_sx = x - lc->xoff;
if (new_sx < PANE_MINIMUM)
return;
layout_set_size(lc, new_sx, lc->sy, lc->xoff, lc->yoff);
resizes++;
} else if (lx == left) {
/* Left border. */
new_sx = lc->sx + (lx - x);
if (new_sx < PANE_MINIMUM)
return;
new_xoff = x + 1;
layout_set_size(lc, new_sx, lc->sy, new_xoff, lc->yoff);
resizes++;
} else if (ly == wp->yoff + sy) {
/* Bottom border. */
new_sy = y - lc->yoff;
if (new_sy < PANE_MINIMUM)
return;
layout_set_size(lc, lc->sx, new_sy, lc->xoff, lc->yoff);
resizes++;
} else if (ly == wp->yoff - 1) {
/* Top border (move instead of resize). */
new_xoff = lc->xoff + (x - lx);
new_yoff = y + 1;
layout_set_size(lc, lc->sx, lc->sy, new_xoff, new_yoff);
resizes++;
}
if (resizes != 0) {
layout_fix_panes(w, NULL);
server_redraw_window(w);
server_redraw_window_borders(w);
}
}
static void
cmd_resize_pane_mouse_update_tiled(struct client *c, struct mouse_event *m)
{ {
struct winlink *wl; struct winlink *wl;
struct window *w; struct window *w;
u_int y, ly, x, lx; u_int y, ly, x, lx;
static const int offsets[][2] = { static const int offsets[][2] = {
{ 0, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 }, { 0, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 },
}; };
struct layout_cell *cells[nitems(offsets)], *lc; struct layout_cell *cells[nitems(offsets)], *lc;

View File

@@ -44,9 +44,9 @@ const struct cmd_entry cmd_run_shell_entry = {
.name = "run-shell", .name = "run-shell",
.alias = "run", .alias = "run",
.args = { "bd:Ct:Es:c:", 0, 1, cmd_run_shell_args_parse }, .args = { "bd:Ct:Es:c:", 0, -1, cmd_run_shell_args_parse },
.usage = "[-bCE] [-c start-directory] [-d delay] " CMD_TARGET_PANE_USAGE .usage = "[-bCE] [-c start-directory] [-d delay] " CMD_TARGET_PANE_USAGE
" [shell-command]", " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -115,9 +115,11 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = target->s; struct session *s = target->s;
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
const char *delay, *cmd; const char *delay, *cmd;
struct format_tree *ft;
double d; double d;
struct timeval tv; struct timeval tv;
char *end; char *end, key[16];
u_int i;
int wait = !args_has(args, 'b'); int wait = !args_has(args, 'b');
if ((delay = args_get(args, 'd')) != NULL) { if ((delay = args_get(args, 'd')) != NULL) {
@@ -132,8 +134,15 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata = xcalloc(1, sizeof *cdata); cdata = xcalloc(1, sizeof *cdata);
if (!args_has(args, 'C')) { if (!args_has(args, 'C')) {
cmd = args_string(args, 0); cmd = args_string(args, 0);
if (cmd != NULL) if (cmd != NULL) {
cdata->cmd = format_single_from_target(item, cmd); ft = format_create_from_target(item);
for (i = 1; i < args_count(args); i++) {
xsnprintf(key, sizeof key, "%u", i);
format_add(ft, key, "%s", args_string(args, i));
}
cdata->cmd = format_expand(ft, cmd);
format_free(ft);
}
} else { } else {
cdata->state = args_make_commands_prepare(self, item, 0, NULL, cdata->state = args_make_commands_prepare(self, item, 0, NULL,
wait, 1); wait, 1);
@@ -198,8 +207,17 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
} }
if (job_run(cmd, 0, NULL, NULL, cdata->s, cdata->cwd, NULL, if (job_run(cmd, 0, NULL, NULL, cdata->s, cdata->cwd, NULL,
cmd_run_shell_callback, cmd_run_shell_free, cdata, cmd_run_shell_callback, cmd_run_shell_free, cdata,
cdata->flags, -1, -1) == NULL) cdata->flags, -1, -1) == NULL) {
if (cdata->item == NULL)
status_message_set(c, -1, 1, 0, 0,
"failed to run command: %s", cmd);
else {
cmdq_error(cdata->item,
"failed to run command: %s", cmd);
cmdq_continue(cdata->item);
}
cmd_run_shell_free(cdata); cmd_run_shell_free(cdata);
}
return; return;
} }

View File

@@ -90,7 +90,7 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
previous = 1; previous = 1;
oldlayout = w->old_layout; oldlayout = w->old_layout;
w->old_layout = layout_dump(w->layout_root); w->old_layout = layout_dump(w, w->layout_root);
if (next || previous) { if (next || previous) {
if (next) if (next)

View File

@@ -103,7 +103,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
* spawned without being visited (for example split-window -d). * spawned without being visited (for example split-window -d).
*/ */
lastwp = TAILQ_FIRST(&w->last_panes); lastwp = TAILQ_FIRST(&w->last_panes);
if (lastwp == NULL && window_count_panes(w) == 2) { if (lastwp == NULL && window_count_panes(w, 1) == 2) {
lastwp = TAILQ_PREV(w->active, window_panes, entry); lastwp = TAILQ_PREV(w->active, window_panes, entry);
if (lastwp == NULL) if (lastwp == NULL)
lastwp = TAILQ_NEXT(w->active, entry); lastwp = TAILQ_NEXT(w->active, entry);
@@ -160,6 +160,10 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
server_redraw_window_borders(markedwp->window); server_redraw_window_borders(markedwp->window);
server_status_window(markedwp->window); server_status_window(markedwp->window);
} }
if (window_pane_is_floating(wp)) {
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
}
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -213,7 +217,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'T')) { if (args_has(args, 'T')) {
title = format_single_from_target(item, args_get(args, 'T')); title = format_single_from_target(item, args_get(args, 'T'));
if (screen_set_title(&wp->base, title)) { if (screen_set_title(&wp->base, title, 0)) {
notify_pane("pane-title-changed", wp); notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window); server_redraw_window_borders(wp->window);
server_status_window(wp->window); server_status_window(wp->window);

View File

@@ -39,7 +39,8 @@ const struct cmd_entry cmd_send_keys_entry = {
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL, .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL|
CMD_READONLY,
.exec = cmd_send_keys_exec .exec = cmd_send_keys_exec
}; };
@@ -167,6 +168,11 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
u_int count = args_count(args); u_int count = args_count(args);
char *cause = NULL; char *cause = NULL;
if (tc != NULL && tc->flags & CLIENT_READONLY && !args_has(args, 'X')) {
cmdq_error(item, "client is read-only");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'N')) { if (args_has(args, 'N')) {
np = args_strtonum_and_expand(args, 'N', 1, UINT_MAX, item, np = args_strtonum_and_expand(args, 'N', 1, UINT_MAX, item,
&cause); &cause);

View File

@@ -90,6 +90,7 @@ cmd_server_access_exec(struct cmd *self, struct cmdq_item *item)
pw = getpwnam(name); pw = getpwnam(name);
if (pw == NULL) { if (pw == NULL) {
cmdq_error(item, "unknown user: %s", name); cmdq_error(item, "unknown user: %s", name);
free(name);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
free(name); free(name);

View File

@@ -57,30 +57,30 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct paste_buffer *pb; struct paste_buffer *pb = NULL;
char *bufdata, *cause; char *bufname = NULL, *bufdata = NULL, *cause = NULL;
const char *bufname, *olddata; const char *olddata;
size_t bufsize, newsize; size_t bufsize = 0, newsize;
bufname = args_get(args, 'b'); if (args_get(args, 'b') != NULL) {
if (bufname == NULL) bufname = xstrdup(args_get(args, 'b'));
pb = NULL;
else
pb = paste_get_name(bufname); pb = paste_get_name(bufname);
}
if (cmd_get_entry(self) == &cmd_delete_buffer_entry) { if (cmd_get_entry(self) == &cmd_delete_buffer_entry) {
if (pb == NULL) { if (pb == NULL) {
if (bufname != NULL) { if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname); cmdq_error(item, "unknown buffer: %s", bufname);
return (CMD_RETURN_ERROR); goto fail;
} }
pb = paste_get_top(&bufname); pb = paste_get_top(&bufname);
} }
if (pb == NULL) { if (pb == NULL) {
cmdq_error(item, "no buffer"); cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR); goto fail;
} }
paste_free(pb); paste_free(pb);
free(bufname);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -88,32 +88,28 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (pb == NULL) { if (pb == NULL) {
if (bufname != NULL) { if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname); cmdq_error(item, "unknown buffer: %s", bufname);
return (CMD_RETURN_ERROR); goto fail;
} }
pb = paste_get_top(&bufname); pb = paste_get_top(&bufname);
} }
if (pb == NULL) { if (pb == NULL) {
cmdq_error(item, "no buffer"); cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR); goto fail;
} }
if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) { if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) {
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(cause); goto fail;
return (CMD_RETURN_ERROR);
} }
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (args_count(args) != 1) { if (args_count(args) != 1) {
cmdq_error(item, "no data specified"); cmdq_error(item, "no data specified");
return (CMD_RETURN_ERROR); goto fail;
} }
if ((newsize = strlen(args_string(args, 0))) == 0) if ((newsize = strlen(args_string(args, 0))) == 0)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
bufsize = 0;
bufdata = NULL;
if (args_has(args, 'a') && pb != NULL) { if (args_has(args, 'a') && pb != NULL) {
olddata = paste_buffer_data(pb, &bufsize); olddata = paste_buffer_data(pb, &bufsize);
bufdata = xmalloc(bufsize); bufdata = xmalloc(bufsize);
@@ -126,12 +122,16 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (paste_set(bufdata, bufsize, bufname, &cause) != 0) { if (paste_set(bufdata, bufsize, bufname, &cause) != 0) {
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(bufdata); goto fail;
free(cause);
return (CMD_RETURN_ERROR);
} }
if (args_has(args, 'w') && tc != NULL) if (args_has(args, 'w') && tc != NULL)
tty_set_selection(&tc->tty, "", bufdata, bufsize); tty_set_selection(&tc->tty, "", bufdata, bufsize);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
fail:
free(bufdata);
free(bufname);
free(cause);
return (CMD_RETURN_ERROR);
} }

View File

@@ -60,6 +60,9 @@ cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_clear_prompt_history_entry) { if (cmd_get_entry(self) == &cmd_clear_prompt_history_entry) {
if (typestr == NULL) { if (typestr == NULL) {
for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) { for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) {
for (hidx = 0; hidx < status_prompt_hsize[tidx];
hidx++)
free(status_prompt_hlist[tidx][hidx]);
free(status_prompt_hlist[tidx]); free(status_prompt_hlist[tidx]);
status_prompt_hlist[tidx] = NULL; status_prompt_hlist[tidx] = NULL;
status_prompt_hsize[tidx] = 0; status_prompt_hsize[tidx] = 0;
@@ -70,6 +73,8 @@ cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "invalid type: %s", typestr); cmdq_error(item, "invalid type: %s", typestr);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
for (hidx = 0; hidx < status_prompt_hsize[type]; hidx++)
free(status_prompt_hlist[type][hidx]);
free(status_prompt_hlist[type]); free(status_prompt_hlist[type]);
status_prompt_hlist[type] = NULL; status_prompt_hlist[type] = NULL;
status_prompt_hsize[type] = 0; status_prompt_hsize[type] = 0;

View File

@@ -27,22 +27,41 @@
#include "tmux.h" #include "tmux.h"
/* /*
* Split a window (add a new pane). * Create a new pane.
*/ */
#define SPLIT_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" #define SPLIT_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}"
static enum cmd_retval cmd_split_window_exec(struct cmd *, static enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmdq_item *);
struct cmdq_item *);
const struct cmd_entry cmd_new_pane_entry = {
.name = "new-pane",
.alias = "newp",
.args = { "bc:de:EfF:hIkl:Lm:p:PR:s:S:t:vx:X:y:Y:Z", 0, -1, NULL },
.usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-p percentage] "
"[-s style] [-S active-border-style] "
"[-R inactive-border-style] [-x width] [-y height] "
"[-X x-position] [-Y y-position] " CMD_TARGET_PANE_USAGE " "
"[shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_split_window_exec
};
const struct cmd_entry cmd_split_window_entry = { const struct cmd_entry cmd_split_window_entry = {
.name = "split-window", .name = "split-window",
.alias = "splitw", .alias = "splitw",
.args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1, NULL }, .args = { "bc:de:EfF:hIkl:m:p:PR:s:S:t:vZ", 0, -1, NULL },
.usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] " .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] " CMD_TARGET_PANE_USAGE "[-F format] [-l size] [-m message] [-p percentage] "
" [shell-command [argument ...]]", "[-s style] [-S active-border-style] "
"[-R inactive-border-style] " CMD_TARGET_PANE_USAGE " "
"[shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -62,64 +81,46 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *wl = target->wl; struct winlink *wl = target->wl;
struct window *w = wl->window; struct window *w = wl->window;
struct window_pane *wp = target->wp, *new_wp; struct window_pane *wp = target->wp, *new_wp;
enum layout_type type; struct layout_cell *lc = NULL;
struct layout_cell *lc;
struct cmd_find_state fs; struct cmd_find_state fs;
int size, flags, input; int input, empty, is_floating, flags = 0;
const char *template; const char *template, *style;
char *cause = NULL, *cp; char *cause = NULL, *cp;
struct args_value *av; struct args_value *av;
u_int count = args_count(args), curval = 0; u_int count = args_count(args);
type = LAYOUT_TOPBOTTOM; if (cmd_get_entry(self) == &cmd_new_pane_entry)
if (args_has(args, 'h')) is_floating = !args_has(args, 'L');
type = LAYOUT_LEFTRIGHT; else
is_floating = 0;
/* If the 'p' flag is dropped then this bit can be moved into 'l'. */ flags = is_floating ? SPAWN_FLOATING : 0;
if (args_has(args, 'l') || args_has(args, 'p')) {
if (args_has(args, 'f')) {
if (type == LAYOUT_TOPBOTTOM)
curval = w->sy;
else
curval = w->sx;
} else {
if (type == LAYOUT_TOPBOTTOM)
curval = wp->sy;
else
curval = wp->sx;
}
}
size = -1;
if (args_has(args, 'l')) {
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
item, &cause);
} else if (args_has(args, 'p')) {
size = args_strtonum_and_expand(args, 'p', 0, 100, item,
&cause);
if (cause == NULL)
size = curval * size / 100;
}
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
input = (args_has(args, 'I') && count == 0);
flags = 0;
if (args_has(args, 'b')) if (args_has(args, 'b'))
flags |= SPAWN_BEFORE; flags |= SPAWN_BEFORE;
if (args_has(args, 'f')) if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE; flags |= SPAWN_FULLSIZE;
if (input || (count == 1 && *args_string(args, 0) == '\0'))
input = args_has(args, 'I');
if (input)
empty = 1;
else
empty = args_has(args, 'E');
if (empty &&
count != 0 &&
(count != 1 || *args_string(args, 0) != '\0')) {
cmdq_error(item, "command cannot be given for empty pane");
return (CMD_RETURN_ERROR);
}
if (empty)
flags |= SPAWN_EMPTY; flags |= SPAWN_EMPTY;
lc = layout_split_pane(wp, type, size, flags); if (is_floating)
if (lc == NULL) { lc = layout_get_floating_cell(item, args, w, wp, &cause);
cmdq_error(item, "no space for new pane"); else
lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause);
if (cause != NULL) {
cmdq_error(item, "size or position %s", cause);
free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
@@ -156,11 +157,51 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
environ_free(sc.environ); environ_free(sc.environ);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
style = args_get(args, 's');
if (style != NULL) {
if (options_set_string(new_wp->options, "window-style", 0,
"%s", style) == NULL) {
cmdq_error(item, "bad style: %s", style);
return (CMD_RETURN_ERROR);
}
options_set_string(new_wp->options, "window-active-style", 0,
"%s", style);
new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
PANE_THEMECHANGED);
}
style = args_get(args, 'S');
if (style != NULL) {
if (options_set_string(new_wp->options,
"pane-active-border-style", 0, "%s", style) == NULL) {
cmdq_error(item, "bad active border style: %s", style);
return (CMD_RETURN_ERROR);
}
}
style = args_get(args, 'R');
if (style != NULL) {
if (options_set_string(new_wp->options, "pane-border-style", 0,
"%s", style) == NULL) {
cmdq_error(item, "bad inactive border style: %s",
style);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'k') || args_has(args, 'm')) {
options_set_number(new_wp->options, "remain-on-exit", 3);
if (args_has(args, 'm')) {
options_set_string(new_wp->options,
"remain-on-exit-format", 0, "%s",
args_get(args, 'm'));
}
}
if (input) { if (input) {
switch (window_pane_start_input(new_wp, item, &cause)) { switch (window_pane_start_input(new_wp, item, &cause)) {
case -1: case -1:
server_client_remove_pane(new_wp); server_client_remove_pane(new_wp);
layout_close_pane(new_wp); if (!is_floating)
layout_close_pane(new_wp);
window_remove_pane(wp->window, new_wp); window_remove_pane(wp->window, new_wp);
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(cause); free(cause);
@@ -175,9 +216,12 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
} }
if (!args_has(args, 'd')) if (!args_has(args, 'd'))
cmd_find_from_winlink_pane(current, wl, new_wp, 0); cmd_find_from_winlink_pane(current, wl, new_wp, 0);
window_pop_zoom(wp->window);
server_redraw_window(wp->window); if (!is_floating) {
server_status_session(s); window_pop_zoom(wp->window);
server_redraw_window(wp->window);
}
server_redraw_session(s);
if (args_has(args, 'P')) { if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL) if ((template = args_get(args, 'F')) == NULL)

View File

@@ -79,6 +79,12 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
if (src_wp == dst_wp) if (src_wp == dst_wp)
goto out; goto out;
if (window_pane_is_floating(src_wp) ||
window_pane_is_floating(dst_wp)) {
cmdq_error(item, "cannot swap floating panes");
return (CMD_RETURN_ERROR);
}
server_client_remove_pane(src_wp); server_client_remove_pane(src_wp);
server_client_remove_pane(dst_wp); server_client_remove_pane(dst_wp);
@@ -92,12 +98,26 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
else else
TAILQ_INSERT_AFTER(&dst_w->panes, tmp_wp, src_wp, entry); TAILQ_INSERT_AFTER(&dst_w->panes, tmp_wp, src_wp, entry);
tmp_wp = TAILQ_PREV(dst_wp, window_panes, zentry);
TAILQ_REMOVE(&dst_w->z_index, dst_wp, zentry);
TAILQ_REPLACE(&src_w->z_index, src_wp, dst_wp, zentry);
if (tmp_wp == src_wp)
tmp_wp = dst_wp;
if (tmp_wp == NULL)
TAILQ_INSERT_HEAD(&dst_w->z_index, src_wp, zentry);
else
TAILQ_INSERT_AFTER(&dst_w->z_index, tmp_wp, src_wp, zentry);
src_lc = src_wp->layout_cell; src_lc = src_wp->layout_cell;
dst_lc = dst_wp->layout_cell; dst_lc = dst_wp->layout_cell;
src_lc->wp = dst_wp; src_lc->wp = dst_wp;
dst_wp->layout_cell = src_lc; dst_wp->layout_cell = src_lc;
dst_lc->wp = src_wp; dst_lc->wp = src_wp;
src_wp->layout_cell = dst_lc; src_wp->layout_cell = dst_lc;
if (window_pane_is_floating(src_wp) != window_pane_is_floating(dst_wp)) {
src_wp->layout_cell->flags ^= LAYOUT_CELL_FLOATING;
dst_wp->layout_cell->flags ^= LAYOUT_CELL_FLOATING;
}
src_wp->window = dst_w; src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options); options_set_parent(src_wp->options, dst_w->options);

View File

@@ -20,6 +20,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include "tmux.h" #include "tmux.h"
@@ -34,9 +35,9 @@ const struct cmd_entry cmd_switch_client_entry = {
.name = "switch-client", .name = "switch-client",
.alias = "switchc", .alias = "switchc",
.args = { "lc:EFnpt:rT:Z", 0, 0, NULL }, .args = { "c:EFlnO:pt:rT:Z", 0, 0, NULL },
.usage = "[-ElnprZ] [-c target-client] [-t target-session] " .usage = "[-ElnprZ] [-c target-client] [-t target-session] "
"[-T key-table]", "[-T key-table] [-O order]",
/* -t is special */ /* -t is special */
@@ -53,6 +54,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
const char *tflag = args_get(args, 't'); const char *tflag = args_get(args, 't');
enum cmd_find_type type; enum cmd_find_type type;
int flags; int flags;
struct client *c = cmdq_get_client(item);
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct session *s; struct session *s;
struct winlink *wl; struct winlink *wl;
@@ -60,6 +62,8 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *wp; struct window_pane *wp;
const char *tablename; const char *tablename;
struct key_table *table; struct key_table *table;
struct sort_criteria sort_crit;
uid_t uid;
if (tflag != NULL && if (tflag != NULL &&
(tflag[strcspn(tflag, ":.%")] != '\0' || strcmp(tflag, "=") == 0)) { (tflag[strcspn(tflag, ":.%")] != '\0' || strcmp(tflag, "=") == 0)) {
@@ -76,6 +80,13 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
wp = target.wp; wp = target.wp;
if (args_has(args, 'r')) { if (args_has(args, 'r')) {
if (tc->flags & CLIENT_READONLY) {
uid = proc_get_peer_uid(c->peer);
if (uid != getuid()) {
cmdq_error(item, "client is read-only");
return (CMD_RETURN_ERROR);
}
}
if (tc->flags & CLIENT_READONLY) if (tc->flags & CLIENT_READONLY)
tc->flags &= ~(CLIENT_READONLY|CLIENT_IGNORESIZE); tc->flags &= ~(CLIENT_READONLY|CLIENT_IGNORESIZE);
else else
@@ -95,13 +106,22 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
if (args_has(args, 'n')) { if (args_has(args, 'n')) {
if ((s = session_next_session(tc->session)) == NULL) { s = session_next_session(tc->session, &sort_crit);
if (s == NULL) {
cmdq_error(item, "can't find next session"); cmdq_error(item, "can't find next session");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
} else if (args_has(args, 'p')) { } else if (args_has(args, 'p')) {
if ((s = session_previous_session(tc->session)) == NULL) { s = session_previous_session(tc->session, &sort_crit);
if (s == NULL) {
cmdq_error(item, "can't find previous session"); cmdq_error(item, "can't find previous session");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }

31
cmd.c
View File

@@ -71,6 +71,7 @@ extern const struct cmd_entry cmd_lock_server_entry;
extern const struct cmd_entry cmd_lock_session_entry; extern const struct cmd_entry cmd_lock_session_entry;
extern const struct cmd_entry cmd_move_pane_entry; extern const struct cmd_entry cmd_move_pane_entry;
extern const struct cmd_entry cmd_move_window_entry; extern const struct cmd_entry cmd_move_window_entry;
extern const struct cmd_entry cmd_new_pane_entry;
extern const struct cmd_entry cmd_new_session_entry; extern const struct cmd_entry cmd_new_session_entry;
extern const struct cmd_entry cmd_new_window_entry; extern const struct cmd_entry cmd_new_window_entry;
extern const struct cmd_entry cmd_next_layout_entry; extern const struct cmd_entry cmd_next_layout_entry;
@@ -163,6 +164,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_lock_session_entry, &cmd_lock_session_entry,
&cmd_move_pane_entry, &cmd_move_pane_entry,
&cmd_move_window_entry, &cmd_move_window_entry,
&cmd_new_pane_entry,
&cmd_new_session_entry, &cmd_new_session_entry,
&cmd_new_window_entry, &cmd_new_window_entry,
&cmd_next_layout_entry, &cmd_next_layout_entry,
@@ -303,6 +305,8 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
if (argc == 0) if (argc == 0)
return (0); return (0);
if (argc < 0 || argc > 1000)
return (-1);
*argv = xcalloc(argc, sizeof **argv); *argv = xcalloc(argc, sizeof **argv);
buf[len - 1] = '\0'; buf[len - 1] = '\0';
@@ -676,11 +680,16 @@ cmd_list_copy(const struct cmd_list *cmdlist, int argc, char **argv)
/* Get a command list as a string. */ /* Get a command list as a string. */
char * char *
cmd_list_print(const struct cmd_list *cmdlist, int escaped) cmd_list_print(const struct cmd_list *cmdlist, int flags)
{ {
struct cmd *cmd, *next; struct cmd *cmd, *next;
char *buf, *this; char *buf, *this;
size_t len; size_t len;
const char *separator;
int escaped = flags & CMD_LIST_PRINT_ESCAPED;
int no_groups = flags & CMD_LIST_PRINT_NO_GROUPS;
const char *single_separator = escaped ? " \\; " : " ; ";
const char *double_separator = escaped ? " \\;\\; " : " ;; ";
len = 1; len = 1;
buf = xcalloc(1, len); buf = xcalloc(1, len);
@@ -695,17 +704,11 @@ cmd_list_print(const struct cmd_list *cmdlist, int escaped)
next = TAILQ_NEXT(cmd, qentry); next = TAILQ_NEXT(cmd, qentry);
if (next != NULL) { if (next != NULL) {
if (cmd->group != next->group) { if (!no_groups && cmd->group != next->group)
if (escaped) separator = double_separator;
strlcat(buf, " \\;\\; ", len); else
else separator = single_separator;
strlcat(buf, " ;; ", len); strlcat(buf, separator, len);
} else {
if (escaped)
strlcat(buf, " \\; ", len);
else
strlcat(buf, " ; ", len);
}
} }
free(this); free(this);
@@ -773,9 +776,9 @@ cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
if (m->statusat == 0 && y >= m->statuslines) if (m->statusat == 0 && y >= m->statuslines)
y -= m->statuslines; y -= m->statuslines;
if (x < wp->xoff || x >= wp->xoff + wp->sx) if ((int)x < wp->xoff || (int)x >= wp->xoff + (int)wp->sx)
return (-1); return (-1);
if (y < wp->yoff || y >= wp->yoff + wp->sy) if ((int)y < wp->yoff || (int)y >= wp->yoff + (int)wp->sy)
return (-1); return (-1);
if (xp != NULL) if (xp != NULL)

View File

@@ -1082,22 +1082,22 @@ colour_palette_free(struct colour_palette *p)
/* Get a colour from a palette. */ /* Get a colour from a palette. */
int int
colour_palette_get(struct colour_palette *p, int c) colour_palette_get(struct colour_palette *p, int n)
{ {
if (p == NULL) if (p == NULL)
return (-1); return (-1);
if (c >= 90 && c <= 97) if (n >= 90 && n <= 97)
c = 8 + c - 90; n = 8 + n - 90;
else if (c & COLOUR_FLAG_256) else if (n & COLOUR_FLAG_256)
c &= ~COLOUR_FLAG_256; n &= ~COLOUR_FLAG_256;
else if (c >= 8) else if (n >= 8)
return (-1); return (-1);
if (p->palette != NULL && p->palette[c] != -1) if (p->palette != NULL && p->palette[n] != -1)
return (p->palette[c]); return (p->palette[n]);
if (p->default_palette != NULL && p->default_palette[c] != -1) if (p->default_palette != NULL && p->default_palette[n] != -1)
return (p->default_palette[c]); return (p->default_palette[n]);
return (-1); return (-1);
} }
@@ -1107,15 +1107,14 @@ colour_palette_set(struct colour_palette *p, int n, int c)
{ {
u_int i; u_int i;
if (p == NULL || n > 255) if (p == NULL || n < 0 || n > 255)
return (0); return (0);
if (c == -1 && p->palette == NULL) if (c == -1 && p->palette == NULL)
return (0); return (0);
if (c != -1 && p->palette == NULL) { if (p->palette == NULL) {
if (p->palette == NULL) p->palette = xcalloc(256, sizeof *p->palette);
p->palette = xcalloc(256, sizeof *p->palette);
for (i = 0; i < 256; i++) for (i = 0; i < 256; i++)
p->palette[i] = -1; p->palette[i] = -1;
} }

View File

@@ -388,7 +388,7 @@ int clock_gettime(int, struct timespec *);
/* base64.c */ /* base64.c */
#undef b64_ntop #undef b64_ntop
#undef b64_pton #undef b64_pton
int b64_ntop(const char *, size_t, char *, size_t); int b64_ntop(const u_char *, size_t, char *, size_t);
int b64_pton(const char *, u_char *, size_t); int b64_pton(const char *, u_char *, size_t);
#endif #endif
@@ -463,6 +463,9 @@ int utf8proc_wctomb(char *, wchar_t);
#ifdef NEED_FUZZING #ifdef NEED_FUZZING
/* tmux.c */ /* tmux.c */
#define main __weak main #define main __weak main
#define regcomp(preg, pattern, cflags) (0)
#define regexec(preg, string, nmatch, pmatch, eflags) (REG_NOMATCH)
#define regfree(preg) ((void)0)
#endif #endif
/* getopt.c */ /* getopt.c */

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: base64.c,v 1.8 2015/01/16 16:48:51 deraadt Exp $ */ /* $OpenBSD: base64.c,v 1.15 2021/10/25 14:41:09 jca Exp $ */
/* /*
* Copyright (c) 1996 by Internet Software Consortium. * Copyright (c) 1996 by Internet Software Consortium.
@@ -46,15 +46,15 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <arpa/nameser.h>
#include <ctype.h> #include <ctype.h>
#include <resolv.h> #include <resolv.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "compat.h"
static const char Base64[] = static const char Base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char Pad64 = '='; static const char Pad64 = '=';
@@ -107,9 +107,9 @@ static const char Pad64 = '=';
end of the data is performed using the '=' character. end of the data is performed using the '=' character.
Since all base64 input is an integral number of octets, only the Since all base64 input is an integral number of octets, only the
------------------------------------------------- -------------------------------------------------
following cases can arise: following cases can arise:
(1) the final quantum of encoding input is an integral (1) the final quantum of encoding input is an integral
multiple of 24 bits; here, the final unit of encoded multiple of 24 bits; here, the final unit of encoded
output will be an integral multiple of 4 characters output will be an integral multiple of 4 characters
@@ -123,15 +123,12 @@ static const char Pad64 = '=';
*/ */
int int
b64_ntop(src, srclength, target, targsize) b64_ntop(unsigned char const *src, size_t srclength, char *target,
u_char const *src; size_t targsize)
size_t srclength;
char *target;
size_t targsize;
{ {
size_t datalength = 0; size_t datalength = 0;
u_char input[3]; unsigned char input[3];
u_char output[4]; unsigned char output[4];
int i; int i;
while (2 < srclength) { while (2 < srclength) {
@@ -152,14 +149,14 @@ b64_ntop(src, srclength, target, targsize)
target[datalength++] = Base64[output[2]]; target[datalength++] = Base64[output[2]];
target[datalength++] = Base64[output[3]]; target[datalength++] = Base64[output[3]];
} }
/* Now we worry about padding. */ /* Now we worry about padding. */
if (0 != srclength) { if (0 != srclength) {
/* Get what's left. */ /* Get what's left. */
input[0] = input[1] = input[2] = '\0'; input[0] = input[1] = input[2] = '\0';
for (i = 0; i < srclength; i++) for (i = 0; i < srclength; i++)
input[i] = *src++; input[i] = *src++;
output[0] = input[0] >> 2; output[0] = input[0] >> 2;
output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
@@ -187,13 +184,10 @@ b64_ntop(src, srclength, target, targsize)
*/ */
int int
b64_pton(src, target, targsize) b64_pton(char const *src, unsigned char *target, size_t targsize)
char const *src;
u_char *target;
size_t targsize;
{ {
int tarindex, state, ch; int tarindex, state, ch;
u_char nextbyte; unsigned char nextbyte;
char *pos; char *pos;
state = 0; state = 0;
@@ -207,7 +201,7 @@ b64_pton(src, target, targsize)
break; break;
pos = strchr(Base64, ch); pos = strchr(Base64, ch);
if (pos == 0) /* A non-base64 character. */ if (pos == 0) /* A non-base64 character. */
return (-1); return (-1);
switch (state) { switch (state) {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: imsg-buffer.c,v 1.35 2025/06/04 09:06:56 claudio Exp $ */ /* $OpenBSD: imsg-buffer.c,v 1.36 2025/08/25 08:29:49 claudio Exp $ */
/* /*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org> * Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
@@ -699,8 +699,6 @@ msgbuf_queuelen(struct msgbuf *msgbuf)
void void
msgbuf_clear(struct msgbuf *msgbuf) msgbuf_clear(struct msgbuf *msgbuf)
{ {
struct ibuf *buf;
/* write side */ /* write side */
ibufq_flush(&msgbuf->bufs); ibufq_flush(&msgbuf->bufs);

View File

@@ -81,7 +81,8 @@ struct systemd_job_watch {
}; };
static int static int
job_removed_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) job_removed_handler(sd_bus_message *m, void *userdata,
__unused sd_bus_error *ret_error)
{ {
struct systemd_job_watch *watch = userdata; struct systemd_job_watch *watch = userdata;
const char *path = NULL; const char *path = NULL;
@@ -109,7 +110,7 @@ systemd_move_to_new_cgroup(char **cause)
sd_bus_message *m = NULL, *reply = NULL; sd_bus_message *m = NULL, *reply = NULL;
sd_bus *bus = NULL; sd_bus *bus = NULL;
sd_bus_slot *slot = NULL; sd_bus_slot *slot = NULL;
char *name, *desc, *slice; char *name, *desc, *slice, *unit;
sd_id128_t uuid; sd_id128_t uuid;
int r; int r;
uint64_t elapsed_usec; uint64_t elapsed_usec;
@@ -241,6 +242,26 @@ systemd_move_to_new_cgroup(char **cause)
goto finish; goto finish;
} }
/*
* Try locating systemd unit that started the server, and mark pane units
* as dependent on it. Use "Before" to make sure systemd will not try to
* kill them first.
*/
if (sd_pid_get_user_unit(parent_pid, &unit) == 0 ||
sd_pid_get_unit(parent_pid, &unit) == 0) {
r = sd_bus_message_append(m, "(sv)", "Before", "as", 1, unit);
if (r >= 0) {
r = sd_bus_message_append(m, "(sv)", "PartOf", "as", 1,
unit);
}
free(unit);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
}
/* End properties array. */ /* End properties array. */
r = sd_bus_message_close_container(m); r = sd_bus_message_close_container(m);
if (r < 0) { if (r < 0) {

View File

@@ -1,6 +1,6 @@
# configure.ac # configure.ac
AC_INIT([tmux], 3.6a) AC_INIT([tmux], 3.7b)
AC_PREREQ([2.60]) AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc) AC_CONFIG_AUX_DIR(etc)
@@ -64,6 +64,24 @@ AC_ARG_ENABLE(
) )
AM_CONDITIONAL(IS_DEBUG, test "x$enable_debug" = xyes) AM_CONDITIONAL(IS_DEBUG, test "x$enable_debug" = xyes)
# Is this --enable-optimizations?
AC_ARG_ENABLE(
optimizations,
AS_HELP_STRING(--enable-optimizations, enable optimization build flags),
,
enable_optimizations=yes
)
AM_CONDITIONAL(IS_OPTIMIZED, test "x$enable_optimizations" = xyes)
# Is this --enable-asan?
AC_ARG_ENABLE(
asan,
AS_HELP_STRING(--enable-asan, enable ASAN build flags),
,
enable_asan=no
)
AM_CONDITIONAL(IS_ASAN, test "x$enable_asan" = xyes)
# Is this a static build? # Is this a static build?
AC_ARG_ENABLE( AC_ARG_ENABLE(
static, static,
@@ -279,24 +297,24 @@ fi
# Look for ncurses or curses. Try pkg-config first then directly for the # Look for ncurses or curses. Try pkg-config first then directly for the
# library. # library.
PKG_CHECK_MODULES( PKG_CHECK_MODULES(
LIBTINFO, LIBTINFOW,
tinfo, tinfow,
[ [
AM_CPPFLAGS="$LIBTINFO_CFLAGS $AM_CPPFLAGS" AM_CPPFLAGS="$LIBTINFOW_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS" CPPFLAGS="$LIBTINFOW_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBTINFO_LIBS $LIBS" LIBS="$LIBTINFOW_LIBS $LIBS"
found_ncurses=yes found_ncurses=yes
], ],
found_ncurses=no found_ncurses=no
) )
if test "x$found_ncurses" = xno; then if test "x$found_ncurses" = xno; then
PKG_CHECK_MODULES( PKG_CHECK_MODULES(
LIBNCURSES, LIBTINFO,
ncurses, tinfo,
[ [
AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS" AM_CPPFLAGS="$LIBTINFO_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS" CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBNCURSES_LIBS $LIBS" LIBS="$LIBTINFO_LIBS $LIBS"
found_ncurses=yes found_ncurses=yes
], ],
found_ncurses=no found_ncurses=no
@@ -315,10 +333,23 @@ if test "x$found_ncurses" = xno; then
found_ncurses=no found_ncurses=no
) )
fi fi
if test "x$found_ncurses" = xno; then
PKG_CHECK_MODULES(
LIBNCURSES,
ncurses,
[
AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBNCURSES_LIBS $LIBS"
found_ncurses=yes
],
found_ncurses=no
)
fi
if test "x$found_ncurses" = xno; then if test "x$found_ncurses" = xno; then
AC_SEARCH_LIBS( AC_SEARCH_LIBS(
setupterm, setupterm,
[tinfo terminfo ncurses ncursesw], [tinfow tinfo terminfo ncursesw ncurses],
found_ncurses=yes, found_ncurses=yes,
found_ncurses=no found_ncurses=no
) )

View File

@@ -24,7 +24,8 @@
#include "tmux.h" #include "tmux.h"
#define CONTROL_SHOULD_NOTIFY_CLIENT(c) \ #define CONTROL_SHOULD_NOTIFY_CLIENT(c) \
((c) != NULL && ((c)->flags & CLIENT_CONTROL)) ((c) != NULL && ((c)->flags & CLIENT_CONTROL) && \
(c)->control_state != NULL)
void void
control_notify_pane_mode_changed(int pane) control_notify_pane_mode_changed(int pane)
@@ -51,29 +52,24 @@ control_notify_window_layout_changed(struct window *w)
template = "%layout-change #{window_id} #{window_layout} " template = "%layout-change #{window_id} #{window_layout} "
"#{window_visible_layout} #{window_raw_flags}"; "#{window_visible_layout} #{window_raw_flags}";
/*
* When the last pane in a window is closed it won't have a layout root
* and we don't need to inform the client about the layout change
* because the whole window will go away soon.
*/
wl = TAILQ_FIRST(&w->winlinks);
if (wl == NULL || w->layout_root == NULL)
return;
cp = format_single(NULL, template, NULL, NULL, wl, NULL);
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
continue; continue;
s = c->session; s = c->session;
if (winlink_find_by_window_id(&s->windows, w->id) != NULL)
if (winlink_find_by_window_id(&s->windows, w->id) == NULL)
continue;
/*
* When the last pane in a window is closed it won't have a
* layout root and we don't need to inform the client about the
* layout change because the whole window will go away soon.
*/
if (w->layout_root == NULL)
continue;
wl = winlink_find_by_window(&s->windows, w);
if (wl != NULL) {
cp = format_single(NULL, template, c, NULL, wl, NULL);
control_write(c, "%s", cp); control_write(c, "%s", cp);
free(cp);
}
} }
free(cp);
} }
void void
@@ -81,6 +77,8 @@ control_notify_window_pane_changed(struct window *w)
{ {
struct client *c; struct client *c;
if (w->active == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue; continue;

219
control.c
View File

@@ -207,10 +207,12 @@ control_free_sub(struct control_state *cs, struct control_sub *csub)
RB_FOREACH_SAFE(csp, control_sub_panes, &csub->panes, csp1) { RB_FOREACH_SAFE(csp, control_sub_panes, &csub->panes, csp1) {
RB_REMOVE(control_sub_panes, &csub->panes, csp); RB_REMOVE(control_sub_panes, &csub->panes, csp);
free(csp->last);
free(csp); free(csp);
} }
RB_FOREACH_SAFE(csw, control_sub_windows, &csub->windows, csw1) { RB_FOREACH_SAFE(csw, control_sub_windows, &csub->windows, csw1) {
RB_REMOVE(control_sub_windows, &csub->windows, csw); RB_REMOVE(control_sub_windows, &csub->windows, csw);
free(csw->last);
free(csw); free(csw);
} }
free(csub->last); free(csub->last);
@@ -298,6 +300,7 @@ control_reset_offsets(struct client *c)
struct control_pane *cp, *cp1; struct control_pane *cp, *cp1;
RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) { RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) {
control_discard_pane(c, cp);
RB_REMOVE(control_panes, &cs->panes, cp); RB_REMOVE(control_panes, &cs->panes, cp);
free(cp); free(cp);
} }
@@ -352,6 +355,9 @@ control_set_pane_off(struct client *c, struct window_pane *wp)
struct control_pane *cp; struct control_pane *cp;
cp = control_add_pane(c, wp); cp = control_add_pane(c, wp);
control_discard_pane(c, cp);
memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
cp->flags |= CONTROL_PANE_OFF; cp->flags |= CONTROL_PANE_OFF;
} }
@@ -610,7 +616,7 @@ control_append_data(struct client *c, struct control_pane *cp, uint64_t age,
struct evbuffer *message, struct window_pane *wp, size_t size) struct evbuffer *message, struct window_pane *wp, size_t size)
{ {
u_char *new_data; u_char *new_data;
size_t new_size; size_t new_size, start;
u_int i; u_int i;
if (message == NULL) { if (message == NULL) {
@@ -629,10 +635,16 @@ control_append_data(struct client *c, struct control_pane *cp, uint64_t age,
if (new_size < size) if (new_size < size)
fatalx("not enough data: %zu < %zu", new_size, size); fatalx("not enough data: %zu < %zu", new_size, size);
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
if (new_data[i] < ' ' || new_data[i] == '\\') if (new_data[i] < ' ' || new_data[i] == '\\') {
evbuffer_add_printf(message, "\\%03o", new_data[i]); evbuffer_add_printf(message, "\\%03o", new_data[i]);
else } else {
evbuffer_add_printf(message, "%c", new_data[i]); start = i;
while (i + 1 < size &&
new_data[i + 1] >= ' ' &&
new_data[i + 1] != '\\')
i++;
evbuffer_add(message, new_data + start, i - start + 1);
}
} }
window_pane_update_used_data(wp, &cp->offset, size); window_pane_update_used_data(wp, &cp->offset, size);
return (message); return (message);
@@ -822,6 +834,9 @@ control_stop(struct client *c)
struct control_block *cb, *cb1; struct control_block *cb, *cb1;
struct control_sub *csub, *csub1; struct control_sub *csub, *csub1;
if (cs == NULL)
return;
if (~c->flags & CLIENT_CONTROLCONTROL) if (~c->flags & CLIENT_CONTROLCONTROL)
bufferevent_free(cs->write_event); bufferevent_free(cs->write_event);
bufferevent_free(cs->read_event); bufferevent_free(cs->read_event);
@@ -831,24 +846,23 @@ control_stop(struct client *c)
if (evtimer_initialized(&cs->subs_timer)) if (evtimer_initialized(&cs->subs_timer))
evtimer_del(&cs->subs_timer); evtimer_del(&cs->subs_timer);
control_reset_offsets(c);
TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
control_free_block(cs, cb); control_free_block(cs, cb);
control_reset_offsets(c);
c->control_state = NULL;
free(cs); free(cs);
} }
/* Check session subscription. */ /* Check session subscription. */
static void static void
control_check_subs_session(struct client *c, struct control_sub *csub) control_check_subs_session(struct client *c, struct control_sub *csub,
struct format_tree *ft)
{ {
struct session *s = c->session; struct session *s = c->session;
struct format_tree *ft;
char *value; char *value;
ft = format_create_defaults(NULL, c, s, NULL, NULL);
value = format_expand(ft, csub->format); value = format_expand(ft, csub->format);
format_free(ft);
if (csub->last != NULL && strcmp(value, csub->last) == 0) { if (csub->last != NULL && strcmp(value, csub->last) == 0) {
free(value); free(value);
@@ -909,48 +923,38 @@ control_check_subs_pane(struct client *c, struct control_sub *csub)
} }
} }
/* Check all panes subscription. */ /* Check all-panes subscription for a pane. */
static void static void
control_check_subs_all_panes(struct client *c, struct control_sub *csub) control_check_subs_all_panes_one(struct client *c, struct control_sub *csub,
struct format_tree *ft, struct winlink *wl, struct window_pane *wp)
{ {
struct session *s = c->session; struct session *s = c->session;
struct window_pane *wp; struct window *w = wl->window;
struct window *w;
struct winlink *wl;
struct format_tree *ft;
char *value; char *value;
struct control_sub_pane *csp, find; struct control_sub_pane *csp, find;
RB_FOREACH(wl, winlinks, &s->windows) { value = format_expand(ft, csub->format);
w = wl->window;
TAILQ_FOREACH(wp, &w->panes, entry) {
ft = format_create_defaults(NULL, c, s, wl, wp);
value = format_expand(ft, csub->format);
format_free(ft);
find.pane = wp->id; find.pane = wp->id;
find.idx = wl->idx; find.idx = wl->idx;
csp = RB_FIND(control_sub_panes, &csub->panes, &find); csp = RB_FIND(control_sub_panes, &csub->panes, &find);
if (csp == NULL) { if (csp == NULL) {
csp = xcalloc(1, sizeof *csp); csp = xcalloc(1, sizeof *csp);
csp->pane = wp->id; csp->pane = wp->id;
csp->idx = wl->idx; csp->idx = wl->idx;
RB_INSERT(control_sub_panes, &csub->panes, csp); RB_INSERT(control_sub_panes, &csub->panes, csp);
}
if (csp->last != NULL &&
strcmp(value, csp->last) == 0) {
free(value);
continue;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u %%%u : %s",
csub->name, s->id, w->id, wl->idx, wp->id, value);
free(csp->last);
csp->last = value;
}
} }
if (csp->last != NULL && strcmp(value, csp->last) == 0) {
free(value);
return;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u %%%u : %s",
csub->name, s->id, w->id, wl->idx, wp->id, value);
free(csp->last);
csp->last = value;
} }
/* Check window subscription. */ /* Check window subscription. */
@@ -999,45 +1003,38 @@ control_check_subs_window(struct client *c, struct control_sub *csub)
} }
} }
/* Check all windows subscription. */ /* Check all-windows subscription for a window. */
static void static void
control_check_subs_all_windows(struct client *c, struct control_sub *csub) control_check_subs_all_windows_one(struct client *c, struct control_sub *csub,
struct format_tree *ft, struct winlink *wl)
{ {
struct session *s = c->session; struct session *s = c->session;
struct window *w; struct window *w = wl->window;
struct winlink *wl;
struct format_tree *ft;
char *value; char *value;
struct control_sub_window *csw, find; struct control_sub_window *csw, find;
RB_FOREACH(wl, winlinks, &s->windows) { value = format_expand(ft, csub->format);
w = wl->window;
ft = format_create_defaults(NULL, c, s, wl, NULL); find.window = w->id;
value = format_expand(ft, csub->format); find.idx = wl->idx;
format_free(ft);
find.window = w->id; csw = RB_FIND(control_sub_windows, &csub->windows, &find);
find.idx = wl->idx; if (csw == NULL) {
csw = xcalloc(1, sizeof *csw);
csw = RB_FIND(control_sub_windows, &csub->windows, &find); csw->window = w->id;
if (csw == NULL) { csw->idx = wl->idx;
csw = xcalloc(1, sizeof *csw); RB_INSERT(control_sub_windows, &csub->windows, csw);
csw->window = w->id;
csw->idx = wl->idx;
RB_INSERT(control_sub_windows, &csub->windows, csw);
}
if (csw->last != NULL && strcmp(value, csw->last) == 0) {
free(value);
continue;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u - : %s",
csub->name, s->id, w->id, wl->idx, value);
free(csw->last);
csw->last = value;
} }
if (csw->last != NULL && strcmp(value, csw->last) == 0) {
free(value);
return;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u - : %s",
csub->name, s->id, w->id, wl->idx, value);
free(csw->last);
csw->last = value;
} }
/* Check subscriptions timer. */ /* Check subscriptions timer. */
@@ -1047,30 +1044,94 @@ control_check_subs_timer(__unused int fd, __unused short events, void *data)
struct client *c = data; struct client *c = data;
struct control_state *cs = c->control_state; struct control_state *cs = c->control_state;
struct control_sub *csub, *csub1; struct control_sub *csub, *csub1;
struct session *s = c->session;
struct format_tree *ft;
struct winlink *wl;
struct window_pane *wp;
struct timeval tv = { .tv_sec = 1 }; struct timeval tv = { .tv_sec = 1 };
int have_session = 0, have_all_panes = 0;
int have_all_windows = 0;
log_debug("%s: timer fired", __func__); log_debug("%s: timer fired", __func__);
evtimer_add(&cs->subs_timer, &tv); evtimer_add(&cs->subs_timer, &tv);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) { if (s == NULL)
return;
/* Find which subscription types are present. */
RB_FOREACH(csub, control_subs, &cs->subs) {
switch (csub->type) { switch (csub->type) {
case CONTROL_SUB_SESSION: case CONTROL_SUB_SESSION:
control_check_subs_session(c, csub); have_session = 1;
break;
case CONTROL_SUB_PANE:
control_check_subs_pane(c, csub);
break; break;
case CONTROL_SUB_ALL_PANES: case CONTROL_SUB_ALL_PANES:
control_check_subs_all_panes(c, csub); have_all_panes = 1;
break;
case CONTROL_SUB_ALL_WINDOWS:
have_all_windows = 1;
break;
default:
break;
}
}
/* Check session subscriptions. */
if (have_session) {
ft = format_create_defaults(NULL, c, s, NULL, NULL);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
if (csub->type == CONTROL_SUB_SESSION)
control_check_subs_session(c, csub, ft);
}
format_free(ft);
}
/* Check pane and window subscriptions. */
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
switch (csub->type) {
case CONTROL_SUB_PANE:
control_check_subs_pane(c, csub);
break; break;
case CONTROL_SUB_WINDOW: case CONTROL_SUB_WINDOW:
control_check_subs_window(c, csub); control_check_subs_window(c, csub);
break; break;
case CONTROL_SUB_SESSION:
case CONTROL_SUB_ALL_PANES:
case CONTROL_SUB_ALL_WINDOWS: case CONTROL_SUB_ALL_WINDOWS:
control_check_subs_all_windows(c, csub);
break; break;
} }
} }
/* Check all-panes subscriptions. */
if (have_all_panes) {
RB_FOREACH(wl, winlinks, &s->windows) {
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
ft = format_create_defaults(NULL, c, s, wl, wp);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
csub1) {
if (csub->type != CONTROL_SUB_ALL_PANES)
continue;
control_check_subs_all_panes_one(c,
csub, ft, wl, wp);
}
format_free(ft);
}
}
}
/* Check all-windows subscriptions. */
if (have_all_windows) {
RB_FOREACH(wl, winlinks, &s->windows) {
ft = format_create_defaults(NULL, c, s, wl, NULL);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
csub1) {
if (csub->type != CONTROL_SUB_ALL_WINDOWS)
continue;
control_check_subs_all_windows_one(c, csub, ft,
wl);
}
format_free(ft);
}
}
} }
/* Add a subscription. */ /* Add a subscription. */

View File

@@ -57,6 +57,9 @@ environ_free(struct environ *env)
{ {
struct environ_entry *envent, *envent1; struct environ_entry *envent, *envent1;
if (env == NULL)
return;
RB_FOREACH_SAFE(envent, environ, env, envent1) { RB_FOREACH_SAFE(envent, environ, env, envent1) {
RB_REMOVE(environ, env, envent); RB_REMOVE(environ, env, envent);
free(envent->name); free(envent->name);
@@ -263,11 +266,6 @@ environ_for_session(struct session *s, int no_TERM)
environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux"); environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux");
environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion()); environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion());
environ_set(env, "COLORTERM", 0, "truecolor"); environ_set(env, "COLORTERM", 0, "truecolor");
} else {
environ_unset(env, "TERM");
environ_unset(env, "TERM_PROGRAM");
environ_unset(env, "TERM_PROGRAM_VERSION");
environ_unset(env, "COLORTERM");
} }
#ifdef HAVE_SYSTEMD #ifdef HAVE_SYSTEMD

30
file.c
View File

@@ -55,6 +55,7 @@ file_get_path(struct client *c, const char *file)
if (*path == '/') if (*path == '/')
return (path); return (path);
xasprintf(&full_path, "%s/%s", server_client_get_cwd(c, NULL), path); xasprintf(&full_path, "%s/%s", server_client_get_cwd(c, NULL), path);
free(path);
return (full_path); return (full_path);
} }
@@ -398,6 +399,10 @@ file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
} }
for (;;) { for (;;) {
size = fread(buffer, 1, sizeof buffer, f); size = fread(buffer, 1, sizeof buffer, f);
if (ferror(f)) {
cf->error = errno;
goto done;
}
if (evbuffer_add(cf->buffer, buffer, size) != 0) { if (evbuffer_add(cf->buffer, buffer, size) != 0) {
cf->error = ENOMEM; cf->error = ENOMEM;
goto done; goto done;
@@ -670,7 +675,7 @@ file_write_close(struct client_files *files, struct imsg *imsg)
/* Client file read error callback. */ /* Client file read error callback. */
static void static void
file_read_error_callback(__unused struct bufferevent *bev, __unused short what, file_read_error_callback(__unused struct bufferevent *bev, short what,
void *arg) void *arg)
{ {
struct client_file *cf = arg; struct client_file *cf = arg;
@@ -679,7 +684,7 @@ file_read_error_callback(__unused struct bufferevent *bev, __unused short what,
log_debug("read error file %d", cf->stream); log_debug("read error file %d", cf->stream);
msg.stream = cf->stream; msg.stream = cf->stream;
msg.error = 0; msg.error = (what & EVBUFFER_ERROR) ? EIO : 0;
proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg); proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg);
bufferevent_free(cf->event); bufferevent_free(cf->event);
@@ -803,7 +808,7 @@ file_read_cancel(struct client_files *files, struct imsg *imsg)
} }
/* Handle a write ready message (server). */ /* Handle a write ready message (server). */
void int
file_write_ready(struct client_files *files, struct imsg *imsg) file_write_ready(struct client_files *files, struct imsg *imsg)
{ {
struct msg_write_ready *msg = imsg->data; struct msg_write_ready *msg = imsg->data;
@@ -811,19 +816,20 @@ file_write_ready(struct client_files *files, struct imsg *imsg)
struct client_file find, *cf; struct client_file find, *cf;
if (msglen != sizeof *msg) if (msglen != sizeof *msg)
fatalx("bad MSG_WRITE_READY size"); return (-1);
find.stream = msg->stream; find.stream = msg->stream;
if ((cf = RB_FIND(client_files, files, &find)) == NULL) if ((cf = RB_FIND(client_files, files, &find)) == NULL)
return; return (0);
if (msg->error != 0) { if (msg->error != 0) {
cf->error = msg->error; cf->error = msg->error;
file_fire_done(cf); file_fire_done(cf);
} else } else
file_push(cf); file_push(cf);
return (0);
} }
/* Handle read data message (server). */ /* Handle read data message (server). */
void int
file_read_data(struct client_files *files, struct imsg *imsg) file_read_data(struct client_files *files, struct imsg *imsg)
{ {
struct msg_read_data *msg = imsg->data; struct msg_read_data *msg = imsg->data;
@@ -833,10 +839,10 @@ file_read_data(struct client_files *files, struct imsg *imsg)
size_t bsize = msglen - sizeof *msg; size_t bsize = msglen - sizeof *msg;
if (msglen < sizeof *msg) if (msglen < sizeof *msg)
fatalx("bad MSG_READ_DATA size"); return (-1);
find.stream = msg->stream; find.stream = msg->stream;
if ((cf = RB_FIND(client_files, files, &find)) == NULL) if ((cf = RB_FIND(client_files, files, &find)) == NULL)
return; return (0);
log_debug("file %d read %zu bytes", cf->stream, bsize); log_debug("file %d read %zu bytes", cf->stream, bsize);
if (cf->error == 0 && !cf->closed) { if (cf->error == 0 && !cf->closed) {
@@ -846,10 +852,11 @@ file_read_data(struct client_files *files, struct imsg *imsg)
} else } else
file_fire_read(cf); file_fire_read(cf);
} }
return (0);
} }
/* Handle a read done message (server). */ /* Handle a read done message (server). */
void int
file_read_done(struct client_files *files, struct imsg *imsg) file_read_done(struct client_files *files, struct imsg *imsg)
{ {
struct msg_read_done *msg = imsg->data; struct msg_read_done *msg = imsg->data;
@@ -857,12 +864,13 @@ file_read_done(struct client_files *files, struct imsg *imsg)
struct client_file find, *cf; struct client_file find, *cf;
if (msglen != sizeof *msg) if (msglen != sizeof *msg)
fatalx("bad MSG_READ_DONE size"); return (-1);
find.stream = msg->stream; find.stream = msg->stream;
if ((cf = RB_FIND(client_files, files, &find)) == NULL) if ((cf = RB_FIND(client_files, files, &find)) == NULL)
return; return (0);
log_debug("file %d read done", cf->stream); log_debug("file %d read done", cf->stream);
cf->error = msg->error; cf->error = msg->error;
file_fire_done(cf); file_fire_done(cf);
return (0);
} }

View File

@@ -49,6 +49,7 @@ format_is_type(struct format_range *fr, struct style *sy)
case STYLE_RANGE_NONE: case STYLE_RANGE_NONE:
case STYLE_RANGE_LEFT: case STYLE_RANGE_LEFT:
case STYLE_RANGE_RIGHT: case STYLE_RANGE_RIGHT:
case STYLE_RANGE_CONTROL:
return (1); return (1);
case STYLE_RANGE_PANE: case STYLE_RANGE_PANE:
case STYLE_RANGE_WINDOW: case STYLE_RANGE_WINDOW:
@@ -1065,6 +1066,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
log_debug("%s: range user|%u at %u-%u", __func__, log_debug("%s: range user|%u at %u-%u", __func__,
sr->argument, sr->start, sr->end); sr->argument, sr->start, sr->end);
break; break;
case STYLE_RANGE_CONTROL:
log_debug("%s: range control|%u at %u-%u", __func__,
sr->argument, sr->start, sr->end);
break;
} }
format_free_range(&frs, fr); format_free_range(&frs, fr);
} }
@@ -1116,7 +1121,7 @@ format_width(const char *expanded)
/* /*
* Trim on the left, taking #[] into account. Note, we copy the whole set of * Trim on the left, taking #[] into account. Note, we copy the whole set of
* unescaped #s, but only add their escaped size to width. This is because the * unescaped #s, but only add their escaped size to width. This is because the
* format_draw function will actually do the escaping when it runs * format_draw function will actually do the escaping.
*/ */
char * char *
format_trim_left(const char *expanded, u_int limit) format_trim_left(const char *expanded, u_int limit)

654
format.c

File diff suppressed because it is too large Load Diff

83
fuzz/cmd-parse-fuzzer.c Normal file
View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 2026 David Korczynski <david@adalogics.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.
*/
/*
* Fuzz the tmux command parser (cmd_parse_from_buffer).
*
* This exercises:
* - cmd-parse.y (yacc grammar, lexer, command building)
* - cmd.c (command lookup and validation)
* - arguments.c (argument parsing and flag handling)
* - cmd-find.c (target resolution)
* - options.c (option name lookups during parsing)
*/
#include <stddef.h>
#include <string.h>
#include "tmux.h"
struct event_base *libevent;
int
LLVMFuzzerTestOneInput(const u_char *data, size_t size)
{
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
if (size > 2048 || size == 0)
return 0;
memset(&pi, 0, sizeof pi);
pi.flags = CMD_PARSE_QUIET;
pr = cmd_parse_from_buffer(data, size, &pi);
switch (pr->status) {
case CMD_PARSE_SUCCESS:
cmd_list_free(pr->cmdlist);
break;
case CMD_PARSE_ERROR:
free(pr->error);
break;
default:
break;
}
return 0;
}
int
LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv)
{
const struct options_table_entry *oe;
global_environ = environ_create();
global_options = options_create(NULL);
global_s_options = options_create(NULL);
global_w_options = options_create(NULL);
for (oe = options_table; oe->name != NULL; oe++) {
if (oe->scope & OPTIONS_TABLE_SERVER)
options_default(global_options, oe);
if (oe->scope & OPTIONS_TABLE_SESSION)
options_default(global_s_options, oe);
if (oe->scope & OPTIONS_TABLE_WINDOW)
options_default(global_w_options, oe);
}
libevent = osdep_event_init();
socket_path = xstrdup("dummy");
return 0;
}

133
fuzz/cmd-parse-fuzzer.dict Normal file
View File

@@ -0,0 +1,133 @@
# tmux command names
"set-option"
"bind-key"
"unbind-key"
"send-keys"
"new-session"
"new-window"
"split-window"
"select-window"
"select-pane"
"kill-pane"
"kill-window"
"kill-session"
"kill-server"
"list-sessions"
"list-windows"
"list-panes"
"list-keys"
"list-buffers"
"list-clients"
"list-commands"
"attach-session"
"detach-client"
"switch-client"
"rename-session"
"rename-window"
"resize-pane"
"resize-window"
"display-message"
"display-menu"
"display-popup"
"display-panes"
"copy-mode"
"paste-buffer"
"capture-pane"
"save-buffer"
"load-buffer"
"set-buffer"
"delete-buffer"
"show-buffer"
"choose-buffer"
"choose-tree"
"choose-client"
"if-shell"
"run-shell"
"source-file"
"command-prompt"
"confirm-before"
"pipe-pane"
"wait-for"
"set-environment"
"show-environment"
"set-hook"
"show-hooks"
"show-messages"
"show-options"
"set-window-option"
"show-window-options"
"clear-history"
"clock-mode"
"find-window"
"join-pane"
"move-pane"
"break-pane"
"swap-pane"
"swap-window"
"move-window"
"link-window"
"unlink-window"
"rotate-window"
"next-window"
"previous-window"
"last-window"
"last-pane"
"next-layout"
"previous-layout"
"select-layout"
"customize-mode"
"refresh-client"
"suspend-client"
"lock-client"
"lock-server"
"lock-session"
"respawn-pane"
"respawn-window"
"start-server"
"server-access"
"send-prefix"
"clear-prompt-history"
"show-prompt-history"
# Common flags and syntax
"-t"
"-s"
"-g"
"-w"
"-p"
"-a"
"-o"
"-q"
"-u"
"-F"
"-f"
"-b"
"-d"
"-e"
"-n"
"-r"
# Targets and option names
"status"
"default"
"mouse"
"on"
"off"
"vi"
"emacs"
"set -g"
"set -s"
"setw"
"bind"
"unbind"
# Syntax elements
"{"
"}"
";"
"#{"
"%if"
"%elif"
"%else"
"%endif"
"%hidden"

View File

@@ -0,0 +1,2 @@
[libfuzzer]
max_len = 2048

88
fuzz/format-fuzzer.c Normal file
View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2026 David Korczynski <david@adalogics.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.
*/
/*
* Fuzz the tmux format string expander (format_expand).
*
* This exercises:
* - format.c (format parsing, modifier chains, conditionals, math, regex)
* - colour.c (colour name and RGB parsing within formats)
* - utf8.c (UTF-8 width calculations in format padding)
*/
#include <stddef.h>
#include <string.h>
#include "tmux.h"
struct event_base *libevent;
int
LLVMFuzzerTestOneInput(const u_char *data, size_t size)
{
struct format_tree *ft;
char *buf, *expanded;
if (size > 2048 || size == 0)
return 0;
/* Null-terminate the input for format_expand. */
buf = malloc(size + 1);
if (buf == NULL)
return 0;
memcpy(buf, data, size);
buf[size] = '\0';
ft = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
format_add(ft, "session_name", "%s", "fuzz-session");
format_add(ft, "window_index", "%d", 0);
format_add(ft, "window_name", "%s", "fuzz-window");
format_add(ft, "pane_index", "%d", 0);
format_add(ft, "pane_id", "%s", "%%0");
format_add(ft, "host", "%s", "fuzzhost");
format_add(ft, "pane_width", "%d", 80);
format_add(ft, "pane_height", "%d", 25);
expanded = format_expand(ft, buf);
free(expanded);
format_free(ft);
free(buf);
return 0;
}
int
LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv)
{
const struct options_table_entry *oe;
global_environ = environ_create();
global_options = options_create(NULL);
global_s_options = options_create(NULL);
global_w_options = options_create(NULL);
for (oe = options_table; oe->name != NULL; oe++) {
if (oe->scope & OPTIONS_TABLE_SERVER)
options_default(global_options, oe);
if (oe->scope & OPTIONS_TABLE_SESSION)
options_default(global_s_options, oe);
if (oe->scope & OPTIONS_TABLE_WINDOW)
options_default(global_w_options, oe);
}
libevent = osdep_event_init();
socket_path = xstrdup("dummy");
return 0;
}

71
fuzz/format-fuzzer.dict Normal file
View File

@@ -0,0 +1,71 @@
# Format expansion syntax
"#{"
"}"
"#{?"
"#{=="
"#{!="
"#{<"
"#{>"
"#{m:"
"#{C:"
"#{s/"
"#{t:"
"#{T:"
"#{E:"
"#{S:"
"#{W:"
"#{P:"
"##"
# Common format modifiers
"b:"
"d:"
"q:"
"l:"
"e:"
"t:"
"p:"
"w:"
"n:"
"a:"
"=:"
"||"
"&&"
","
"#{e|"
"#{p-1:"
"#{=/10/...:"
# Format variables
"session_name"
"window_index"
"window_name"
"pane_index"
"pane_id"
"host"
"pane_width"
"pane_height"
"pane_current_command"
"pane_pid"
"session_id"
"window_id"
"client_name"
"version"
"line"
# Conditionals
"#{?pane_active,"
",}"
"#{?#{==:"
# Math and comparison
"#{e|+:"
"#{e|-:"
"#{e|*:"
"#{e|/:"
"#{e|%:"
# Nested
"#{="
"#{"
"#{l:"

View File

@@ -0,0 +1,2 @@
[libfuzzer]
max_len = 2048

View File

@@ -44,7 +44,7 @@ LLVMFuzzerTestOneInput(const u_char *data, size_t size)
w = window_create(PANE_WIDTH, PANE_HEIGHT, 0, 0); w = window_create(PANE_WIDTH, PANE_HEIGHT, 0, 0);
wp = window_add_pane(w, NULL, 0, 0); wp = window_add_pane(w, NULL, 0, 0);
bufferevent_pair_new(libevent, BEV_OPT_CLOSE_ON_FREE, vpty); bufferevent_pair_new(libevent, BEV_OPT_CLOSE_ON_FREE, vpty);
wp->ictx = input_init(wp, vpty[0], NULL); wp->ictx = input_init(wp, vpty[0], NULL, NULL);
window_add_ref(w, __func__); window_add_ref(w, __func__);
wp->fd = open("/dev/null", O_WRONLY); wp->fd = open("/dev/null", O_WRONLY);

79
fuzz/style-fuzzer.c Normal file
View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2026 David Korczynski <david@adalogics.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.
*/
/*
* Fuzz the tmux style parser (style_parse).
*
* This exercises:
* - style.c (style string parsing, alignment, ranges)
* - colour.c (colour name, RGB, and indexed colour parsing)
*/
#include <stddef.h>
#include <string.h>
#include "tmux.h"
struct event_base *libevent;
int
LLVMFuzzerTestOneInput(const u_char *data, size_t size)
{
struct style sy;
struct grid_cell gc;
char *buf;
if (size > 512 || size == 0)
return 0;
/* Null-terminate the input for style_parse. */
buf = malloc(size + 1);
if (buf == NULL)
return 0;
memcpy(buf, data, size);
buf[size] = '\0';
memset(&gc, 0, sizeof gc);
style_set(&sy, &gc);
style_parse(&sy, &gc, buf);
free(buf);
return 0;
}
int
LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv)
{
const struct options_table_entry *oe;
global_environ = environ_create();
global_options = options_create(NULL);
global_s_options = options_create(NULL);
global_w_options = options_create(NULL);
for (oe = options_table; oe->name != NULL; oe++) {
if (oe->scope & OPTIONS_TABLE_SERVER)
options_default(global_options, oe);
if (oe->scope & OPTIONS_TABLE_SESSION)
options_default(global_s_options, oe);
if (oe->scope & OPTIONS_TABLE_WINDOW)
options_default(global_w_options, oe);
}
libevent = osdep_event_init();
socket_path = xstrdup("dummy");
return 0;
}

73
fuzz/style-fuzzer.dict Normal file
View File

@@ -0,0 +1,73 @@
# Style attributes
"default"
"ignore"
"nodefaults"
"bright"
"bold"
"dim"
"underscore"
"blink"
"reverse"
"hidden"
"italics"
"overline"
"strikethrough"
"double-underscore"
"curly-underscore"
"dotted-underscore"
"dashed-underscore"
"nobright"
"nobold"
"nodim"
"nounderscore"
"noblink"
"noreverse"
"nohidden"
"noitalics"
"nooverline"
"nostrikethrough"
# Colours
"fg="
"bg="
"us="
"fill="
"colour0"
"colour255"
"red"
"green"
"blue"
"yellow"
"cyan"
"magenta"
"white"
"black"
# RGB and hex
"#ff0000"
"#00ff00"
"#0000ff"
# Alignment and ranges
"align=left"
"align=centre"
"align=right"
"align=absolute-centre"
"list=on"
"list=focus"
"list=left-marker"
"list=right-marker"
"range=left"
"range=right"
"range=pane"
"range=window"
"range=session"
"range=user"
# Width and padding
"width="
"pad="
# Delimiters
","
" "

View File

@@ -0,0 +1,2 @@
[libfuzzer]
max_len = 512

View File

@@ -45,15 +45,20 @@ grid_reader_line_length(struct grid_reader *gr)
/* Move cursor forward one position. */ /* Move cursor forward one position. */
void void
grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all) grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all, int onemore)
{ {
u_int px; u_int px;
struct grid_cell gc; struct grid_cell gc;
if (all) if (all)
px = gr->gd->sx; px = gr->gd->sx;
else else if (onemore)
px = grid_reader_line_length(gr); px = grid_reader_line_length(gr);
else {
px = grid_reader_line_length(gr);
if (px != 0)
px--;
}
if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) { if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) {
grid_reader_cursor_start_of_line(gr, 0); grid_reader_cursor_start_of_line(gr, 0);

View File

@@ -82,7 +82,7 @@ grid_view_clear_history(struct grid *gd, u_int bg)
/* Scroll the lines into the history. */ /* Scroll the lines into the history. */
for (yy = 0; yy < last; yy++) { for (yy = 0; yy < last; yy++) {
grid_collect_history(gd); grid_collect_history(gd, 0);
grid_scroll_history(gd, bg); grid_scroll_history(gd, bg);
} }
if (last < gd->sy) if (last < gd->sy)
@@ -107,7 +107,7 @@ grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower,
u_int bg) u_int bg)
{ {
if (gd->flags & GRID_HISTORY) { if (gd->flags & GRID_HISTORY) {
grid_collect_history(gd); grid_collect_history(gd, 0);
if (rupper == 0 && rlower == gd->sy - 1) if (rupper == 0 && rlower == gd->sy - 1)
grid_scroll_history(gd, bg); grid_scroll_history(gd, bg);
else { else {

61
grid.c
View File

@@ -125,6 +125,8 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
else if (gce->offset >= gl->extdsize) else if (gce->offset >= gl->extdsize)
fatalx("offset too big"); fatalx("offset too big");
gl->flags |= GRID_LINE_EXTENDED; gl->flags |= GRID_LINE_EXTENDED;
if (gc->link != 0)
gl->flags |= GRID_LINE_HYPERLINK;
if (gc->flags & GRID_FLAG_TAB) if (gc->flags & GRID_FLAG_TAB)
uc = gc->data.width; uc = gc->data.width;
@@ -200,14 +202,22 @@ grid_adjust_lines(struct grid *gd, u_int lines)
/* Copy default into a cell. */ /* Copy default into a cell. */
static void static void
grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg) grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg, int moved)
{ {
struct grid_line *gl = &gd->linedata[py]; struct grid_line *gl = &gd->linedata[py];
struct grid_cell_entry *gce = &gl->celldata[px]; struct grid_cell_entry *gce = &gl->celldata[px];
struct grid_extd_entry *gee; struct grid_extd_entry *gee;
u_int old_offset = gce->offset;
int had_extd = (gce->flags & GRID_FLAG_EXTENDED);
memcpy(gce, &grid_cleared_entry, sizeof *gce); memcpy(gce, &grid_cleared_entry, sizeof *gce);
if (bg != 8) { if (!moved && had_extd && old_offset < gl->extdsize) {
gce->flags |= GRID_FLAG_EXTENDED;
gce->offset = old_offset;
gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
if (bg != 8)
gee->bg = bg;
} else if (bg != 8) {
if (bg & COLOUR_FLAG_RGB) { if (bg & COLOUR_FLAG_RGB) {
grid_get_extended_cell(gl, gce, gce->flags); grid_get_extended_cell(gl, gce, gce->flags);
gee = grid_extended_cell(gl, gce, &grid_cleared_cell); gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
@@ -235,7 +245,7 @@ grid_check_y(struct grid *gd, const char *from, u_int py)
int int
grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
{ {
int flags1 = gc1->flags, flags2 = gc2->flags;; int flags1 = gc1->flags, flags2 = gc2->flags;
if (gc1->fg != gc2->fg || gc1->bg != gc2->bg) if (gc1->fg != gc2->fg || gc1->bg != gc2->bg)
return (0); return (0);
@@ -277,9 +287,8 @@ static void
grid_free_line(struct grid *gd, u_int py) grid_free_line(struct grid *gd, u_int py)
{ {
free(gd->linedata[py].celldata); free(gd->linedata[py].celldata);
gd->linedata[py].celldata = NULL;
free(gd->linedata[py].extddata); free(gd->linedata[py].extddata);
gd->linedata[py].extddata = NULL; memset(&gd->linedata[py], 0, sizeof gd->linedata[py]);
} }
/* Free several lines. */ /* Free several lines. */
@@ -324,9 +333,7 @@ void
grid_destroy(struct grid *gd) grid_destroy(struct grid *gd)
{ {
grid_free_lines(gd, 0, gd->hsize + gd->sy); grid_free_lines(gd, 0, gd->hsize + gd->sy);
free(gd->linedata); free(gd->linedata);
free(gd); free(gd);
} }
@@ -361,9 +368,13 @@ grid_compare(struct grid *ga, struct grid *gb)
static void static void
grid_trim_history(struct grid *gd, u_int ny) grid_trim_history(struct grid *gd, u_int ny)
{ {
u_int remaining;
grid_free_lines(gd, 0, ny); grid_free_lines(gd, 0, ny);
remaining = gd->hsize + gd->sy - ny;
memmove(&gd->linedata[0], &gd->linedata[ny], memmove(&gd->linedata[0], &gd->linedata[ny],
(gd->hsize + gd->sy - ny) * (sizeof *gd->linedata)); remaining * (sizeof *gd->linedata));
memset(&gd->linedata[remaining], 0, ny * (sizeof *gd->linedata));
} }
/* /*
@@ -371,14 +382,17 @@ grid_trim_history(struct grid *gd, u_int ny)
* and shift up. * and shift up.
*/ */
void void
grid_collect_history(struct grid *gd) grid_collect_history(struct grid *gd, int all)
{ {
u_int ny; u_int ny;
if (gd->hsize == 0 || gd->hsize < gd->hlimit) if (gd->hsize == 0 || gd->hsize < gd->hlimit)
return; return;
ny = gd->hlimit / 10; if (all)
ny = gd->hsize - gd->hlimit;
else
ny = gd->hlimit / 10;
if (ny < 1) if (ny < 1)
ny = 1; ny = 1;
if (ny > gd->hsize) if (ny > gd->hsize)
@@ -399,12 +413,14 @@ grid_collect_history(struct grid *gd)
void void
grid_remove_history(struct grid *gd, u_int ny) grid_remove_history(struct grid *gd, u_int ny)
{ {
u_int yy; u_int yy, start;
if (ny > gd->hsize) if (ny > gd->hsize)
return; return;
start = gd->hsize + gd->sy - ny;
for (yy = 0; yy < ny; yy++) for (yy = 0; yy < ny; yy++)
grid_free_line(gd, gd->hsize + gd->sy - 1 - yy); grid_free_line(gd, start + yy);
memset(&gd->linedata[start], 0, ny * sizeof *gd->linedata);
gd->hsize -= ny; gd->hsize -= ny;
} }
@@ -493,9 +509,14 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
else if (gd->sx > sx) else if (gd->sx > sx)
sx = gd->sx; sx = gd->sx;
gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); gl->celldata = xreallocarray(gl->celldata, sx,
sizeof *gl->celldata);
if (gl->cellsize < sx) {
memset(gl->celldata + gl->cellsize, 0,
(sx - gl->cellsize) * sizeof *gl->celldata);
}
for (xx = gl->cellsize; xx < sx; xx++) for (xx = gl->cellsize; xx < sx; xx++)
grid_clear_cell(gd, xx, py, bg); grid_clear_cell(gd, xx, py, bg, 0);
gl->cellsize = sx; gl->cellsize = sx;
} }
@@ -663,7 +684,7 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
grid_expand_line(gd, yy, px + ox, 8); /* default bg first */ grid_expand_line(gd, yy, px + ox, 8); /* default bg first */
for (xx = px; xx < px + ox; xx++) for (xx = px; xx < px + ox; xx++)
grid_clear_cell(gd, xx, yy, bg); grid_clear_cell(gd, xx, yy, bg, 0);
} }
} }
@@ -757,7 +778,7 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
for (xx = px; xx < px + nx; xx++) { for (xx = px; xx < px + nx; xx++) {
if (xx >= dx && xx < dx + nx) if (xx >= dx && xx < dx + nx)
continue; continue;
grid_clear_cell(gd, xx, py, bg); grid_clear_cell(gd, xx, py, bg, 1);
} }
} }
@@ -1082,18 +1103,22 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
off = 0; off = 0;
gl = grid_peek_line(gd, py); gl = grid_peek_line(gd, py);
if (gl == NULL) {
buf[0] = '\0';
return (buf);
}
if (flags & GRID_STRING_EMPTY_CELLS) if (flags & GRID_STRING_EMPTY_CELLS)
end = gl->cellsize; end = gl->cellsize;
else else
end = gl->cellused; end = gl->cellused;
for (xx = px; xx < px + nx; xx++) { for (xx = px; xx < px + nx; xx++) {
if (gl == NULL || xx >= end) if (xx >= end)
break; break;
grid_get_cell(gd, xx, py, &gc); grid_get_cell(gd, xx, py, &gc);
if (gc.flags & GRID_FLAG_PADDING) if (gc.flags & GRID_FLAG_PADDING)
continue; continue;
if (flags & GRID_STRING_WITH_SEQUENCES) { if (lastgc != NULL && (flags & GRID_STRING_WITH_SEQUENCES)) {
grid_string_cells_code(*lastgc, &gc, code, sizeof code, grid_string_cells_code(*lastgc, &gc, code, sizeof code,
flags, s, &has_link); flags, s, &has_link);
codelen = strlen(code); codelen = strlen(code);

View File

@@ -122,19 +122,13 @@ sixel_set_pixel(struct sixel_image *si, u_int x, u_int y, u_int c)
static int static int
sixel_parse_write(struct sixel_image *si, u_int ch) sixel_parse_write(struct sixel_image *si, u_int ch)
{ {
struct sixel_line *sl;
u_int i; u_int i;
if (sixel_parse_expand_lines(si, si->dy + 6) != 0)
return (1);
sl = &si->lines[si->dy];
for (i = 0; i < 6; i++) { for (i = 0; i < 6; i++) {
if (sixel_parse_expand_line(si, sl, si->dx + 1) != 0) if (ch & (1 << i)) {
return (1); if (sixel_set_pixel(si, si->dx, si->dy + i, si->dc))
if (ch & (1 << i)) return (1);
sl->data[si->dx] = si->dc; }
sl++;
} }
return (0); return (0);
} }
@@ -357,7 +351,7 @@ sixel_parse(const char *buf, size_t len, u_int p2, u_int xpixel, u_int ypixel)
return (si); return (si);
bad: bad:
free(si); sixel_free(si);
return (NULL); return (NULL);
} }
@@ -455,12 +449,12 @@ sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
new->p2 = si->p2; new->p2 = si->p2;
new->set_ra = si->set_ra; new->set_ra = si->set_ra;
/* clamp to slice end */ /* subtract offset */
new->ra_x = si->ra_x < psx ? si->ra_x : psx;
new->ra_y = si->ra_y < psy ? si->ra_y : psy;
/* subtract slice origin */
new->ra_x = new->ra_x > pox ? new->ra_x - pox : 0; new->ra_x = new->ra_x > pox ? new->ra_x - pox : 0;
new->ra_y = new->ra_y > poy ? new->ra_y - poy : 0; new->ra_y = new->ra_y > poy ? new->ra_y - poy : 0;
/* clamp to size */
new->ra_x = si->ra_x < psx ? si->ra_x : psx;
new->ra_y = si->ra_y < psy ? si->ra_y : psy;
/* resize */ /* resize */
new->ra_x = new->ra_x * xpixel / si->xpixel; new->ra_x = new->ra_x * xpixel / si->xpixel;
new->ra_y = new->ra_y * ypixel / si->ypixel; new->ra_y = new->ra_y * ypixel / si->ypixel;
@@ -487,9 +481,9 @@ static void
sixel_print_add(char **buf, size_t *len, size_t *used, const char *s, sixel_print_add(char **buf, size_t *len, size_t *used, const char *s,
size_t slen) size_t slen)
{ {
if (*used + slen >= *len + 1) { while (*used + slen >= *len + 1) {
*buf = xreallocarray(*buf, 2, *len);
(*len) *= 2; (*len) *= 2;
*buf = xrealloc(*buf, *len);
} }
memcpy(*buf + *used, s, slen); memcpy(*buf + *used, s, slen);
(*used) += slen; (*used) += slen;

62
image.c
View File

@@ -25,16 +25,40 @@
static struct images all_images = TAILQ_HEAD_INITIALIZER(all_images); static struct images all_images = TAILQ_HEAD_INITIALIZER(all_images);
static u_int all_images_count; static u_int all_images_count;
#define MAX_IMAGE_COUNT 20
static void printflike(3, 4)
image_log(struct image *im, const char* from, const char* fmt, ...)
{
va_list ap;
char s[128];
if (log_get_level() == 0)
return;
if (fmt == NULL) {
log_debug("%s: %p (%ux%u %u,%u)", from, im, im->sx, im->sy,
im->px, im->py);
return;
}
va_start(ap, fmt);
vsnprintf(s, sizeof s, fmt, ap);
va_end(ap);
log_debug("%s: %p (%ux%u %u,%u): %s", from, im, im->sx, im->sy,
im->px, im->py, s);
}
static void static void
image_free(struct image *im) image_free(struct image *im)
{ {
struct screen *s = im->s; image_log(im, __func__, NULL);
TAILQ_REMOVE(&all_images, im, all_entry); TAILQ_REMOVE(&all_images, im, all_entry);
all_images_count--; all_images_count--;
TAILQ_REMOVE(&s->images, im, entry); TAILQ_REMOVE(im->list, im, entry);
sixel_free(im->data); sixel_free(im->data);
free(im->fallback); free(im->fallback);
free(im); free(im);
@@ -46,6 +70,8 @@ image_free_all(struct screen *s)
struct image *im, *im1; struct image *im, *im1;
int redraw = !TAILQ_EMPTY(&s->images); int redraw = !TAILQ_EMPTY(&s->images);
if (redraw)
log_debug ("%s", __func__);
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) TAILQ_FOREACH_SAFE(im, &s->images, entry, im1)
image_free(im); image_free(im);
return (redraw); return (redraw);
@@ -108,10 +134,12 @@ image_store(struct screen *s, struct sixel_image *si)
image_fallback(&im->fallback, im->sx, im->sy); image_fallback(&im->fallback, im->sx, im->sy);
TAILQ_INSERT_TAIL(&s->images, im, entry); image_log(im, __func__, NULL);
im->list = &s->images;
TAILQ_INSERT_TAIL(im->list, im, entry);
TAILQ_INSERT_TAIL(&all_images, im, all_entry); TAILQ_INSERT_TAIL(&all_images, im, all_entry);
if (++all_images_count == 10/*XXX*/) if (++all_images_count == MAX_IMAGE_COUNT)
image_free(TAILQ_FIRST(&all_images)); image_free(TAILQ_FIRST(&all_images));
return (im); return (im);
@@ -121,10 +149,12 @@ int
image_check_line(struct screen *s, u_int py, u_int ny) image_check_line(struct screen *s, u_int py, u_int ny)
{ {
struct image *im, *im1; struct image *im, *im1;
int redraw = 0; int redraw = 0, in;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) { TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (py + ny > im->py && py < im->py + im->sy) { in = (py + ny > im->py && py < im->py + im->sy);
image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in);
if (in) {
image_free(im); image_free(im);
redraw = 1; redraw = 1;
} }
@@ -136,15 +166,18 @@ int
image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny) image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny)
{ {
struct image *im, *im1; struct image *im, *im1;
int redraw = 0; int redraw = 0, in;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) { TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (py + ny <= im->py || py >= im->py + im->sy) in = (py < im->py + im->sy &&
continue; py + ny > im->py &&
if (px + nx <= im->px || px >= im->px + im->sx) px < im->px + im->sx &&
continue; px + nx > im->px);
image_free(im); image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in);
redraw = 1; if (in) {
image_free(im);
redraw = 1;
}
} }
return (redraw); return (redraw);
} }
@@ -159,17 +192,20 @@ image_scroll_up(struct screen *s, u_int lines)
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) { TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (im->py >= lines) { if (im->py >= lines) {
image_log(im, __func__, "1, lines=%u", lines);
im->py -= lines; im->py -= lines;
redraw = 1; redraw = 1;
continue; continue;
} }
if (im->py + im->sy <= lines) { if (im->py + im->sy <= lines) {
image_log(im, __func__, "2, lines=%u", lines);
image_free(im); image_free(im);
redraw = 1; redraw = 1;
continue; continue;
} }
sx = im->sx; sx = im->sx;
sy = (im->py + im->sy) - lines; sy = (im->py + im->sy) - lines;
image_log(im, __func__, "3, lines=%u, sy=%u", lines, sy);
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1); new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
sixel_free(im->data); sixel_free(im->data);

View File

@@ -313,12 +313,6 @@ static struct input_key_entry input_key_defaults[] = {
{ .key = KEYC_DC|KEYC_BUILD_MODIFIERS, { .key = KEYC_DC|KEYC_BUILD_MODIFIERS,
.data = "\033[3;_~" .data = "\033[3;_~"
}, },
{ .key = KEYC_REPORT_DARK_THEME,
.data = "\033[?997;1n"
},
{ .key = KEYC_REPORT_LIGHT_THEME,
.data = "\033[?997;2n"
},
}; };
static const key_code input_key_modifiers[] = { static const key_code input_key_modifiers[] = {
0, 0,
@@ -679,8 +673,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
} }
/* Ignore internal function key codes. */ /* Ignore internal function key codes. */
if ((key >= KEYC_BASE && key < KEYC_BASE_END) || if (KEYC_IS_USER(key) || KEYC_IS_SPECIAL(key) || KEYC_IS_MOUSE(key)) {
(key >= KEYC_USER && key < KEYC_USER_END)) {
log_debug("%s: ignoring key 0x%llx", __func__, key); log_debug("%s: ignoring key 0x%llx", __func__, key);
return (0); return (0);
} }

433
input.c
View File

@@ -63,8 +63,8 @@ struct input_request {
struct input_ctx *ictx; struct input_ctx *ictx;
enum input_request_type type; enum input_request_type type;
time_t t; uint64_t t;
enum input_end_type end; enum input_end_type end;
int idx; int idx;
void *data; void *data;
@@ -72,7 +72,7 @@ struct input_request {
TAILQ_ENTRY(input_request) entry; TAILQ_ENTRY(input_request) entry;
TAILQ_ENTRY(input_request) centry; TAILQ_ENTRY(input_request) centry;
}; };
#define INPUT_REQUEST_TIMEOUT 2 #define INPUT_REQUEST_TIMEOUT 500
/* Input parser cell. */ /* Input parser cell. */
struct input_cell { struct input_cell {
@@ -101,6 +101,7 @@ struct input_ctx {
struct bufferevent *event; struct bufferevent *event;
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
struct colour_palette *palette; struct colour_palette *palette;
struct client *c;
struct input_cell cell; struct input_cell cell;
struct input_cell old_cell; struct input_cell old_cell;
@@ -148,7 +149,7 @@ struct input_ctx {
/* Helper functions. */ /* Helper functions. */
struct input_transition; struct input_transition;
static void input_request_timer_callback(int, short, void *); static void input_request_timer_callback(int, short, void *);
static void input_start_request_timer(struct input_ctx *); static void input_start_request_timer(struct input_ctx *);
static struct input_request *input_make_request(struct input_ctx *, static struct input_request *input_make_request(struct input_ctx *,
enum input_request_type); enum input_request_type);
@@ -163,6 +164,7 @@ static void input_reset_cell(struct input_ctx *);
static void input_report_current_theme(struct input_ctx *); static void input_report_current_theme(struct input_ctx *);
static void input_osc_4(struct input_ctx *, const char *); static void input_osc_4(struct input_ctx *, const char *);
static void input_osc_8(struct input_ctx *, const char *); static void input_osc_8(struct input_ctx *, const char *);
static void input_osc_9(struct input_ctx *, const char *);
static void input_osc_10(struct input_ctx *, const char *); static void input_osc_10(struct input_ctx *, const char *);
static void input_osc_11(struct input_ctx *, const char *); static void input_osc_11(struct input_ctx *, const char *);
static void input_osc_12(struct input_ctx *, const char *); static void input_osc_12(struct input_ctx *, const char *);
@@ -279,6 +281,7 @@ enum input_csi_type {
INPUT_CSI_IL, INPUT_CSI_IL,
INPUT_CSI_MODOFF, INPUT_CSI_MODOFF,
INPUT_CSI_MODSET, INPUT_CSI_MODSET,
INPUT_CSI_QUERY,
INPUT_CSI_QUERY_PRIVATE, INPUT_CSI_QUERY_PRIVATE,
INPUT_CSI_RCP, INPUT_CSI_RCP,
INPUT_CSI_REP, INPUT_CSI_REP,
@@ -334,6 +337,7 @@ static const struct input_table_entry input_csi_table[] = {
{ 'n', "", INPUT_CSI_DSR }, { 'n', "", INPUT_CSI_DSR },
{ 'n', ">", INPUT_CSI_MODOFF }, { 'n', ">", INPUT_CSI_MODOFF },
{ 'n', "?", INPUT_CSI_DSR_PRIVATE }, { 'n', "?", INPUT_CSI_DSR_PRIVATE },
{ 'p', "$", INPUT_CSI_QUERY },
{ 'p', "?$", INPUT_CSI_QUERY_PRIVATE }, { 'p', "?$", INPUT_CSI_QUERY_PRIVATE },
{ 'q', " ", INPUT_CSI_DECSCUSR }, { 'q', " ", INPUT_CSI_DECSCUSR },
{ 'q', ">", INPUT_CSI_XDA }, { 'q', ">", INPUT_CSI_XDA },
@@ -854,7 +858,7 @@ input_restore_state(struct input_ctx *ictx)
/* Initialise input parser. */ /* Initialise input parser. */
struct input_ctx * struct input_ctx *
input_init(struct window_pane *wp, struct bufferevent *bev, input_init(struct window_pane *wp, struct bufferevent *bev,
struct colour_palette *palette) struct colour_palette *palette, struct client *c)
{ {
struct input_ctx *ictx; struct input_ctx *ictx;
@@ -862,6 +866,7 @@ input_init(struct window_pane *wp, struct bufferevent *bev,
ictx->wp = wp; ictx->wp = wp;
ictx->event = bev; ictx->event = bev;
ictx->palette = palette; ictx->palette = palette;
ictx->c = c;
ictx->input_space = INPUT_BUF_START; ictx->input_space = INPUT_BUF_START;
ictx->input_buf = xmalloc(INPUT_BUF_START); ictx->input_buf = xmalloc(INPUT_BUF_START);
@@ -898,6 +903,8 @@ input_free(struct input_ctx *ictx)
evbuffer_free(ictx->since_ground); evbuffer_free(ictx->since_ground);
event_del(&ictx->ground_timer); event_del(&ictx->ground_timer);
screen_write_stop_sync(ictx->wp);
free(ictx); free(ictx);
} }
@@ -945,7 +952,7 @@ input_set_state(struct input_ctx *ictx, const struct input_transition *itr)
/* Parse data. */ /* Parse data. */
static void static void
input_parse(struct input_ctx *ictx, u_char *buf, size_t len) input_parse(struct input_ctx *ictx, const u_char *buf, size_t len)
{ {
struct screen_write_ctx *sctx = &ictx->ctx; struct screen_write_ctx *sctx = &ictx->ctx;
const struct input_state *state = NULL; const struct input_state *state = NULL;
@@ -1016,7 +1023,7 @@ input_parse_pane(struct window_pane *wp)
/* Parse given input. */ /* Parse given input. */
void void
input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) input_parse_buffer(struct window_pane *wp, const u_char *buf, size_t len)
{ {
struct input_ctx *ictx = wp->ictx; struct input_ctx *ictx = wp->ictx;
struct screen_write_ctx *sctx = &ictx->ctx; struct screen_write_ctx *sctx = &ictx->ctx;
@@ -1047,7 +1054,7 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len)
/* Parse given input for screen. */ /* Parse given input for screen. */
void void
input_parse_screen(struct input_ctx *ictx, struct screen *s, input_parse_screen(struct input_ctx *ictx, struct screen *s,
screen_write_init_ctx_cb cb, void *arg, u_char *buf, size_t len) screen_write_init_ctx_cb cb, void *arg, const u_char *buf, size_t len)
{ {
struct screen_write_ctx *sctx = &ictx->ctx; struct screen_write_ctx *sctx = &ictx->ctx;
@@ -1135,11 +1142,9 @@ input_get(struct input_ctx *ictx, u_int validx, int minval, int defval)
static void static void
input_send_reply(struct input_ctx *ictx, const char *reply) input_send_reply(struct input_ctx *ictx, const char *reply)
{ {
struct bufferevent *bev = ictx->event; if (ictx->event != NULL) {
if (bev != NULL) {
log_debug("%s: %s", __func__, reply); log_debug("%s: %s", __func__, reply);
bufferevent_write(bev, reply, strlen(reply)); bufferevent_write(ictx->event, reply, strlen(reply));
} }
} }
@@ -1605,8 +1610,34 @@ input_csi_dispatch(struct input_ctx *ictx)
break; break;
} }
break; break;
case INPUT_CSI_QUERY:
m = input_get(ictx, 0, 0, 0);
switch (m) {
case 4: /* IRM */
n = (s->mode & MODE_INSERT) ? 1 : 2;
break;
default:
n = 0;
break;
}
if (m > 0)
input_reply(ictx, 1, "\033[%d;%d$y", m, n);
break;
case INPUT_CSI_QUERY_PRIVATE: case INPUT_CSI_QUERY_PRIVATE:
switch (input_get(ictx, 0, 0, 0)) { m = input_get(ictx, 0, 0, 0);
switch (m) {
case 1: /* DECCKM */
n = (s->mode & MODE_KCURSOR) ? 1 : 2;
break;
case 3: /* DECCOLM: always reset */
n = 4;
break;
case 6: /* DECOM */
n = (s->mode & MODE_ORIGIN) ? 1 : 2;
break;
case 7: /* DECAWM */
n = (s->mode & MODE_WRAP) ? 1 : 2;
break;
case 12: /* cursor blink: 1 = blink, 2 = steady */ case 12: /* cursor blink: 1 = blink, 2 = steady */
if (s->cstyle != SCREEN_CURSOR_DEFAULT || if (s->cstyle != SCREEN_CURSOR_DEFAULT ||
s->mode & MODE_CURSOR_BLINKING_SET) s->mode & MODE_CURSOR_BLINKING_SET)
@@ -1615,30 +1646,54 @@ input_csi_dispatch(struct input_ctx *ictx)
if (ictx->wp != NULL) if (ictx->wp != NULL)
oo = ictx->wp->options; oo = ictx->wp->options;
else else
oo = global_options; oo = global_w_options;
p = options_get_number(oo, "cursor-style"); p = options_get_number(oo, "cursor-style");
/* blink for 1,3,5; steady for 0,2,4,6 */ /* blink for 1,3,5; steady for 0,2,4,6 */
n = (p == 1 || p == 3 || p == 5) ? 1 : 2; n = (p == 1 || p == 3 || p == 5) ? 1 : 2;
} }
input_reply(ictx, 1, "\033[?12;%d$y", n);
break; break;
case 2004: /* bracketed paste */ case 25: /* DECTCEM */
n = (s->mode & MODE_BRACKETPASTE) ? 1 : 2; n = (s->mode & MODE_CURSOR) ? 1 : 2;
input_reply(ictx, 1, "\033[?2004;%d$y", n);
break; break;
case 1004: /* focus reporting */ case 47:
case 1047:
case 1049: /* alternate screen */
n = SCREEN_IS_ALTERNATE(s) ? 1 : 2;
break;
case 1000: /* mouse: normal tracking */
n = (s->mode & MODE_MOUSE_STANDARD) ? 1 : 2;
break;
case 1002: /* mouse: button-event tracking */
n = (s->mode & MODE_MOUSE_BUTTON) ? 1 : 2;
break;
case 1003: /* mouse: any-event tracking */
n = (s->mode & MODE_MOUSE_ALL) ? 1 : 2;
break;
case 1004: /* focus reporting */
n = (s->mode & MODE_FOCUSON) ? 1 : 2; n = (s->mode & MODE_FOCUSON) ? 1 : 2;
input_reply(ictx, 1, "\033[?1004;%d$y", n);
break; break;
case 1006: /* SGR mouse */ case 1005: /* mouse: UTF-8 */
n = (s->mode & MODE_MOUSE_UTF8) ? 1 : 2;
break;
case 1006: /* mouse: SGR */
n = (s->mode & MODE_MOUSE_SGR) ? 1 : 2; n = (s->mode & MODE_MOUSE_SGR) ? 1 : 2;
input_reply(ictx, 1, "\033[?1006;%d$y", n);
break; break;
case 2031: case 2004: /* bracketed paste */
input_reply(ictx, 1, "\033[?2031;2$y"); n = (s->mode & MODE_BRACKETPASTE) ? 1 : 2;
break;
case 2026: /* synchronized output */
n = (s->mode & MODE_SYNC) ? 1 : 2;
break;
case 2031: /* theme update notifications */
n = (s->mode & MODE_THEME_UPDATES) ? 1 : 2;
break;
default:
n = 0;
break; break;
} }
if (m > 0)
input_reply(ictx, 1, "\033[?%d;%d$y", m, n);
break; break;
case INPUT_CSI_DSR: case INPUT_CSI_DSR:
switch (input_get(ictx, 0, 0, 0)) { switch (input_get(ictx, 0, 0, 0)) {
@@ -1898,8 +1953,15 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
case 2004: case 2004:
screen_write_mode_clear(sctx, MODE_BRACKETPASTE); screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
break; break;
case 2026:
screen_write_stop_sync(ictx->wp);
if (ictx->wp != NULL)
ictx->wp->flags |= PANE_REDRAW;
break;
case 2031: case 2031:
screen_write_mode_clear(sctx, MODE_THEME_UPDATES); screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
if (ictx->wp != NULL)
ictx->wp->flags &= ~PANE_THEMECHANGED;
break; break;
default: default:
log_debug("%s: unknown '%c'", __func__, ictx->ch); log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -1998,6 +2060,13 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
break; break;
case 2031: case 2031:
screen_write_mode_set(sctx, MODE_THEME_UPDATES); screen_write_mode_set(sctx, MODE_THEME_UPDATES);
if (ictx->wp != NULL) {
ictx->wp->last_theme = window_pane_get_theme(ictx->wp);
ictx->wp->flags &= ~PANE_THEMECHANGED;
}
break;
case 2026:
screen_write_start_sync(ictx->wp);
break; break;
default: default:
log_debug("%s: unknown '%c'", __func__, ictx->ch); log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -2502,7 +2571,7 @@ input_handle_decrqss(struct input_ctx *ictx)
if (wp != NULL) if (wp != NULL)
oo = wp->options; oo = wp->options;
else else
oo = global_options; oo = global_w_options;
opt_ps = options_get_number(oo, "cursor-style"); opt_ps = options_get_number(oo, "cursor-style");
/* Sanity clamp: valid Ps are 0..6 per DECSCUSR. */ /* Sanity clamp: valid Ps are 0..6 per DECSCUSR. */
@@ -2542,8 +2611,9 @@ input_dcs_dispatch(struct input_ctx *ictx)
#endif #endif
if (wp == NULL) if (wp == NULL)
return (0); oo = global_w_options;
oo = wp->options; else
oo = wp->options;
if (ictx->flags & INPUT_DISCARD) { if (ictx->flags & INPUT_DISCARD) {
log_debug("%s: %zu bytes (discard)", __func__, len); log_debug("%s: %zu bytes (discard)", __func__, len);
@@ -2551,8 +2621,8 @@ input_dcs_dispatch(struct input_ctx *ictx)
} }
#ifdef ENABLE_SIXEL #ifdef ENABLE_SIXEL
w = wp->window; if (wp != NULL && buf[0] == 'q' && ictx->interm_len == 0) {
if (buf[0] == 'q' && ictx->interm_len == 0) { w = wp->window;
if (input_split(ictx) != 0) if (input_split(ictx) != 0)
return (0); return (0);
p2 = input_get(ictx, 1, 0, 0); p2 = input_get(ictx, 1, 0, 0);
@@ -2631,7 +2701,7 @@ input_exit_osc(struct input_ctx *ictx)
case 2: case 2:
if (wp != NULL && if (wp != NULL &&
options_get_number(wp->options, "allow-set-title") && options_get_number(wp->options, "allow-set-title") &&
screen_set_title(sctx->s, p)) { screen_set_title(sctx->s, p, 1)) {
notify_pane("pane-title-changed", wp); notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window); server_redraw_window_borders(wp->window);
server_status_window(wp->window); server_status_window(wp->window);
@@ -2641,17 +2711,17 @@ input_exit_osc(struct input_ctx *ictx)
input_osc_4(ictx, p); input_osc_4(ictx, p);
break; break;
case 7: case 7:
if (utf8_isvalid(p)) { if (wp != NULL && screen_set_path(sctx->s, p, 1)) {
screen_set_path(sctx->s, p); server_redraw_window_borders(wp->window);
if (wp != NULL) { server_status_window(wp->window);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}
} }
break; break;
case 8: case 8:
input_osc_8(ictx, p); input_osc_8(ictx, p);
break; break;
case 9:
input_osc_9(ictx, p);
break;
case 10: case 10:
input_osc_10(ictx, p); input_osc_10(ictx, p);
break; break;
@@ -2709,7 +2779,7 @@ input_exit_apc(struct input_ctx *ictx)
if (wp != NULL && if (wp != NULL &&
options_get_number(wp->options, "allow-set-title") && options_get_number(wp->options, "allow-set-title") &&
screen_set_title(sctx->s, ictx->input_buf)) { screen_set_title(sctx->s, ictx->input_buf, 1)) {
notify_pane("pane-title-changed", wp); notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window); server_redraw_window_borders(wp->window);
server_status_window(wp->window); server_status_window(wp->window);
@@ -2752,10 +2822,10 @@ input_exit_rename(struct input_ctx *ictx)
if (o != NULL) if (o != NULL)
options_remove_or_default(o, -1, NULL); options_remove_or_default(o, -1, NULL);
if (!options_get_number(w->options, "automatic-rename")) if (!options_get_number(w->options, "automatic-rename"))
window_set_name(w, ""); window_set_name(w, "", 1);
} else { } else {
options_set_number(w->options, "automatic-rename", 0); options_set_number(w->options, "automatic-rename", 0);
window_set_name(w, ictx->input_buf); window_set_name(w, ictx->input_buf, 1);
} }
server_redraw_window_borders(w); server_redraw_window_borders(w);
server_status_window(w); server_status_window(w);
@@ -2920,6 +2990,57 @@ bad:
free(id); free(id);
} }
/* Helper to handle setting the progress bar and redrawing. */
static void
input_set_progress_bar(struct input_ctx *ictx, enum progress_bar_state state,
int p)
{
screen_set_progress_bar(ictx->ctx.s, state, p);
if (ictx->wp != NULL) {
server_redraw_window_borders(ictx->wp->window);
server_status_window(ictx->wp->window);
}
}
/* Handle the OSC 9;4 sequence for progress bars. */
static void
input_osc_9(struct input_ctx *ictx, const char *p)
{
const char *pb = p;
enum progress_bar_state state;
int progress = 0;
if (*pb++ != '4')
return;
if (*pb == '\0' || (*pb == ';' && pb[1] == '\0'))
return;
if (*pb++ != ';')
return;
if (*pb < '0' || *pb > '4')
goto bad;
state = *pb++ - '0';
if (*pb == '\0' || (*pb == ';' && pb[1] == '\0')) {
input_set_progress_bar(ictx, state, -1);
return;
}
if (*pb++ != ';')
goto bad;
while (*pb >= '0' && *pb <= '9') {
if (progress > 100)
goto bad;
progress = progress * 10 + *pb++ - '0';
}
if (*pb != '\0' || progress < 0 || progress > 100)
goto bad;
input_set_progress_bar(ictx, state, progress);
return;
bad:
log_debug("bad OSC 9;4 %s", p);
}
/* Handle the OSC 10 sequence for setting and querying foreground colour. */ /* Handle the OSC 10 sequence for setting and querying foreground colour. */
static void static void
@@ -3069,67 +3190,110 @@ input_osc_133(struct input_ctx *ictx, const char *p)
} }
} }
/* Handle OSC 52 reply. */
static void
input_osc_52_reply(struct input_ctx *ictx, char clip)
{
struct bufferevent *ev = ictx->event;
struct paste_buffer *pb;
int state;
const char *buf;
size_t len;
state = options_get_number(global_options, "get-clipboard");
if (state == 0)
return;
if (state == 1) {
if ((pb = paste_get_top(NULL)) == NULL)
return;
buf = paste_buffer_data(pb, &len);
if (ictx->input_end == INPUT_END_BEL)
input_reply_clipboard(ev, buf, len, "\007", clip);
else
input_reply_clipboard(ev, buf, len, "\033\\", clip);
return;
}
input_add_request(ictx, INPUT_REQUEST_CLIPBOARD, ictx->input_end);
}
/*
* Parse and decode OSC 52 clipboard data. Returns 0 on failure or if handled
* as a query. On success, returns 1 and sets *out, *outlen, and *flags (caller
* must free *out).
*/
static int
input_osc_52_parse(struct input_ctx *ictx, const char *p, u_char **out,
int *outlen, char *clip)
{
char *end;
size_t len;
const char *allow = "cpqs01234567";
u_int i, j = 0;
if (options_get_number(global_options, "set-clipboard") != 2)
return (0);
if ((end = strchr(p, ';')) == NULL)
return (0);
end++;
if (*end == '\0')
return (0);
log_debug("%s: %s", __func__, end);
for (i = 0; p + i != end; i++) {
if (strchr(allow, p[i]) != NULL && strchr(clip, p[i]) == NULL)
clip[j++] = p[i];
}
log_debug("%s: %.*s %s", __func__, (int)(end - p - 1), p, clip);
if (strcmp(end, "?") == 0) {
input_osc_52_reply(ictx, *clip);
return (0);
}
len = ((strlen(end) + 3) / 4) * 3;
if (len == 0)
return (0);
*out = xmalloc(len);
if ((*outlen = b64_pton(end, *out, len)) == -1) {
free(*out);
*out = NULL;
return (0);
}
return (1);
}
/* Handle the OSC 52 sequence for setting the clipboard. */ /* Handle the OSC 52 sequence for setting the clipboard. */
static void static void
input_osc_52(struct input_ctx *ictx, const char *p) input_osc_52(struct input_ctx *ictx, const char *p)
{ {
struct window_pane *wp = ictx->wp; struct window_pane *wp = ictx->wp;
char *end;
const char *buf = NULL;
size_t len = 0;
u_char *out;
int outlen, state;
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
struct paste_buffer *pb; u_char *out;
const char* allow = "cpqs01234567"; int outlen;
char flags[sizeof "cpqs01234567"] = ""; char clip[sizeof "cpqs01234567"] = "";
u_int i, j = 0;
if (wp == NULL) if (!input_osc_52_parse(ictx, p, &out, &outlen, clip))
return;
state = options_get_number(global_options, "set-clipboard");
if (state != 2)
return; return;
if ((end = strchr(p, ';')) == NULL) if (wp == NULL) {
return; /* Popup window. */
end++; if (ictx->c == NULL) {
if (*end == '\0') free(out);
return; return;
log_debug("%s: %s", __func__, end); }
tty_set_selection(&ictx->c->tty, clip, out, outlen);
for (i = 0; p + i != end; i++) { paste_add(NULL, out, outlen);
if (strchr(allow, p[i]) != NULL && strchr(flags, p[i]) == NULL) } else {
flags[j++] = p[i]; /* Normal window. */
screen_write_start_pane(&ctx, wp, NULL);
screen_write_setselection(&ctx, clip, out, outlen);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
paste_add(NULL, out, outlen);
} }
log_debug("%s: %.*s %s", __func__, (int)(end - p - 1), p, flags);
if (strcmp(end, "?") == 0) {
if ((pb = paste_get_top(NULL)) != NULL)
buf = paste_buffer_data(pb, &len);
if (ictx->input_end == INPUT_END_BEL)
input_reply_clipboard(ictx->event, buf, len, "\007");
else
input_reply_clipboard(ictx->event, buf, len, "\033\\");
return;
}
len = (strlen(end) / 4) * 3;
if (len == 0)
return;
out = xmalloc(len);
if ((outlen = b64_pton(end, out, len)) == -1) {
free(out);
return;
}
screen_write_start_pane(&ctx, wp, NULL);
screen_write_setselection(&ctx, flags, out, outlen);
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
paste_add(NULL, out, outlen);
} }
/* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */ /* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */
@@ -3169,9 +3333,10 @@ input_osc_104(struct input_ctx *ictx, const char *p)
free(copy); free(copy);
} }
/* Send a clipboard reply. */
void void
input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len, input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len,
const char *end) const char *end, char clip)
{ {
char *out = NULL; char *out = NULL;
int outlen = 0; int outlen = 0;
@@ -3187,7 +3352,10 @@ input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len,
} }
} }
bufferevent_write(bev, "\033]52;;", 6); bufferevent_write(bev, "\033]52;", 5);
if (clip != 0)
bufferevent_write(bev, &clip, 1);
bufferevent_write(bev, ";", 1);
if (outlen != 0) if (outlen != 0)
bufferevent_write(bev, out, outlen); bufferevent_write(bev, out, outlen);
bufferevent_write(bev, end, strlen(end)); bufferevent_write(bev, end, strlen(end));
@@ -3208,7 +3376,7 @@ input_request_timer_callback(__unused int fd, __unused short events, void *arg)
{ {
struct input_ctx *ictx = arg; struct input_ctx *ictx = arg;
struct input_request *ir, *ir1; struct input_request *ir, *ir1;
time_t t = time(NULL); uint64_t t = get_timer();
TAILQ_FOREACH_SAFE(ir, &ictx->requests, entry, ir1) { TAILQ_FOREACH_SAFE(ir, &ictx->requests, entry, ir1) {
if (ir->t >= t - INPUT_REQUEST_TIMEOUT) if (ir->t >= t - INPUT_REQUEST_TIMEOUT)
@@ -3225,7 +3393,7 @@ input_request_timer_callback(__unused int fd, __unused short events, void *arg)
static void static void
input_start_request_timer(struct input_ctx *ictx) input_start_request_timer(struct input_ctx *ictx)
{ {
struct timeval tv = { .tv_sec = 0, .tv_usec = 500000 }; struct timeval tv = { .tv_sec = 0, .tv_usec = 100000 };
event_del(&ictx->request_timer); event_del(&ictx->request_timer);
event_add(&ictx->request_timer, &tv); event_add(&ictx->request_timer, &tv);
@@ -3240,7 +3408,7 @@ input_make_request(struct input_ctx *ictx, enum input_request_type type)
ir = xcalloc (1, sizeof *ir); ir = xcalloc (1, sizeof *ir);
ir->type = type; ir->type = type;
ir->ictx = ictx; ir->ictx = ictx;
ir->t = time(NULL); ir->t = get_timer();
if (++ictx->request_count == 1) if (++ictx->request_count == 1)
input_start_request_timer(ictx); input_start_request_timer(ictx);
@@ -3305,6 +3473,9 @@ input_add_request(struct input_ctx *ictx, enum input_request_type type, int idx)
xsnprintf(s, sizeof s, "\033]4;%d;?\033\\", idx); xsnprintf(s, sizeof s, "\033]4;%d;?\033\\", idx);
tty_puts(&c->tty, s); tty_puts(&c->tty, s);
break; break;
case INPUT_REQUEST_CLIPBOARD:
tty_putcode_ss(&c->tty, TTYC_MS, "", "?");
break;
case INPUT_REQUEST_QUEUE: case INPUT_REQUEST_QUEUE:
break; break;
} }
@@ -3312,6 +3483,40 @@ input_add_request(struct input_ctx *ictx, enum input_request_type type, int idx)
return (0); return (0);
} }
/* Handle a palette reply. */
static void
input_request_palette_reply(struct input_request *ir, void *data)
{
struct input_request_palette_data *pd = data;
input_osc_colour_reply(ir->ictx, 0, 4, pd->idx, pd->c, ir->end);
}
/* Handle a clipboard reply. */
static void
input_request_clipboard_reply(struct input_request *ir, void *data)
{
struct input_ctx *ictx = ir->ictx;
struct bufferevent *ev = ictx->event;
struct input_request_clipboard_data *cd = data;
int state;
char *copy;
state = options_get_number(global_options, "get-clipboard");
if (state == 0 || state == 1)
return;
if (state == 3) {
copy = xmalloc(cd->len);
memcpy(copy, cd->buf, cd->len);
paste_add(NULL, copy, cd->len);
}
if (ir->idx == INPUT_END_BEL)
input_reply_clipboard(ev, cd->buf, cd->len, "\007", cd->clip);
else
input_reply_clipboard(ev, cd->buf, cd->len, "\033\\", cd->clip);
}
/* Handle a reply to a request. */ /* Handle a reply to a request. */
void void
input_request_reply(struct client *c, enum input_request_type type, void *data) input_request_reply(struct client *c, enum input_request_type type, void *data)
@@ -3321,11 +3526,22 @@ input_request_reply(struct client *c, enum input_request_type type, void *data)
int complete = 0; int complete = 0;
TAILQ_FOREACH_SAFE(ir, &c->input_requests, centry, ir1) { TAILQ_FOREACH_SAFE(ir, &c->input_requests, centry, ir1) {
if (ir->type == type && pd->idx == ir->idx) { if (ir->type != type) {
input_free_request(ir);
continue;
}
if (type == INPUT_REQUEST_PALETTE) {
if (pd->idx != ir->idx) {
input_free_request(ir);
continue;
}
found = ir;
break;
}
if (type == INPUT_REQUEST_CLIPBOARD) {
found = ir; found = ir;
break; break;
} }
input_free_request(ir);
} }
if (found == NULL) if (found == NULL)
return; return;
@@ -3335,8 +3551,11 @@ input_request_reply(struct client *c, enum input_request_type type, void *data)
break; break;
if (ir->type == INPUT_REQUEST_QUEUE) if (ir->type == INPUT_REQUEST_QUEUE)
input_send_reply(ir->ictx, ir->data); input_send_reply(ir->ictx, ir->data);
else if (ir == found && ir->type == INPUT_REQUEST_PALETTE) { else if (ir == found) {
input_osc_colour_reply(ir->ictx, 0, 4, pd->idx, pd->c, ir->end); if (ir->type == INPUT_REQUEST_PALETTE)
input_request_palette_reply(ir, data);
else if (ir->type == INPUT_REQUEST_CLIPBOARD)
input_request_clipboard_reply(ir, data);
complete = 1; complete = 1;
} }
input_free_request(ir); input_free_request(ir);
@@ -3349,7 +3568,7 @@ input_cancel_requests(struct client *c)
{ {
struct input_request *ir, *ir1; struct input_request *ir, *ir1;
TAILQ_FOREACH_SAFE(ir, &c->input_requests, entry, ir1) TAILQ_FOREACH_SAFE(ir, &c->input_requests, centry, ir1)
input_free_request(ir); input_free_request(ir);
} }
@@ -3357,14 +3576,24 @@ input_cancel_requests(struct client *c)
static void static void
input_report_current_theme(struct input_ctx *ictx) input_report_current_theme(struct input_ctx *ictx)
{ {
switch (window_pane_get_theme(ictx->wp)) { struct window_pane *wp = ictx->wp;
if (wp != NULL) {
wp->last_theme = window_pane_get_theme(wp);
wp->flags &= ~PANE_THEMECHANGED;
switch (wp->last_theme) {
case THEME_DARK: case THEME_DARK:
log_debug("%s: %%%u dark theme", __func__, wp->id);
input_reply(ictx, 0, "\033[?997;1n"); input_reply(ictx, 0, "\033[?997;1n");
break; break;
case THEME_LIGHT: case THEME_LIGHT:
log_debug("%s: %%%u light theme", __func__, wp->id);
input_reply(ictx, 0, "\033[?997;2n"); input_reply(ictx, 0, "\033[?997;2n");
break; break;
case THEME_UNKNOWN: case THEME_UNKNOWN:
log_debug("%s: %%%u unknown theme", __func__, wp->id);
break; break;
}
} }
} }

View File

@@ -29,7 +29,8 @@
" 'Previous' 'p' {switch-client -p}" \ " 'Previous' 'p' {switch-client -p}" \
" ''" \ " ''" \
" 'Renumber' 'N' {move-window -r}" \ " 'Renumber' 'N' {move-window -r}" \
" 'Rename' 'n' {command-prompt -I \"#S\" {rename-session -- '%%'}}" \ " 'Rename' 'r' {command-prompt -I \"#S\" {rename-session -- '%%'}}" \
" 'Detach' 'd' {detach-client}" \
" ''" \ " ''" \
" 'New Session' 's' {new-session}" \ " 'New Session' 's' {new-session}" \
" 'New Window' 'w' {new-window}" " 'New Window' 'w' {new-window}"
@@ -49,6 +50,8 @@
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}' '<' {send -X history-top}" \ " '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}' '<' {send -X history-top}" \
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}' '>' {send -X history-bottom}" \ " '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}' '>' {send -X history-bottom}" \
" ''" \ " ''" \
" '#{?#{&&:#{buffer_size},#{!:#{pane_in_mode}}},Paste #[underscore]#{=/9/...:buffer_sample},}' 'p' {paste-buffer}" \
" ''" \
" '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {if -F '#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}' 'copy-mode -t='; send -Xt= search-backward -- \"#{q:mouse_word}\"}" \ " '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {if -F '#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}' 'copy-mode -t='; send -Xt= search-backward -- \"#{q:mouse_word}\"}" \
" '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {copy-mode -q; send-keys -l -- \"#{q:mouse_word}\"}" \ " '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {copy-mode -q; send-keys -l -- \"#{q:mouse_word}\"}" \
" '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \ " '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \
@@ -57,11 +60,11 @@
" '#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}' 'C-h' {copy-mode -q; send-keys -l -- \"#{q:mouse_hyperlink}\"}" \ " '#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}' 'C-h' {copy-mode -q; send-keys -l -- \"#{q:mouse_hyperlink}\"}" \
" '#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}' 'h' {copy-mode -q; set-buffer -- \"#{q:mouse_hyperlink}\"}" \ " '#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}' 'h' {copy-mode -q; set-buffer -- \"#{q:mouse_hyperlink}\"}" \
" ''" \ " ''" \
" 'Horizontal Split' 'h' {split-window -h}" \ " '#{?#{!:#{pane_floating_flag}},Horizontal Split,}' 'h' {split-window -h}" \
" 'Vertical Split' 'v' {split-window -v}" \ " '#{?#{!:#{pane_floating_flag}},Vertical Split,}' 'v' {split-window -v}" \
" ''" \ " ''" \
" '#{?#{>:#{window_panes},1},,-}Swap Up' 'u' {swap-pane -U}" \ " '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Up,}' 'u' {swap-pane -U}" \
" '#{?#{>:#{window_panes},1},,-}Swap Down' 'd' {swap-pane -D}" \ " '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Down,}' 'd' {swap-pane -D}" \
" '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \ " '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \
" ''" \ " ''" \
" 'Kill' 'X' {kill-pane}" \ " 'Kill' 'X' {kill-pane}" \
@@ -213,6 +216,7 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat,
bd = xcalloc(1, sizeof *bd); bd = xcalloc(1, sizeof *bd);
bd->key = (key & ~KEYC_MASK_FLAGS); bd->key = (key & ~KEYC_MASK_FLAGS);
bd->tablename = table->name;
if (note != NULL) if (note != NULL)
bd->note = xstrdup(note); bd->note = xstrdup(note);
RB_INSERT(key_bindings, &table->key_bindings, bd); RB_INSERT(key_bindings, &table->key_bindings, bd);
@@ -295,12 +299,12 @@ key_bindings_remove_table(const char *name)
table = key_bindings_get_table(name, 0); table = key_bindings_get_table(name, 0);
if (table != NULL) { if (table != NULL) {
RB_REMOVE(key_tables, &key_tables, table); RB_REMOVE(key_tables, &key_tables, table);
TAILQ_FOREACH(c, &clients, entry) {
if (c->keytable == table)
server_client_set_key_table(c, NULL);
}
key_bindings_unref_table(table); key_bindings_unref_table(table);
} }
TAILQ_FOREACH(c, &clients, entry) {
if (c->keytable == table)
server_client_set_key_table(c, NULL);
}
} }
void void
@@ -358,6 +362,7 @@ key_bindings_init(void)
"bind -N 'Split window horizontally' % { split-window -h }", "bind -N 'Split window horizontally' % { split-window -h }",
"bind -N 'Kill current window' & { confirm-before -p\"kill-window #W? (y/n)\" kill-window }", "bind -N 'Kill current window' & { confirm-before -p\"kill-window #W? (y/n)\" kill-window }",
"bind -N 'Prompt for window index to select' \"'\" { command-prompt -T window-target -pindex { select-window -t ':%%' } }", "bind -N 'Prompt for window index to select' \"'\" { command-prompt -T window-target -pindex { select-window -t ':%%' } }",
"bind -N 'New floating pane' * { new-pane }",
"bind -N 'Switch to previous client' ( { switch-client -p }", "bind -N 'Switch to previous client' ( { switch-client -p }",
"bind -N 'Switch to next client' ) { switch-client -n }", "bind -N 'Switch to next client' ) { switch-client -n }",
"bind -N 'Rename current window' , { command-prompt -I'#W' { rename-window -- '%%' } }", "bind -N 'Rename current window' , { command-prompt -I'#W' { rename-window -- '%%' } }",
@@ -439,6 +444,7 @@ key_bindings_init(void)
/* Mouse button 1 down on pane. */ /* Mouse button 1 down on pane. */
"bind -n MouseDown1Pane { select-pane -t=; send -M }", "bind -n MouseDown1Pane { select-pane -t=; send -M }",
"bind -n C-MouseDown1Pane { swap-pane -s@ }",
/* Mouse button 1 drag on pane. */ /* Mouse button 1 drag on pane. */
"bind -n MouseDrag1Pane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }", "bind -n MouseDrag1Pane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }",
@@ -455,11 +461,19 @@ key_bindings_init(void)
/* Mouse button 1 triple click on pane. */ /* Mouse button 1 triple click on pane. */
"bind -n TripleClick1Pane { select-pane -t=; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel } }", "bind -n TripleClick1Pane { select-pane -t=; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel } }",
/* Mouse button 1 on border. */
"bind -n MouseDown1Border { select-pane -M }",
/* Mouse button 1 drag on border. */ /* Mouse button 1 drag on border. */
"bind -n MouseDrag1Border { resize-pane -M }", "bind -n MouseDrag1Border { resize-pane -M }",
/* Mouse button 1 down on status line. */ /* Mouse button 1 down on status line. */
"bind -n MouseDown1Status { switch-client -t= }", "bind -n MouseDown1Status { switch-client -t= }",
"bind -n C-MouseDown1Status { swap-window -t@ }",
/* Mouse button 1 down on default pane-border-format */
"bind -n MouseDown1Control9 { display-menu -t= -xM -yM -O -T 'Kill pane #{pane_index}?' 'Yes' 'y' { kill-pane -t= } 'No' 'n' {}}",
"bind -n MouseDown1Control8 { resize-pane -Z }",
/* Mouse wheel down on status line. */ /* Mouse wheel down on status line. */
"bind -n WheelDownStatus { next-window }", "bind -n WheelDownStatus { next-window }",
@@ -480,9 +494,9 @@ key_bindings_init(void)
"bind -n M-MouseDown3Pane { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }", "bind -n M-MouseDown3Pane { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }",
/* Mouse on scrollbar. */ /* Mouse on scrollbar. */
"bind -n MouseDown1ScrollbarUp { copy-mode -u }", "bind -n MouseDown1ScrollbarUp { if -Ft= '#{pane_in_mode}' { send -X page-up } {copy-mode -u } }",
"bind -n MouseDown1ScrollbarDown { copy-mode -d }", "bind -n MouseDown1ScrollbarDown { if -Ft= '#{pane_in_mode}' { send -X page-down } {copy-mode -d } }",
"bind -n MouseDrag1ScrollbarSlider { copy-mode -S }", "bind -n MouseDrag1ScrollbarSlider { if -Ft= '#{pane_in_mode}' { send -X scroll-to-mouse } { copy-mode -S } }",
/* Copy mode (emacs) keys. */ /* Copy mode (emacs) keys. */
"bind -Tcopy-mode C-Space { send -X begin-selection }", "bind -Tcopy-mode C-Space { send -X begin-selection }",
@@ -493,7 +507,7 @@ key_bindings_init(void)
"bind -Tcopy-mode C-b { send -X cursor-left }", "bind -Tcopy-mode C-b { send -X cursor-left }",
"bind -Tcopy-mode C-g { send -X clear-selection }", "bind -Tcopy-mode C-g { send -X clear-selection }",
"bind -Tcopy-mode C-k { send -X copy-pipe-end-of-line-and-cancel }", "bind -Tcopy-mode C-k { send -X copy-pipe-end-of-line-and-cancel }",
"bind -Tcopy-mode C-l { send -X cursor-centre-vertical }", "bind -Tcopy-mode C-l { send -X recentre-top-bottom }",
"bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }", "bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }",
"bind -Tcopy-mode C-n { send -X cursor-down }", "bind -Tcopy-mode C-n { send -X cursor-down }",
"bind -Tcopy-mode C-p { send -X cursor-up }", "bind -Tcopy-mode C-p { send -X cursor-up }",
@@ -502,6 +516,7 @@ key_bindings_init(void)
"bind -Tcopy-mode C-v { send -X page-down }", "bind -Tcopy-mode C-v { send -X page-down }",
"bind -Tcopy-mode C-w { send -X copy-pipe-and-cancel }", "bind -Tcopy-mode C-w { send -X copy-pipe-and-cancel }",
"bind -Tcopy-mode Escape { send -X cancel }", "bind -Tcopy-mode Escape { send -X cancel }",
"bind -Tcopy-mode C-[ { send -X cancel }",
"bind -Tcopy-mode Space { send -X page-down }", "bind -Tcopy-mode Space { send -X page-down }",
"bind -Tcopy-mode , { send -X jump-reverse }", "bind -Tcopy-mode , { send -X jump-reverse }",
"bind -Tcopy-mode \\; { send -X jump-again }", "bind -Tcopy-mode \\; { send -X jump-again }",
@@ -575,6 +590,7 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi C-v { send -X rectangle-toggle }", "bind -Tcopy-mode-vi C-v { send -X rectangle-toggle }",
"bind -Tcopy-mode-vi C-y { send -X scroll-up }", "bind -Tcopy-mode-vi C-y { send -X scroll-up }",
"bind -Tcopy-mode-vi Escape { send -X clear-selection }", "bind -Tcopy-mode-vi Escape { send -X clear-selection }",
"bind -Tcopy-mode-vi C-[ { send -X clear-selection }",
"bind -Tcopy-mode-vi Space { send -X begin-selection }", "bind -Tcopy-mode-vi Space { send -X begin-selection }",
"bind -Tcopy-mode-vi '$' { send -X end-of-line }", "bind -Tcopy-mode-vi '$' { send -X end-of-line }",
"bind -Tcopy-mode-vi , { send -X jump-reverse }", "bind -Tcopy-mode-vi , { send -X jump-reverse }",
@@ -683,6 +699,7 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
readonly = 1; readonly = 1;
else else
readonly = cmd_list_all_have(bd->cmdlist, CMD_READONLY); readonly = cmd_list_all_have(bd->cmdlist, CMD_READONLY);
if (!readonly) if (!readonly)
new_item = cmdq_get_callback(key_bindings_read_only, NULL); new_item = cmdq_get_callback(key_bindings_read_only, NULL);
else { else {
@@ -698,3 +715,15 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
new_item = cmdq_append(c, new_item); new_item = cmdq_append(c, new_item);
return (new_item); return (new_item);
} }
int
key_bindings_has_repeat(struct key_binding **l, u_int n)
{
u_int i;
for (i = 0; i < n; i++) {
if (l[i]->flags & KEY_BINDING_REPEAT)
return (1);
}
return (0);
}

View File

@@ -202,7 +202,7 @@ key_string_search_table(const char *string)
return (key_string_table[i].key); return (key_string_table[i].key);
} }
if (sscanf(string, "User%u", &user) == 1 && user < KEYC_NUSER) if (sscanf(string, "User%u", &user) == 1 && user <= KEYC_NUSER)
return (KEYC_USER + user); return (KEYC_USER + user);
return (KEYC_UNKNOWN); return (KEYC_UNKNOWN);
@@ -382,6 +382,14 @@ key_string_lookup_key(key_code key, int with_flags)
s = "PasteEnd"; s = "PasteEnd";
goto append; goto append;
} }
if (key == KEYC_REPORT_DARK_THEME) {
s = "ReportDarkTheme";
goto append;
}
if (key == KEYC_REPORT_LIGHT_THEME) {
s = "ReportLightTheme";
goto append;
}
if (key == KEYC_MOUSE) { if (key == KEYC_MOUSE) {
s = "Mouse"; s = "Mouse";
goto append; goto append;
@@ -410,7 +418,7 @@ key_string_lookup_key(key_code key, int with_flags)
s = "MouseMoveBorder"; s = "MouseMoveBorder";
goto append; goto append;
} }
if (key >= KEYC_USER && key < KEYC_USER_END) { if (KEYC_IS_USER(key)) {
snprintf(tmp, sizeof tmp, "User%u", (u_int)(key - KEYC_USER)); snprintf(tmp, sizeof tmp, "User%u", (u_int)(key - KEYC_USER));
strlcat(out, tmp, sizeof out); strlcat(out, tmp, sizeof out);
goto out; goto out;

View File

@@ -27,10 +27,10 @@ static struct layout_cell *layout_find_bottomright(struct layout_cell *);
static u_short layout_checksum(const char *); static u_short layout_checksum(const char *);
static int layout_append(struct layout_cell *, char *, static int layout_append(struct layout_cell *, char *,
size_t); size_t);
static struct layout_cell *layout_construct(struct layout_cell *, static int layout_construct(struct layout_cell *,
const char **); const char **, struct layout_cell **);
static void layout_assign(struct window_pane **, static void layout_assign(struct window_pane **,
struct layout_cell *); struct layout_cell *, int);
/* Find the bottom-right cell. */ /* Find the bottom-right cell. */
static struct layout_cell * static struct layout_cell *
@@ -58,14 +58,30 @@ layout_checksum(const char *layout)
/* Dump layout as a string. */ /* Dump layout as a string. */
char * char *
layout_dump(struct layout_cell *root) layout_dump(struct window *w, struct layout_cell *root)
{ {
char layout[8192], *out; char layout[8192], *out;
int bracket = 0;
struct window_pane *wp;
*layout = '\0'; *layout = '\0';
if (layout_append(root, layout, sizeof layout) != 0) if (layout_append(root, layout, sizeof layout) != 0)
return (NULL); return (NULL);
TAILQ_FOREACH(wp, &w->z_index, zentry) {
if (!window_pane_is_floating(wp))
break;
if (!bracket) {
strlcat(layout, "<", sizeof layout);
bracket = 1;
}
if (layout_append(wp->layout_cell, layout, sizeof layout) != 0)
return (NULL);
strlcat(layout, ",", sizeof layout);
}
if (bracket)
layout[strlen(layout) - 1] = '>';
xasprintf(&out, "%04hx,%s", layout_checksum(layout), layout); xasprintf(&out, "%04hx,%s", layout_checksum(layout), layout);
return (out); return (out);
} }
@@ -81,12 +97,13 @@ layout_append(struct layout_cell *lc, char *buf, size_t len)
if (len == 0) if (len == 0)
return (-1); return (-1);
if (lc == NULL)
return (0);
if (lc->wp != NULL) { if (lc->wp != NULL) {
tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u,%u", tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%d,%d,%u",
lc->sx, lc->sy, lc->xoff, lc->yoff, lc->wp->id); lc->sx, lc->sy, lc->xoff, lc->yoff, lc->wp->id);
} else { } else {
tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u", tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%d,%d",
lc->sx, lc->sy, lc->xoff, lc->yoff); lc->sx, lc->sy, lc->xoff, lc->yoff);
} }
if (tmplen > (sizeof tmp) - 1) if (tmplen > (sizeof tmp) - 1)
@@ -156,37 +173,43 @@ layout_check(struct layout_cell *lc)
int int
layout_parse(struct window *w, const char *layout, char **cause) layout_parse(struct window *w, const char *layout, char **cause)
{ {
struct layout_cell *lc, *lcchild; struct layout_cell *lcchild, *tiled_lc = NULL;
struct window_pane *wp; struct window_pane *wp;
u_int npanes, ncells, sx = 0, sy = 0; u_int npanes, ncells, sx = 0, sy = 0;
u_short csum; u_short csum;
int n;
/* Check validity. */ /* Check validity. */
if (sscanf(layout, "%hx,", &csum) != 1) { if (sscanf(layout, "%hx,%n", &csum, &n) != 1 || n != 5) {
*cause = xstrdup("invalid layout"); *cause = xstrdup("invalid layout");
return (-1); return (-1);
} }
layout += 5; layout += n;
if (csum != layout_checksum(layout)) { if (csum != layout_checksum(layout)) {
*cause = xstrdup("invalid layout"); *cause = xstrdup("invalid layout");
return (-1); return (-1);
} }
/* Build the layout. */ /* Build the layout. */
lc = layout_construct(NULL, &layout); if (layout_construct(NULL, &layout, &tiled_lc) != 0) {
if (lc == NULL) {
*cause = xstrdup("invalid layout"); *cause = xstrdup("invalid layout");
return (-1); return (-1);
} }
if (tiled_lc == NULL) {
/* A stub layout cell for an empty window. */
tiled_lc = layout_create_cell(NULL);
tiled_lc->type = LAYOUT_LEFTRIGHT;
layout_set_size(tiled_lc, w->sx, w->sy, 0, 0);
}
if (*layout != '\0') { if (*layout != '\0') {
*cause = xstrdup("invalid layout"); *cause = xstrdup("invalid layout");
goto fail; goto fail;
} }
/* Check this window will fit into the layout. */ /* Check this window will fit into the layout. */
npanes = window_count_panes(w, 1);
for (;;) { for (;;) {
npanes = window_count_panes(w); ncells = layout_count_cells(tiled_lc);
ncells = layout_count_cells(lc);
if (npanes > ncells) { if (npanes > ncells) {
xasprintf(cause, "have %u panes but need %u", npanes, xasprintf(cause, "have %u panes but need %u", npanes,
ncells); ncells);
@@ -195,9 +218,12 @@ layout_parse(struct window *w, const char *layout, char **cause)
if (npanes == ncells) if (npanes == ncells)
break; break;
/* Fewer panes than cells - close the bottom right. */ /*
lcchild = layout_find_bottomright(lc); * Fewer panes than cells, close the bottom right until none
layout_destroy_cell(w, lcchild, &lc); * remain.
*/
lcchild = layout_find_bottomright(tiled_lc);
layout_destroy_cell(w, lcchild, &tiled_lc);
} }
/* /*
@@ -205,91 +231,104 @@ layout_parse(struct window *w, const char *layout, char **cause)
* an incorrect top cell size - if it is larger than the top child then * an incorrect top cell size - if it is larger than the top child then
* correct that (if this is still wrong the check code will catch it). * correct that (if this is still wrong the check code will catch it).
*/ */
switch (lc->type) {
switch (tiled_lc->type) {
case LAYOUT_WINDOWPANE: case LAYOUT_WINDOWPANE:
break; break;
case LAYOUT_LEFTRIGHT: case LAYOUT_LEFTRIGHT:
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) {
sy = lcchild->sy + 1; sy = lcchild->sy + 1;
sx += lcchild->sx + 1; sx += lcchild->sx + 1;
} }
break; break;
case LAYOUT_TOPBOTTOM: case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) {
sx = lcchild->sx + 1; sx = lcchild->sx + 1;
sy += lcchild->sy + 1; sy += lcchild->sy + 1;
} }
break; break;
} }
if (lc->type != LAYOUT_WINDOWPANE && (lc->sx != sx || lc->sy != sy)) { if (tiled_lc->type != LAYOUT_WINDOWPANE &&
log_debug("fix layout %u,%u to %u,%u", lc->sx, lc->sy, sx,sy); (tiled_lc->sx != sx || tiled_lc->sy != sy)) {
layout_print_cell(lc, __func__, 0); layout_print_cell(tiled_lc, __func__, 0);
lc->sx = sx - 1; lc->sy = sy - 1; tiled_lc->sx = sx - 1; tiled_lc->sy = sy - 1;
} }
/* Check the new layout. */ /* Check the new layout. */
if (!layout_check(lc)) { if (!layout_check(tiled_lc)) {
*cause = xstrdup("size mismatch after applying layout"); *cause = xstrdup("size mismatch after applying layout");
goto fail; goto fail;
} }
/* Resize to the layout size. */ /* Resize window to the layout size. */
window_resize(w, lc->sx, lc->sy, -1, -1); if (sx != 0 && sy != 0)
window_resize(w, tiled_lc->sx, tiled_lc->sy, -1, -1);
/* Destroy the old layout and swap to the new. */ /* Destroy the old layout and swap to the new. */
layout_free_cell(w->layout_root); layout_free_cell(w->layout_root);
w->layout_root = lc; w->layout_root = tiled_lc;
/* Assign the panes into the cells. */ /* Assign the panes into the cells. */
wp = TAILQ_FIRST(&w->panes); wp = TAILQ_FIRST(&w->panes);
layout_assign(&wp, lc); if (tiled_lc != NULL)
layout_assign(&wp, tiled_lc, 0);
/* Fix pane z-indexes. */
while (!TAILQ_EMPTY(&w->z_index)) {
wp = TAILQ_FIRST(&w->z_index);
TAILQ_REMOVE(&w->z_index, wp, zentry);
}
layout_fix_zindexes(w, tiled_lc);
/* Update pane offsets and sizes. */ /* Update pane offsets and sizes. */
layout_fix_offsets(w); layout_fix_offsets(w);
layout_fix_panes(w, NULL); layout_fix_panes(w, NULL);
recalculate_sizes(); recalculate_sizes();
layout_print_cell(tiled_lc, __func__, 0);
layout_print_cell(lc, __func__, 0);
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);
return (0); return (0);
fail: fail:
layout_free_cell(lc); layout_free_cell(tiled_lc);
return (-1); return (-1);
} }
/* Assign panes into cells. */ /* Assign panes into cells. */
static void static void
layout_assign(struct window_pane **wp, struct layout_cell *lc) layout_assign(struct window_pane **wp, struct layout_cell *lc, int flags)
{ {
struct layout_cell *lcchild; struct layout_cell *lcchild;
if (lc == NULL)
return;
switch (lc->type) { switch (lc->type) {
case LAYOUT_WINDOWPANE: case LAYOUT_WINDOWPANE:
layout_make_leaf(lc, *wp); layout_make_leaf(lc, *wp);
lc->flags |= flags;
*wp = TAILQ_NEXT(*wp, entry); *wp = TAILQ_NEXT(*wp, entry);
return; return;
case LAYOUT_LEFTRIGHT: case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM: case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &lc->cells, entry) TAILQ_FOREACH(lcchild, &lc->cells, entry)
layout_assign(wp, lcchild); layout_assign(wp, lcchild, flags);
return; return;
} }
} }
/* Construct a cell from all or part of a layout tree. */
static struct layout_cell * static struct layout_cell *
layout_construct(struct layout_cell *lcparent, const char **layout) layout_construct_cell(struct layout_cell *lcparent, const char **layout)
{ {
struct layout_cell *lc, *lcchild; struct layout_cell *lc;
u_int sx, sy, xoff, yoff; u_int sx, sy;
int xoff, yoff;
const char *saved; const char *saved;
if (!isdigit((u_char) **layout)) if (!isdigit((u_char) **layout))
return (NULL); return (NULL);
if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4) if (sscanf(*layout, "%ux%u,%d,%d", &sx, &sy, &xoff, &yoff) != 4)
return (NULL); return (NULL);
while (isdigit((u_char) **layout)) while (isdigit((u_char) **layout))
@@ -324,17 +363,35 @@ layout_construct(struct layout_cell *lcparent, const char **layout)
lc->xoff = xoff; lc->xoff = xoff;
lc->yoff = yoff; lc->yoff = yoff;
return (lc);
}
/*
* Given a character string layout, recursively construct cells.
* Possible return values:
* lc LAYOUT_WINDOWPANE, no children
* lc LAYOUT_LEFTRIGHT or LAYOUT_TOPBOTTOM, with children
*/
static int
layout_construct(struct layout_cell *lcparent, const char **layout,
struct layout_cell **lc)
{
struct layout_cell *lcchild;
*lc = layout_construct_cell(lcparent, layout);
switch (**layout) { switch (**layout) {
case ',': case ',':
case '}': case '}':
case ']': case ']':
case '>':
case '\0': case '\0':
return (lc); return (0);
case '{': case '{':
lc->type = LAYOUT_LEFTRIGHT; (*lc)->type = LAYOUT_LEFTRIGHT;
break; break;
case '[': case '[':
lc->type = LAYOUT_TOPBOTTOM; (*lc)->type = LAYOUT_TOPBOTTOM;
break; break;
default: default:
goto fail; goto fail;
@@ -342,13 +399,12 @@ layout_construct(struct layout_cell *lcparent, const char **layout)
do { do {
(*layout)++; (*layout)++;
lcchild = layout_construct(lc, layout); if (layout_construct(*lc, layout, &lcchild) != 0)
if (lcchild == NULL)
goto fail; goto fail;
TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry); TAILQ_INSERT_TAIL(&(*lc)->cells, lcchild, entry);
} while (**layout == ','); } while (**layout == ',');
switch (lc->type) { switch ((*lc)->type) {
case LAYOUT_LEFTRIGHT: case LAYOUT_LEFTRIGHT:
if (**layout != '}') if (**layout != '}')
goto fail; goto fail;
@@ -362,9 +418,9 @@ layout_construct(struct layout_cell *lcparent, const char **layout)
} }
(*layout)++; (*layout)++;
return (lc); return (0);
fail: fail:
layout_free_cell(lc); layout_free_cell(*lc);
return (NULL); return (-1);
} }

View File

@@ -123,6 +123,18 @@ layout_set_previous(struct window *w)
return (layout); return (layout);
} }
static struct window_pane *
layout_first_tiled(struct window *w)
{
struct window_pane *wp;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_is_floating(wp))
return (wp);
}
return (NULL);
}
static void static void
layout_set_even(struct window *w, enum layout_type type) layout_set_even(struct window *w, enum layout_type type)
{ {
@@ -133,7 +145,7 @@ layout_set_even(struct window *w, enum layout_type type)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */ /* Get number of panes. */
n = window_count_panes(w); n = window_count_panes(w, 0);
if (n <= 1) if (n <= 1)
return; return;
@@ -156,6 +168,8 @@ layout_set_even(struct window *w, enum layout_type type)
/* Build new leaf cells. */ /* Build new leaf cells. */
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_is_floating(wp))
continue;
lcnew = layout_create_cell(lc); lcnew = layout_create_cell(lc);
layout_make_leaf(lcnew, wp); layout_make_leaf(lcnew, wp);
lcnew->sx = w->sx; lcnew->sx = w->sx;
@@ -201,7 +215,7 @@ layout_set_main_h(struct window *w)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */ /* Get number of panes. */
n = window_count_panes(w); n = window_count_panes(w, 0);
if (n <= 1) if (n <= 1)
return; return;
n--; /* take off main pane */ n--; /* take off main pane */
@@ -250,14 +264,16 @@ layout_set_main_h(struct window *w)
/* Create the main pane. */ /* Create the main pane. */
lcmain = layout_create_cell(lc); lcmain = layout_create_cell(lc);
layout_set_size(lcmain, sx, mainh, 0, 0); layout_set_size(lcmain, sx, mainh, 0, 0);
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); layout_make_leaf(lcmain, layout_first_tiled(w));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
/* Create the other pane. */ /* Create the other pane. */
lcother = layout_create_cell(lc); lcother = layout_create_cell(lc);
layout_set_size(lcother, sx, otherh, 0, 0); layout_set_size(lcother, sx, otherh, 0, 0);
if (n == 1) { if (n == 1) {
wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); wp = TAILQ_NEXT(layout_first_tiled(w), entry);
while (wp != NULL && window_pane_is_floating(wp))
wp = TAILQ_NEXT(wp, entry);
layout_make_leaf(lcother, wp); layout_make_leaf(lcother, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
} else { } else {
@@ -266,7 +282,9 @@ layout_set_main_h(struct window *w)
/* Add the remaining panes as children. */ /* Add the remaining panes as children. */
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == TAILQ_FIRST(&w->panes)) if (window_pane_is_floating(wp))
continue;
if (wp == layout_first_tiled(w))
continue; continue;
lcchild = layout_create_cell(lcother); lcchild = layout_create_cell(lcother);
layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0); layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
@@ -299,7 +317,7 @@ layout_set_main_h_mirrored(struct window *w)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */ /* Get number of panes. */
n = window_count_panes(w); n = window_count_panes(w, 0);
if (n <= 1) if (n <= 1)
return; return;
n--; /* take off main pane */ n--; /* take off main pane */
@@ -349,7 +367,9 @@ layout_set_main_h_mirrored(struct window *w)
lcother = layout_create_cell(lc); lcother = layout_create_cell(lc);
layout_set_size(lcother, sx, otherh, 0, 0); layout_set_size(lcother, sx, otherh, 0, 0);
if (n == 1) { if (n == 1) {
wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); wp = TAILQ_NEXT(layout_first_tiled(w), entry);
while (wp != NULL && window_pane_is_floating(wp))
wp = TAILQ_NEXT(wp, entry);
layout_make_leaf(lcother, wp); layout_make_leaf(lcother, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
} else { } else {
@@ -358,7 +378,9 @@ layout_set_main_h_mirrored(struct window *w)
/* Add the remaining panes as children. */ /* Add the remaining panes as children. */
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == TAILQ_FIRST(&w->panes)) if (window_pane_is_floating(wp))
continue;
if (wp == layout_first_tiled(w))
continue; continue;
lcchild = layout_create_cell(lcother); lcchild = layout_create_cell(lcother);
layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0); layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
@@ -371,7 +393,7 @@ layout_set_main_h_mirrored(struct window *w)
/* Create the main pane. */ /* Create the main pane. */
lcmain = layout_create_cell(lc); lcmain = layout_create_cell(lc);
layout_set_size(lcmain, sx, mainh, 0, 0); layout_set_size(lcmain, sx, mainh, 0, 0);
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); layout_make_leaf(lcmain, layout_first_tiled(w));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
/* Fix cell offsets. */ /* Fix cell offsets. */
@@ -397,7 +419,7 @@ layout_set_main_v(struct window *w)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */ /* Get number of panes. */
n = window_count_panes(w); n = window_count_panes(w, 0);
if (n <= 1) if (n <= 1)
return; return;
n--; /* take off main pane */ n--; /* take off main pane */
@@ -446,14 +468,16 @@ layout_set_main_v(struct window *w)
/* Create the main pane. */ /* Create the main pane. */
lcmain = layout_create_cell(lc); lcmain = layout_create_cell(lc);
layout_set_size(lcmain, mainw, sy, 0, 0); layout_set_size(lcmain, mainw, sy, 0, 0);
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); layout_make_leaf(lcmain, layout_first_tiled(w));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
/* Create the other pane. */ /* Create the other pane. */
lcother = layout_create_cell(lc); lcother = layout_create_cell(lc);
layout_set_size(lcother, otherw, sy, 0, 0); layout_set_size(lcother, otherw, sy, 0, 0);
if (n == 1) { if (n == 1) {
wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); wp = TAILQ_NEXT(layout_first_tiled(w), entry);
while (wp != NULL && window_pane_is_floating(wp))
wp = TAILQ_NEXT(wp, entry);
layout_make_leaf(lcother, wp); layout_make_leaf(lcother, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
} else { } else {
@@ -462,7 +486,9 @@ layout_set_main_v(struct window *w)
/* Add the remaining panes as children. */ /* Add the remaining panes as children. */
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == TAILQ_FIRST(&w->panes)) if (window_pane_is_floating(wp))
continue;
if (wp == layout_first_tiled(w))
continue; continue;
lcchild = layout_create_cell(lcother); lcchild = layout_create_cell(lcother);
layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0); layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
@@ -495,7 +521,7 @@ layout_set_main_v_mirrored(struct window *w)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */ /* Get number of panes. */
n = window_count_panes(w); n = window_count_panes(w, 0);
if (n <= 1) if (n <= 1)
return; return;
n--; /* take off main pane */ n--; /* take off main pane */
@@ -545,7 +571,9 @@ layout_set_main_v_mirrored(struct window *w)
lcother = layout_create_cell(lc); lcother = layout_create_cell(lc);
layout_set_size(lcother, otherw, sy, 0, 0); layout_set_size(lcother, otherw, sy, 0, 0);
if (n == 1) { if (n == 1) {
wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); wp = TAILQ_NEXT(layout_first_tiled(w), entry);
while (wp != NULL && window_pane_is_floating(wp))
wp = TAILQ_NEXT(wp, entry);
layout_make_leaf(lcother, wp); layout_make_leaf(lcother, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
} else { } else {
@@ -554,7 +582,9 @@ layout_set_main_v_mirrored(struct window *w)
/* Add the remaining panes as children. */ /* Add the remaining panes as children. */
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == TAILQ_FIRST(&w->panes)) if (window_pane_is_floating(wp))
continue;
if (wp == layout_first_tiled(w))
continue; continue;
lcchild = layout_create_cell(lcother); lcchild = layout_create_cell(lcother);
layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0); layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
@@ -567,7 +597,7 @@ layout_set_main_v_mirrored(struct window *w)
/* Create the main pane. */ /* Create the main pane. */
lcmain = layout_create_cell(lc); lcmain = layout_create_cell(lc);
layout_set_size(lcmain, mainw, sy, 0, 0); layout_set_size(lcmain, mainw, sy, 0, 0);
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); layout_make_leaf(lcmain, layout_first_tiled(w));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
/* Fix cell offsets. */ /* Fix cell offsets. */
@@ -593,7 +623,7 @@ layout_set_tiled(struct window *w)
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */ /* Get number of panes. */
n = window_count_panes(w); n = window_count_panes(w, 0);
if (n <= 1) if (n <= 1)
return; return;
@@ -629,8 +659,10 @@ layout_set_tiled(struct window *w)
layout_set_size(lc, sx, sy, 0, 0); layout_set_size(lc, sx, sy, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM); layout_make_node(lc, LAYOUT_TOPBOTTOM);
/* Create a grid of the cells. */ /* Create a grid of the cells, skipping any floating panes. */
wp = TAILQ_FIRST(&w->panes); wp = TAILQ_FIRST(&w->panes);
while (wp != NULL && window_pane_is_floating(wp))
wp = TAILQ_NEXT(wp, entry);
for (j = 0; j < rows; j++) { for (j = 0; j < rows; j++) {
/* If this is the last cell, all done. */ /* If this is the last cell, all done. */
if (wp == NULL) if (wp == NULL)
@@ -645,6 +677,8 @@ layout_set_tiled(struct window *w)
if (n - (j * columns) == 1 || columns == 1) { if (n - (j * columns) == 1 || columns == 1) {
layout_make_leaf(lcrow, wp); layout_make_leaf(lcrow, wp);
wp = TAILQ_NEXT(wp, entry); wp = TAILQ_NEXT(wp, entry);
while (wp != NULL && window_pane_is_floating(wp))
wp = TAILQ_NEXT(wp, entry);
continue; continue;
} }
@@ -657,8 +691,11 @@ layout_set_tiled(struct window *w)
layout_make_leaf(lcchild, wp); layout_make_leaf(lcchild, wp);
TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry); TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
/* Move to the next cell. */ /* Move to the next non-floating cell. */
if ((wp = TAILQ_NEXT(wp, entry)) == NULL) wp = TAILQ_NEXT(wp, entry);
while (wp != NULL && window_pane_is_floating(wp))
wp = TAILQ_NEXT(wp, entry);
if (wp == NULL)
break; break;
} }

366
layout.c
View File

@@ -47,6 +47,7 @@ static int layout_set_size_check(struct window *, struct layout_cell *,
static void layout_resize_child_cells(struct window *, static void layout_resize_child_cells(struct window *,
struct layout_cell *); struct layout_cell *);
/* Create a new layout cell. */
struct layout_cell * struct layout_cell *
layout_create_cell(struct layout_cell *lcparent) layout_create_cell(struct layout_cell *lcparent)
{ {
@@ -54,6 +55,7 @@ layout_create_cell(struct layout_cell *lcparent)
lc = xmalloc(sizeof *lc); lc = xmalloc(sizeof *lc);
lc->type = LAYOUT_WINDOWPANE; lc->type = LAYOUT_WINDOWPANE;
lc->flags = 0;
lc->parent = lcparent; lc->parent = lcparent;
TAILQ_INIT(&lc->cells); TAILQ_INIT(&lc->cells);
@@ -61,19 +63,23 @@ layout_create_cell(struct layout_cell *lcparent)
lc->sx = UINT_MAX; lc->sx = UINT_MAX;
lc->sy = UINT_MAX; lc->sy = UINT_MAX;
lc->xoff = UINT_MAX; lc->xoff = INT_MAX;
lc->yoff = UINT_MAX; lc->yoff = INT_MAX;
lc->wp = NULL; lc->wp = NULL;
return (lc); return (lc);
} }
/* Free a layout cell. */
void void
layout_free_cell(struct layout_cell *lc) layout_free_cell(struct layout_cell *lc)
{ {
struct layout_cell *lcchild; struct layout_cell *lcchild;
if (lc == NULL)
return;
switch (lc->type) { switch (lc->type) {
case LAYOUT_LEFTRIGHT: case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM: case LAYOUT_TOPBOTTOM:
@@ -84,20 +90,26 @@ layout_free_cell(struct layout_cell *lc)
} }
break; break;
case LAYOUT_WINDOWPANE: case LAYOUT_WINDOWPANE:
if (lc->wp != NULL) if (lc->wp != NULL) {
lc->wp->layout_cell->parent = NULL;
lc->wp->layout_cell = NULL; lc->wp->layout_cell = NULL;
}
break; break;
} }
free(lc); free(lc);
} }
/* Log a cell. */
void void
layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
{ {
struct layout_cell *lcchild; struct layout_cell *lcchild;
const char *type; const char *type;
if (lc == NULL)
return;
switch (lc->type) { switch (lc->type) {
case LAYOUT_LEFTRIGHT: case LAYOUT_LEFTRIGHT:
type = "LEFTRIGHT"; type = "LEFTRIGHT";
@@ -112,28 +124,31 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
type = "UNKNOWN"; type = "UNKNOWN";
break; break;
} }
log_debug("%s:%*s%p type %s [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, log_debug("%s:%*s%p type %s [parent %p] wp=%p [%d,%d %ux%u]", hdr, n,
" ", lc, type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, " ", lc, type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx,
lc->sy); lc->sy);
switch (lc->type) { switch (lc->type) {
case LAYOUT_LEFTRIGHT: case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM: case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &lc->cells, entry) TAILQ_FOREACH(lcchild, &lc->cells, entry)
layout_print_cell(lcchild, hdr, n + 1); layout_print_cell(lcchild, hdr, n + 1);
break; break;
case LAYOUT_WINDOWPANE: case LAYOUT_WINDOWPANE:
break; break;
} }
} }
/* Search for a cell by the border position. */
struct layout_cell * struct layout_cell *
layout_search_by_border(struct layout_cell *lc, u_int x, u_int y) layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
{ {
struct layout_cell *lcchild, *last = NULL; struct layout_cell *lcchild, *last = NULL;
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (x >= lcchild->xoff && x < lcchild->xoff + lcchild->sx && if ((int)x >= lcchild->xoff &&
y >= lcchild->yoff && y < lcchild->yoff + lcchild->sy) { (int)x < lcchild->xoff + (int)lcchild->sx &&
(int)y >= lcchild->yoff &&
(int)y < lcchild->yoff + (int)lcchild->sy) {
/* Inside the cell - recurse. */ /* Inside the cell - recurse. */
return (layout_search_by_border(lcchild, x, y)); return (layout_search_by_border(lcchild, x, y));
} }
@@ -145,11 +160,13 @@ layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
switch (lc->type) { switch (lc->type) {
case LAYOUT_LEFTRIGHT: case LAYOUT_LEFTRIGHT:
if (x < lcchild->xoff && x >= last->xoff + last->sx) if ((int)x < lcchild->xoff &&
(int)x >= last->xoff + (int)last->sx)
return (last); return (last);
break; break;
case LAYOUT_TOPBOTTOM: case LAYOUT_TOPBOTTOM:
if (y < lcchild->yoff && y >= last->yoff + last->sy) if ((int)y < lcchild->yoff &&
(int)y >= last->yoff + (int)last->sy)
return (last); return (last);
break; break;
case LAYOUT_WINDOWPANE: case LAYOUT_WINDOWPANE:
@@ -162,9 +179,9 @@ layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
return (NULL); return (NULL);
} }
/* Set cell size. */
void void
layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, int xoff, int yoff)
u_int yoff)
{ {
lc->sx = sx; lc->sx = sx;
lc->sy = sy; lc->sy = sy;
@@ -173,6 +190,7 @@ layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff,
lc->yoff = yoff; lc->yoff = yoff;
} }
/* Make a cell a leaf cell. */
void void
layout_make_leaf(struct layout_cell *lc, struct window_pane *wp) layout_make_leaf(struct layout_cell *lc, struct window_pane *wp)
{ {
@@ -184,6 +202,7 @@ layout_make_leaf(struct layout_cell *lc, struct window_pane *wp)
lc->wp = wp; lc->wp = wp;
} }
/* Make a cell a node cell. */
void void
layout_make_node(struct layout_cell *lc, enum layout_type type) layout_make_node(struct layout_cell *lc, enum layout_type type)
{ {
@@ -198,16 +217,41 @@ layout_make_node(struct layout_cell *lc, enum layout_type type)
lc->wp = NULL; lc->wp = NULL;
} }
/* Fix z-indexes. */
void
layout_fix_zindexes(struct window *w, struct layout_cell *lc)
{
struct layout_cell *lcchild;
if (lc == NULL)
return;
switch (lc->type) {
case LAYOUT_WINDOWPANE:
TAILQ_INSERT_TAIL(&w->z_index, lc->wp, zentry);
break;
case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &lc->cells, entry)
layout_fix_zindexes(w, lcchild);
return;
default:
fatalx("bad layout type");
}
}
/* Fix cell offsets for a child cell. */ /* Fix cell offsets for a child cell. */
static void static void
layout_fix_offsets1(struct layout_cell *lc) layout_fix_offsets1(struct layout_cell *lc)
{ {
struct layout_cell *lcchild; struct layout_cell *lcchild;
u_int xoff, yoff; int xoff, yoff;
if (lc->type == LAYOUT_LEFTRIGHT) { if (lc->type == LAYOUT_LEFTRIGHT) {
xoff = lc->xoff; xoff = lc->xoff;
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->flags & LAYOUT_CELL_FLOATING)
continue;
lcchild->xoff = xoff; lcchild->xoff = xoff;
lcchild->yoff = lc->yoff; lcchild->yoff = lc->yoff;
if (lcchild->type != LAYOUT_WINDOWPANE) if (lcchild->type != LAYOUT_WINDOWPANE)
@@ -217,6 +261,8 @@ layout_fix_offsets1(struct layout_cell *lc)
} else { } else {
yoff = lc->yoff; yoff = lc->yoff;
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->flags & LAYOUT_CELL_FLOATING)
continue;
lcchild->xoff = lc->xoff; lcchild->xoff = lc->xoff;
lcchild->yoff = yoff; lcchild->yoff = yoff;
if (lcchild->type != LAYOUT_WINDOWPANE) if (lcchild->type != LAYOUT_WINDOWPANE)
@@ -230,7 +276,11 @@ layout_fix_offsets1(struct layout_cell *lc)
void void
layout_fix_offsets(struct window *w) layout_fix_offsets(struct window *w)
{ {
struct layout_cell *lc = w->layout_root; struct layout_cell *lc = w->layout_root;
/* Root consists of a single floating cell */
if (lc->flags & LAYOUT_CELL_FLOATING)
return;
lc->xoff = 0; lc->xoff = 0;
lc->yoff = 0; lc->yoff = 0;
@@ -242,13 +292,22 @@ layout_fix_offsets(struct window *w)
static int static int
layout_cell_is_top(struct window *w, struct layout_cell *lc) layout_cell_is_top(struct window *w, struct layout_cell *lc)
{ {
struct layout_cell *next; struct layout_cell *next, *edge;
while (lc != w->layout_root) { while (lc != w->layout_root) {
next = lc->parent; next = lc->parent;
if (next->type == LAYOUT_TOPBOTTOM && if (next == NULL)
lc != TAILQ_FIRST(&next->cells))
return (0); return (0);
if (next->type == LAYOUT_TOPBOTTOM) {
edge = TAILQ_FIRST(&next->cells);
while (edge != NULL) {
if (~edge->flags & LAYOUT_CELL_FLOATING)
break;
edge = TAILQ_NEXT(edge, entry);
}
if (lc != edge)
return (0);
}
lc = next; lc = next;
} }
return (1); return (1);
@@ -258,13 +317,22 @@ layout_cell_is_top(struct window *w, struct layout_cell *lc)
static int static int
layout_cell_is_bottom(struct window *w, struct layout_cell *lc) layout_cell_is_bottom(struct window *w, struct layout_cell *lc)
{ {
struct layout_cell *next; struct layout_cell *next, *edge;
while (lc != w->layout_root) { while (lc != w->layout_root) {
next = lc->parent; next = lc->parent;
if (next->type == LAYOUT_TOPBOTTOM && if (next == NULL)
lc != TAILQ_LAST(&next->cells, layout_cells))
return (0); return (0);
if (next->type == LAYOUT_TOPBOTTOM) {
edge = TAILQ_LAST(&next->cells, layout_cells);
while (edge != NULL) {
if (~edge->flags & LAYOUT_CELL_FLOATING)
break;
edge = TAILQ_PREV(edge, layout_cells, entry);
}
if (lc != edge)
return (0);
}
lc = next; lc = next;
} }
return (1); return (1);
@@ -291,12 +359,10 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
{ {
struct window_pane *wp; struct window_pane *wp;
struct layout_cell *lc; struct layout_cell *lc;
int status, scrollbars, sb_pos, sb_w, sb_pad; int status, sb_w, sb_pad;
u_int sx, sy; u_int sx, sy;
status = options_get_number(w->options, "pane-border-status"); status = options_get_number(w->options, "pane-border-status");
scrollbars = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if ((lc = wp->layout_cell) == NULL || wp == skip) if ((lc = wp->layout_cell) == NULL || wp == skip)
@@ -307,20 +373,21 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
sx = lc->sx; sx = lc->sx;
sy = lc->sy; sy = lc->sy;
if (layout_add_horizontal_border(w, lc, status)) { if (!window_pane_is_floating(wp) &&
layout_add_horizontal_border(w, lc, status)) {
if (status == PANE_STATUS_TOP) if (status == PANE_STATUS_TOP)
wp->yoff++; wp->yoff++;
sy--; sy--;
} }
if (window_pane_show_scrollbar(wp, scrollbars)) { if (window_pane_show_scrollbar(wp)) {
sb_w = wp->scrollbar_style.width; sb_w = wp->scrollbar_style.width;
sb_pad = wp->scrollbar_style.pad; sb_pad = wp->scrollbar_style.pad;
if (sb_w < 1) if (sb_w < 1)
sb_w = 1; sb_w = 1;
if (sb_pad < 0) if (sb_pad < 0)
sb_pad = 0; sb_pad = 0;
if (sb_pos == PANE_SCROLLBARS_LEFT) { if (w->sb_pos == PANE_SCROLLBARS_LEFT) {
if ((int)sx - sb_w < PANE_MINIMUM) { if ((int)sx - sb_w < PANE_MINIMUM) {
wp->xoff = wp->xoff + wp->xoff = wp->xoff +
(int)sx - PANE_MINIMUM; (int)sx - PANE_MINIMUM;
@@ -329,7 +396,7 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
sx = sx - sb_w - sb_pad; sx = sx - sb_w - sb_pad;
wp->xoff = wp->xoff + sb_w + sb_pad; wp->xoff = wp->xoff + sb_w + sb_pad;
} }
} else /* sb_pos == PANE_SCROLLBARS_RIGHT */ } else /* w->sb_pos == PANE_SCROLLBARS_RIGHT */
if ((int)sx - sb_w - sb_pad < PANE_MINIMUM) if ((int)sx - sb_w - sb_pad < PANE_MINIMUM)
sx = PANE_MINIMUM; sx = PANE_MINIMUM;
else else
@@ -346,14 +413,13 @@ u_int
layout_count_cells(struct layout_cell *lc) layout_count_cells(struct layout_cell *lc)
{ {
struct layout_cell *lcchild; struct layout_cell *lcchild;
u_int count; u_int count = 0;
switch (lc->type) { switch (lc->type) {
case LAYOUT_WINDOWPANE: case LAYOUT_WINDOWPANE:
return (1); return (1);
case LAYOUT_LEFTRIGHT: case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM: case LAYOUT_TOPBOTTOM:
count = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) TAILQ_FOREACH(lcchild, &lc->cells, entry)
count += layout_count_cells(lcchild); count += layout_count_cells(lcchild);
return (count); return (count);
@@ -370,16 +436,15 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
struct layout_cell *lcchild; struct layout_cell *lcchild;
struct style *sb_style = &w->active->scrollbar_style; struct style *sb_style = &w->active->scrollbar_style;
u_int available, minimum; u_int available, minimum;
int status, scrollbars; int status;
status = options_get_number(w->options, "pane-border-status"); status = options_get_number(w->options, "pane-border-status");
scrollbars = options_get_number(w->options, "pane-scrollbars");
if (lc->type == LAYOUT_WINDOWPANE) { if (lc->type == LAYOUT_WINDOWPANE) {
/* Space available in this cell only. */ /* Space available in this cell only. */
if (type == LAYOUT_LEFTRIGHT) { if (type == LAYOUT_LEFTRIGHT) {
available = lc->sx; available = lc->sx;
if (scrollbars) if (w->sb != PANE_SCROLLBARS_OFF)
minimum = PANE_MINIMUM + sb_style->width + minimum = PANE_MINIMUM + sb_style->width +
sb_style->pad; sb_style->pad;
else else
@@ -436,8 +501,11 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
/* Child cell runs in a different direction. */ /* Child cell runs in a different direction. */
if (lc->type != type) { if (lc->type != type) {
TAILQ_FOREACH(lcchild, &lc->cells, entry) TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->flags & LAYOUT_CELL_FLOATING)
continue;
layout_resize_adjust(w, lcchild, type, change); layout_resize_adjust(w, lcchild, type, change);
}
return; return;
} }
@@ -449,6 +517,8 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (change == 0) if (change == 0)
break; break;
if (lcchild->flags & LAYOUT_CELL_FLOATING)
continue;
if (change > 0) { if (change > 0) {
layout_resize_adjust(w, lcchild, type, 1); layout_resize_adjust(w, lcchild, type, 1);
change--; change--;
@@ -467,28 +537,33 @@ void
layout_destroy_cell(struct window *w, struct layout_cell *lc, layout_destroy_cell(struct window *w, struct layout_cell *lc,
struct layout_cell **lcroot) struct layout_cell **lcroot)
{ {
struct layout_cell *lcother, *lcparent; struct layout_cell *lcother = NULL, *lcparent;
/* /* If no parent, this is the last pane in a window. */
* If no parent, this is the last pane so window close is imminent and
* there is no need to resize anything.
*/
lcparent = lc->parent; lcparent = lc->parent;
if (lcparent == NULL) { if (lcparent == NULL) {
if (lc->wp != NULL)
*lcroot = NULL;
layout_free_cell(lc); layout_free_cell(lc);
*lcroot = NULL;
return; return;
} }
/* Merge the space into the previous or next cell. */ if (~lc->flags & LAYOUT_CELL_FLOATING) {
if (lc == TAILQ_FIRST(&lcparent->cells)) /* Merge the space into the previous or next cell. */
lcother = TAILQ_NEXT(lc, entry); if (lc == TAILQ_FIRST(&lcparent->cells))
else lcother = TAILQ_NEXT(lc, entry);
lcother = TAILQ_PREV(lc, layout_cells, entry); else
if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT) lcother = TAILQ_PREV(lc, layout_cells, entry);
layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1); }
else if (lcother != NULL) if (lcother != NULL && (~lcother->flags & LAYOUT_CELL_FLOATING)) {
layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1); if (lcparent->type == LAYOUT_LEFTRIGHT) {
layout_resize_adjust(w, lcother, lcparent->type,
lc->sx + 1);
} else {
layout_resize_adjust(w, lcother, lcparent->type,
lc->sy + 1);
}
}
/* Remove this from the parent's list. */ /* Remove this from the parent's list. */
TAILQ_REMOVE(&lcparent->cells, lc, entry); TAILQ_REMOVE(&lcparent->cells, lc, entry);
@@ -499,12 +574,15 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
* replace it by that cell. * replace it by that cell.
*/ */
lc = TAILQ_FIRST(&lcparent->cells); lc = TAILQ_FIRST(&lcparent->cells);
if (TAILQ_NEXT(lc, entry) == NULL) { if (lc != NULL && TAILQ_NEXT(lc, entry) == NULL) {
TAILQ_REMOVE(&lcparent->cells, lc, entry); TAILQ_REMOVE(&lcparent->cells, lc, entry);
lc->parent = lcparent->parent; lc->parent = lcparent->parent;
if (lc->parent == NULL) { if (lc->parent == NULL) {
lc->xoff = 0; lc->yoff = 0; if (~lc->flags & LAYOUT_CELL_FLOATING) {
lc->xoff = 0;
lc->yoff = 0;
}
*lcroot = lc; *lcroot = lc;
} else } else
TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry); TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
@@ -513,6 +591,7 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
} }
} }
/* Initialize layout for pane. */
void void
layout_init(struct window *w, struct window_pane *wp) layout_init(struct window *w, struct window_pane *wp)
{ {
@@ -524,6 +603,7 @@ layout_init(struct window *w, struct window_pane *wp)
layout_fix_panes(w, NULL); layout_fix_panes(w, NULL);
} }
/* Free layout for pane. */
void void
layout_free(struct window *w) layout_free(struct window *w)
{ {
@@ -550,6 +630,8 @@ layout_resize(struct window *w, u_int sx, u_int sy)
* out proportionately - this should leave the layout fitting the new * out proportionately - this should leave the layout fitting the new
* window size. * window size.
*/ */
if (lc->type == LAYOUT_WINDOWPANE && (lc->flags & LAYOUT_CELL_FLOATING))
return;
xchange = sx - lc->sx; xchange = sx - lc->sx;
xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT); xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
if (xchange < 0 && xchange < -xlimit) if (xchange < 0 && xchange < -xlimit)
@@ -741,7 +823,7 @@ layout_resize_pane_shrink(struct window *w, struct layout_cell *lc,
return (size); return (size);
} }
/* Assign window pane to newly split cell. */ /* Assign window pane to new cell. */
void void
layout_assign_pane(struct layout_cell *lc, struct window_pane *wp, layout_assign_pane(struct layout_cell *lc, struct window_pane *wp,
int do_not_resize) int do_not_resize)
@@ -862,6 +944,8 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc)
count = 0; count = 0;
previous = 0; previous = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->flags & LAYOUT_CELL_FLOATING)
continue;
count++; count++;
if (lc->type == LAYOUT_LEFTRIGHT) if (lc->type == LAYOUT_LEFTRIGHT)
previous += lcchild->sx; previous += lcchild->sx;
@@ -880,6 +964,8 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc)
/* Resize children into the new size. */ /* Resize children into the new size. */
idx = 0; idx = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->flags & LAYOUT_CELL_FLOATING)
continue;
if (lc->type == LAYOUT_TOPBOTTOM) { if (lc->type == LAYOUT_TOPBOTTOM) {
lcchild->sx = lc->sx; lcchild->sx = lc->sx;
lcchild->xoff = lc->xoff; lcchild->xoff = lc->xoff;
@@ -888,9 +974,10 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc)
lc->type, lc->sx, count - idx, available); lc->type, lc->sx, count - idx, available);
available -= (lcchild->sx + 1); available -= (lcchild->sx + 1);
} }
if (lc->type == LAYOUT_LEFTRIGHT) if (lc->type == LAYOUT_LEFTRIGHT) {
lcchild->sy = lc->sy; lcchild->sy = lc->sy;
else { lcchild->yoff = lc->yoff;
} else {
lcchild->sy = layout_new_pane_size(w, previous, lcchild, lcchild->sy = layout_new_pane_size(w, previous, lcchild,
lc->type, lc->sy, count - idx, available); lc->type, lc->sy, count - idx, available);
available -= (lcchild->sy + 1); available -= (lcchild->sy + 1);
@@ -913,7 +1000,6 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
u_int sx, sy, xoff, yoff, size1, size2, minimum; u_int sx, sy, xoff, yoff, size1, size2, minimum;
u_int new_size, saved_size, resize_first = 0; u_int new_size, saved_size, resize_first = 0;
int full_size = (flags & SPAWN_FULLSIZE), status; int full_size = (flags & SPAWN_FULLSIZE), status;
int scrollbars;
/* /*
* If full_size is specified, add a new cell at the top of the window * If full_size is specified, add a new cell at the top of the window
@@ -924,7 +1010,6 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
else else
lc = wp->layout_cell; lc = wp->layout_cell;
status = options_get_number(wp->window->options, "pane-border-status"); status = options_get_number(wp->window->options, "pane-border-status");
scrollbars = options_get_number(wp->window->options, "pane-scrollbars");
/* Copy the old cell size. */ /* Copy the old cell size. */
sx = lc->sx; sx = lc->sx;
@@ -935,7 +1020,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
/* Check there is enough space for the two new panes. */ /* Check there is enough space for the two new panes. */
switch (type) { switch (type) {
case LAYOUT_LEFTRIGHT: case LAYOUT_LEFTRIGHT:
if (scrollbars) { if (wp->window->sb != PANE_SCROLLBARS_OFF) {
minimum = PANE_MINIMUM * 2 + sb_style->width + minimum = PANE_MINIMUM * 2 + sb_style->width +
sb_style->pad; sb_style->pad;
} else } else
@@ -1079,14 +1164,51 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
return (lcnew); return (lcnew);
} }
/*
* Creates a cell for a new floating pane. This must be followed by
* layout_assign_pane before much else happens!
*/
struct layout_cell *
layout_floating_pane(struct window *w, u_int sx, u_int sy, int ox, int oy)
{
struct layout_cell *lc = w->layout_root, *lcnew, *lcparent;
if (lc->type == LAYOUT_WINDOWPANE) {
/*
* Adding a pane to a root that doesn't have a container. Must
* create and insert a new root.
*/
lcparent = layout_create_cell(NULL);
layout_make_node(lcparent, LAYOUT_TOPBOTTOM);
layout_set_size(lcparent, w->sx, w->sy, 0, 0);
w->layout_root = lcparent;
/* Insert the old cell. */
lc->parent = lcparent;
TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
} else
lcparent = w->layout_root;
lcnew = layout_create_cell(lcparent);
TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
lcnew->flags |= LAYOUT_CELL_FLOATING;
layout_set_size(lcnew, sx, sy, ox, oy);
return (lcnew);
}
/* Destroy the cell associated with a pane. */ /* Destroy the cell associated with a pane. */
void void
layout_close_pane(struct window_pane *wp) layout_close_pane(struct window_pane *wp)
{ {
struct window *w = wp->window; struct window *w = wp->window;
if (wp->layout_cell == NULL)
return;
/* Remove the cell. */ /* Remove the cell. */
layout_destroy_cell(w, wp->layout_cell, &w->layout_root); layout_destroy_cell(w, wp->layout_cell, &w->layout_root);
wp->layout_cell = NULL;
/* Fix pane offsets and sizes. */ /* Fix pane offsets and sizes. */
if (w->layout_root != NULL) { if (w->layout_root != NULL) {
@@ -1096,13 +1218,13 @@ layout_close_pane(struct window_pane *wp)
notify_window("window-layout-changed", w); notify_window("window-layout-changed", w);
} }
/* Spread out cells inside a parent cell. */
int int
layout_spread_cell(struct window *w, struct layout_cell *parent) layout_spread_cell(struct window *w, struct layout_cell *parent)
{ {
struct layout_cell *lc; struct layout_cell *lc;
struct style *sb_style = &w->active->scrollbar_style;
u_int number, each, size, this, remainder; u_int number, each, size, this, remainder;
int change, changed, status, scrollbars; int change, changed, status;
number = 0; number = 0;
TAILQ_FOREACH (lc, &parent->cells, entry) TAILQ_FOREACH (lc, &parent->cells, entry)
@@ -1110,14 +1232,9 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
if (number <= 1) if (number <= 1)
return (0); return (0);
status = options_get_number(w->options, "pane-border-status"); status = options_get_number(w->options, "pane-border-status");
scrollbars = options_get_number(w->options, "pane-scrollbars");
if (parent->type == LAYOUT_LEFTRIGHT) { if (parent->type == LAYOUT_LEFTRIGHT)
if (scrollbars) size = parent->sx;
size = parent->sx - sb_style->width + sb_style->pad;
else
size = parent->sx;
}
else if (parent->type == LAYOUT_TOPBOTTOM) { else if (parent->type == LAYOUT_TOPBOTTOM) {
if (layout_add_horizontal_border(w, parent, status)) if (layout_add_horizontal_border(w, parent, status))
size = parent->sy - 1; size = parent->sy - 1;
@@ -1130,6 +1247,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
each = (size - (number - 1)) / number; each = (size - (number - 1)) / number;
if (each == 0) if (each == 0)
return (0); return (0);
/* /*
* Remaining space after assigning that which can be evenly * Remaining space after assigning that which can be evenly
* distributed. * distributed.
@@ -1164,6 +1282,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
return (changed); return (changed);
} }
/* Spread out cells evenly. */
void void
layout_spread_out(struct window_pane *wp) layout_spread_out(struct window_pane *wp)
{ {
@@ -1182,3 +1301,122 @@ layout_spread_out(struct window_pane *wp)
} }
} while ((parent = parent->parent) != NULL); } while ((parent = parent->parent) != NULL);
} }
/* Get a new tiled cell. */
struct layout_cell *
layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
struct window *w, struct window_pane *wp, int flags, char **cause)
{
struct layout_cell *lc;
enum layout_type type;
u_int curval;
int size = -1;
if (window_pane_is_floating(wp)) {
*cause = xstrdup("can't split a floating pane");
return (NULL);
}
type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT;
if (args_has(args, 'l') || args_has(args, 'p')) {
if (args_has(args, 'f')) {
if (type == LAYOUT_TOPBOTTOM)
curval = w->sy;
else
curval = w->sx;
} else {
if (type == LAYOUT_TOPBOTTOM)
curval = wp->sy;
else
curval = wp->sx;
}
}
if (args_has(args, 'l')) {
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
item, cause);
} else if (args_has(args, 'p')) {
size = args_strtonum_and_expand(args, 'p', 0, 100, item,
cause);
if (*cause == NULL)
size = curval * size / 100;
}
if (*cause != NULL) {
*cause = xstrdup("invalid tiled geometry");
return (NULL);
}
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
lc = layout_split_pane(wp, type, size, flags);
if (lc == NULL)
*cause = xstrdup("no space for a new pane");
return (lc);
}
/* Get a new floating cell. */
struct layout_cell *
layout_get_floating_cell(struct cmdq_item *item, struct args *args,
struct window *w, __unused struct window_pane *wp, char **cause)
{
struct layout_cell *lcnew;
int sx = w->sx / 2, sy = w->sy / 4;
int ox = INT_MAX, oy = INT_MAX;
if (args_has(args, 'x')) {
sx = args_percentage_and_expand(args, 'x', 0, w->sx - 1, w->sx,
item, cause);
if (*cause != NULL)
return (NULL);
}
if (args_has(args, 'y')) {
sy = args_percentage_and_expand(args, 'y', 0, w->sy - 1, w->sy,
item, cause);
if (*cause != NULL)
return (NULL);
}
if (args_has(args, 'X')) {
ox = args_percentage_and_expand(args, 'X', -sx, w->sx,
w->sx, item, cause);
if (*cause != NULL)
return (NULL);
}
if (args_has(args, 'Y')) {
oy = args_percentage_and_expand(args, 'Y', -sy, w->sy,
w->sy, item, cause);
if (*cause != NULL)
return (NULL);
}
if (ox == INT_MAX) {
if (w->last_new_pane_x == 0)
ox = 4;
else {
ox = w->last_new_pane_x + 4;
if (w->last_new_pane_x > w->sx)
ox = 4;
}
w->last_new_pane_x = ox;
}
if (oy == INT_MAX) {
if (w->last_new_pane_y == 0)
oy = 2;
else {
oy = w->last_new_pane_y + 2;
if (w->last_new_pane_y > w->sy)
oy = 2;
}
w->last_new_pane_y = oy;
}
lcnew = layout_floating_pane(w, sx, sy, ox, oy);
return (lcnew);
}

122
menu.c
View File

@@ -27,13 +27,18 @@ struct menu_data {
struct cmdq_item *item; struct cmdq_item *item;
int flags; int flags;
struct grid_cell style; char *style;
struct grid_cell border_style; char *border_style;
struct grid_cell selected_style; char *selected_style;
struct grid_cell style_gc;
struct grid_cell border_style_gc;
struct grid_cell selected_style_gc;
enum box_lines border_lines; enum box_lines border_lines;
struct cmd_find_state fs; struct cmd_find_state fs;
struct screen s; struct screen s;
struct visible_ranges r;
u_int px; u_int px;
u_int py; u_int py;
@@ -85,6 +90,7 @@ menu_add_item(struct menu *menu, const struct menu_item *item,
else else
s = format_single(qitem, item->name, c, NULL, NULL, NULL); s = format_single(qitem, item->name, c, NULL, NULL, NULL);
if (*s == '\0') { /* no item if empty after format expanded */ if (*s == '\0') { /* no item if empty after format expanded */
free(s);
menu->count--; menu->count--;
return; return;
} }
@@ -156,6 +162,9 @@ menu_free(struct menu *menu)
{ {
u_int i; u_int i;
if (menu == NULL)
return;
for (i = 0; i < menu->count; i++) { for (i = 0; i < menu->count; i++) {
free((void *)menu->items[i].name); free((void *)menu->items[i].name);
free((void *)menu->items[i].command); free((void *)menu->items[i].command);
@@ -181,15 +190,70 @@ menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
} }
/* Return parts of the input range which are not obstructed by the menu. */ /* Return parts of the input range which are not obstructed by the menu. */
void struct visible_ranges *
menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py, menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py,
u_int nx, struct overlay_ranges *r) u_int nx)
{ {
struct menu_data *md = data; struct menu_data *md = data;
struct menu *menu = md->menu; struct menu *menu = md->menu;
server_client_overlay_range(md->px, md->py, menu->width + 4, server_client_overlay_range(md->px, md->py, menu->width + 4,
menu->count + 2, px, py, nx, r); menu->count + 2, px, py, nx, &md->r);
return (&md->r);
}
static void
menu_reapply_styles(struct menu_data *md, struct client *c)
{
struct session *s = c->session;
struct options *o;
struct format_tree *ft;
struct style sytmp;
if (s == NULL)
return;
o = s->curw->window->options;
ft = format_create_defaults(NULL, c, s, s->curw, NULL);
/* Reapply menu style from options. */
memcpy(&md->style_gc, &grid_default_cell, sizeof md->style_gc);
style_apply(&md->style_gc, o, "menu-style", ft);
if (md->style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &md->style_gc, md->style) == 0) {
md->style_gc.fg = sytmp.gc.fg;
md->style_gc.bg = sytmp.gc.bg;
}
}
/* Reapply selected style from options. */
memcpy(&md->selected_style_gc, &grid_default_cell,
sizeof md->selected_style_gc);
style_apply(&md->selected_style_gc, o, "menu-selected-style", ft);
if (md->selected_style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &md->selected_style_gc,
md->selected_style) == 0) {
md->selected_style_gc.fg = sytmp.gc.fg;
md->selected_style_gc.bg = sytmp.gc.bg;
}
}
/* Reapply border style from options. */
memcpy(&md->border_style_gc, &grid_default_cell,
sizeof md->border_style_gc);
style_apply(&md->border_style_gc, o, "menu-border-style", ft);
if (md->border_style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &md->border_style_gc,
md->border_style) == 0) {
md->border_style_gc.fg = sytmp.gc.fg;
md->border_style_gc.bg = sytmp.gc.bg;
}
}
format_free(ft);
} }
void void
@@ -203,16 +267,18 @@ menu_draw_cb(struct client *c, void *data,
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
u_int i, px = md->px, py = md->py; u_int i, px = md->px, py = md->py;
menu_reapply_styles(md, c);
screen_write_start(&ctx, s); screen_write_start(&ctx, s);
screen_write_clearscreen(&ctx, 8); screen_write_clearscreen(&ctx, 8);
if (md->border_lines != BOX_LINES_NONE) { if (md->border_lines != BOX_LINES_NONE) {
screen_write_box(&ctx, menu->width + 4, menu->count + 2, screen_write_box(&ctx, menu->width + 4, menu->count + 2,
md->border_lines, &md->border_style, menu->title); md->border_lines, &md->border_style_gc, menu->title);
} }
screen_write_menu(&ctx, menu, md->choice, md->border_lines, screen_write_menu(&ctx, menu, md->choice, md->border_lines,
&md->style, &md->border_style, &md->selected_style); &md->style_gc, &md->border_style_gc, &md->selected_style_gc);
screen_write_stop(&ctx); screen_write_stop(&ctx);
for (i = 0; i < screen_size_y(&md->s); i++) { for (i = 0; i < screen_size_y(&md->s); i++) {
@@ -232,8 +298,13 @@ menu_free_cb(__unused struct client *c, void *data)
if (md->cb != NULL) if (md->cb != NULL)
md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data); md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
free(md->r.ranges);
screen_free(&md->s); screen_free(&md->s);
menu_free(md->menu); menu_free(md->menu);
free(md->style);
free(md->selected_style);
free(md->border_style);
free(md); free(md);
} }
@@ -292,7 +363,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
name = menu->items[i].name; name = menu->items[i].name;
if (name == NULL || *name == '-') if (name == NULL || *name == '-')
continue; continue;
if (event->key == menu->items[i].key) { if ((event->key & ~KEYC_MASK_FLAGS) == menu->items[i].key) {
md->choice = i; md->choice = i;
goto chosen; goto chosen;
} }
@@ -400,6 +471,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
case '\r': case '\r':
goto chosen; goto chosen;
case '\033': /* Escape */ case '\033': /* Escape */
case '['|KEYC_CTRL:
case 'c'|KEYC_CTRL: case 'c'|KEYC_CTRL:
case 'g'|KEYC_CTRL: case 'g'|KEYC_CTRL:
case 'q': case 'q':
@@ -470,24 +542,6 @@ menu_resize_cb(struct client *c, void *data)
md->py = ny; md->py = ny;
} }
static void
menu_set_style(struct client *c, struct grid_cell *gc, const char *style,
const char *option)
{
struct style sytmp;
struct options *o = c->session->curw->window->options;
memcpy(gc, &grid_default_cell, sizeof *gc);
style_apply(gc, o, option, NULL);
if (style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, gc, style) == 0) {
gc->fg = sytmp.gc.fg;
gc->bg = sytmp.gc.bg;
}
}
}
struct menu_data * struct menu_data *
menu_prepare(struct menu *menu, int flags, int starting_choice, menu_prepare(struct menu *menu, int flags, int starting_choice,
struct cmdq_item *item, u_int px, u_int py, struct client *c, struct cmdq_item *item, u_int px, u_int py, struct client *c,
@@ -515,10 +569,12 @@ menu_prepare(struct menu *menu, int flags, int starting_choice,
md->flags = flags; md->flags = flags;
md->border_lines = lines; md->border_lines = lines;
menu_set_style(c, &md->style, style, "menu-style"); if (style != NULL)
menu_set_style(c, &md->selected_style, selected_style, md->style = xstrdup(style);
"menu-selected-style"); if (selected_style != NULL)
menu_set_style(c, &md->border_style, border_style, "menu-border-style"); md->selected_style = xstrdup(selected_style);
if (border_style != NULL)
md->border_style = xstrdup(border_style);
if (fs != NULL) if (fs != NULL)
cmd_find_copy_state(&md->fs, fs); cmd_find_copy_state(&md->fs, fs);
@@ -582,7 +638,7 @@ menu_display(struct menu *menu, int flags, int starting_choice,
style, selected_style, border_style, fs, cb, data); style, selected_style, border_style, fs, cb, data);
if (md == NULL) if (md == NULL)
return (-1); return (-1);
server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, server_client_set_overlay(c, 0, menu_check_cb, menu_mode_cb,
menu_key_cb, menu_free_cb, menu_resize_cb, md); menu_draw_cb, menu_key_cb, menu_free_cb, menu_resize_cb, md);
return (0); return (0);
} }

View File

@@ -48,9 +48,7 @@ struct mode_tree_data {
void *modedata; void *modedata;
const struct menu_item *menu; const struct menu_item *menu;
const char **sort_list; struct sort_criteria sort_crit;
u_int sort_size;
struct mode_tree_sort_criteria sort_crit;
mode_tree_build_cb buildcb; mode_tree_build_cb buildcb;
mode_tree_draw_cb drawcb; mode_tree_draw_cb drawcb;
@@ -59,6 +57,8 @@ struct mode_tree_data {
mode_tree_height_cb heightcb; mode_tree_height_cb heightcb;
mode_tree_key_cb keycb; mode_tree_key_cb keycb;
mode_tree_swap_cb swapcb; mode_tree_swap_cb swapcb;
mode_tree_sort_cb sortcb;
mode_tree_help_cb helpcb;
struct mode_tree_list children; struct mode_tree_list children;
struct mode_tree_list saved; struct mode_tree_list saved;
@@ -133,6 +133,35 @@ static const struct menu_item mode_tree_menu_items[] = {
{ NULL, KEYC_NONE, NULL } { NULL, KEYC_NONE, NULL }
}; };
static const char* mode_tree_help_start[] = {
"\r\033[1m Up, k \033[0m\016x\017 \033[0mMove cursor up\n",
"\r\033[1m Down, j \033[0m\016x\017 \033[0mMove cursor down\n",
"\r\033[1m g \033[0m\016x\017 \033[0mGo to top\n",
"\r\033[1m G \033[0m\016x\017 \033[0mGo to bottom\n",
"\r\033[1m PPage, C-b \033[0m\016x\017 \033[0mPage up\n",
"\r\033[1m NPage, C-f \033[0m\016x\017 \033[0mPage down\n",
"\r\033[1m Left, h \033[0m\016x\017 \033[0mCollapse %1\n",
"\r\033[1m Right, l \033[0m\016x\017 \033[0mExpand %1\n",
"\r\033[1m M-- \033[0m\016x\017 \033[0mCollapse all %1s\n",
"\r\033[1m M-+ \033[0m\016x\017 \033[0mExpand all %1s\n",
"\r\033[1m t \033[0m\016x\017 \033[0mToggle %1 tag\n",
"\r\033[1m T \033[0m\016x\017 \033[0mUntag all %1s\n",
"\r\033[1m C-t \033[0m\016x\017 \033[0mTag all %1s\n",
"\r\033[1m C-s \033[0m\016x\017 \033[0mSearch forward\n",
"\r\033[1m n \033[0m\016x\017 \033[0mRepeat search forward\n",
"\r\033[1m N \033[0m\016x\017 \033[0mRepeat search backward\n",
"\r\033[1m f \033[0m\016x\017 \033[0mFilter %1s\n",
"\r\033[1m O \033[0m\016x\017 \033[0mChange sort order\n",
"\r\033[1m r \033[0m\016x\017 \033[0mReverse sort order\n",
"\r\033[1m v \033[0m\016x\017 \033[0mToggle preview\n",
NULL
};
static const char* mode_tree_help_end[] = {
"\r\033[1m q, Escape \033[0m\016x\017 \033[0mExit mode\033[H",
NULL
};
#define MODE_TREE_HELP_DEFAULT_WIDTH 39
static int static int
mode_tree_is_lowercase(const char *ptr) mode_tree_is_lowercase(const char *ptr)
{ {
@@ -271,6 +300,8 @@ mode_tree_clear_tagged(struct mode_tree_list *mtl)
void void
mode_tree_up(struct mode_tree_data *mtd, int wrap) mode_tree_up(struct mode_tree_data *mtd, int wrap)
{ {
if (mtd->line_size == 0)
return;
if (mtd->current == 0) { if (mtd->current == 0) {
if (wrap) { if (wrap) {
mtd->current = mtd->line_size - 1; mtd->current = mtd->line_size - 1;
@@ -287,6 +318,8 @@ mode_tree_up(struct mode_tree_data *mtd, int wrap)
int int
mode_tree_down(struct mode_tree_data *mtd, int wrap) mode_tree_down(struct mode_tree_data *mtd, int wrap)
{ {
if (mtd->line_size == 0)
return (0);
if (mtd->current == mtd->line_size - 1) { if (mtd->current == mtd->line_size - 1) {
if (wrap) { if (wrap) {
mtd->current = 0; mtd->current = 0;
@@ -324,7 +357,7 @@ mode_tree_swap(struct mode_tree_data *mtd, int direction)
return; return;
if (mtd->swapcb(mtd->line_list[mtd->current].item->itemdata, if (mtd->swapcb(mtd->line_list[mtd->current].item->itemdata,
mtd->line_list[swap_with].item->itemdata)) { mtd->line_list[swap_with].item->itemdata, &mtd->sort_crit)) {
mtd->current = swap_with; mtd->current = swap_with;
mode_tree_build(mtd); mode_tree_build(mtd);
} }
@@ -333,6 +366,8 @@ mode_tree_swap(struct mode_tree_data *mtd, int direction)
void * void *
mode_tree_get_current(struct mode_tree_data *mtd) mode_tree_get_current(struct mode_tree_data *mtd)
{ {
if (mtd->line_size == 0)
return (NULL);
return (mtd->line_list[mtd->current].item->itemdata); return (mtd->line_list[mtd->current].item->itemdata);
} }
@@ -382,7 +417,7 @@ mode_tree_expand(struct mode_tree_data *mtd, uint64_t tag)
u_int found; u_int found;
if (!mode_tree_get_tag(mtd, tag, &found)) if (!mode_tree_get_tag(mtd, tag, &found))
return; return;
if (!mtd->line_list[found].item->expanded) { if (!mtd->line_list[found].item->expanded) {
mtd->line_list[found].item->expanded = 1; mtd->line_list[found].item->expanded = 1;
mode_tree_build(mtd); mode_tree_build(mtd);
@@ -403,6 +438,8 @@ mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
return (1); return (1);
} }
if (mtd->current >= mtd->line_size) { if (mtd->current >= mtd->line_size) {
if (mtd->line_size == 0)
return (0);
mtd->current = mtd->line_size - 1; mtd->current = mtd->line_size - 1;
if (mtd->current > mtd->height - 1) if (mtd->current > mtd->height - 1)
mtd->offset = mtd->current - mtd->height + 1; mtd->offset = mtd->current - mtd->height + 1;
@@ -454,12 +491,11 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb, mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
mode_tree_search_cb searchcb, mode_tree_menu_cb menucb, mode_tree_search_cb searchcb, mode_tree_menu_cb menucb,
mode_tree_height_cb heightcb, mode_tree_key_cb keycb, mode_tree_height_cb heightcb, mode_tree_key_cb keycb,
mode_tree_swap_cb swapcb, void *modedata, const struct menu_item *menu, mode_tree_swap_cb swapcb, mode_tree_sort_cb sortcb,
const char **sort_list, u_int sort_size, struct screen **s) mode_tree_help_cb helpcb, void *modedata, const struct menu_item *menu,
struct screen **s)
{ {
struct mode_tree_data *mtd; struct mode_tree_data *mtd;
const char *sort;
u_int i;
mtd = xcalloc(1, sizeof *mtd); mtd = xcalloc(1, sizeof *mtd);
mtd->references = 1; mtd->references = 1;
@@ -468,9 +504,6 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->modedata = modedata; mtd->modedata = modedata;
mtd->menu = menu; mtd->menu = menu;
mtd->sort_list = sort_list;
mtd->sort_size = sort_size;
if (args_has(args, 'N') > 1) if (args_has(args, 'N') > 1)
mtd->preview = MODE_TREE_PREVIEW_BIG; mtd->preview = MODE_TREE_PREVIEW_BIG;
else if (args_has(args, 'N')) else if (args_has(args, 'N'))
@@ -478,13 +511,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
else else
mtd->preview = MODE_TREE_PREVIEW_NORMAL; mtd->preview = MODE_TREE_PREVIEW_NORMAL;
sort = args_get(args, 'O'); mtd->sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort != NULL) {
for (i = 0; i < sort_size; i++) {
if (strcasecmp(sort, sort_list[i]) == 0)
mtd->sort_crit.field = i;
}
}
mtd->sort_crit.reversed = args_has(args, 'r'); mtd->sort_crit.reversed = args_has(args, 'r');
if (args_has(args, 'f')) if (args_has(args, 'f'))
@@ -499,6 +526,8 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->heightcb = heightcb; mtd->heightcb = heightcb;
mtd->keycb = keycb; mtd->keycb = keycb;
mtd->swapcb = swapcb; mtd->swapcb = swapcb;
mtd->sortcb = sortcb;
mtd->helpcb = helpcb;
TAILQ_INIT(&mtd->children); TAILQ_INIT(&mtd->children);
@@ -566,6 +595,8 @@ mode_tree_build(struct mode_tree_data *mtd)
TAILQ_CONCAT(&mtd->saved, &mtd->children, entry); TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
TAILQ_INIT(&mtd->children); TAILQ_INIT(&mtd->children);
if (mtd->sortcb != NULL)
mtd->sortcb(&mtd->sort_crit);
mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter); mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter);
mtd->no_matches = TAILQ_EMPTY(&mtd->children); mtd->no_matches = TAILQ_EMPTY(&mtd->children);
if (mtd->no_matches) if (mtd->no_matches)
@@ -851,9 +882,9 @@ mode_tree_draw(struct mode_tree_data *mtd)
screen_write_cursormove(&ctx, 0, h, 0); screen_write_cursormove(&ctx, 0, h, 0);
screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, NULL, NULL); screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, NULL, NULL);
if (mtd->sort_list != NULL) { if (mtd->sort_crit.order_seq != NULL) {
xasprintf(&text, " %s (sort: %s%s)", mti->name, xasprintf(&text, " %s (sort: %s%s)", mti->name,
mtd->sort_list[mtd->sort_crit.field], sort_order_to_string(mtd->sort_crit.order),
mtd->sort_crit.reversed ? ", reversed" : ""); mtd->sort_crit.reversed ? ", reversed" : "");
} else } else
xasprintf(&text, " %s", mti->name); xasprintf(&text, " %s", mti->name);
@@ -1132,6 +1163,57 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
} }
} }
static void
mode_tree_display_help(__unused struct mode_tree_data *mtd, struct client *c)
{
struct session *s = c->session;
u_int px, py, w, h = 0;
const char **line, **lines = NULL, *item = "item";
char *new_line;
if (mtd->helpcb == NULL)
w = MODE_TREE_HELP_DEFAULT_WIDTH;
else {
lines = mtd->helpcb(&w, &item);
if (w < MODE_TREE_HELP_DEFAULT_WIDTH)
w = MODE_TREE_HELP_DEFAULT_WIDTH;
}
for (line = mode_tree_help_start; *line != NULL; line++)
h++;
for (line = lines; line != NULL && *line != NULL; line++)
h++;
for (line = mode_tree_help_end; *line != NULL; line++)
h++;
if (c->tty.sx < w || c->tty.sy < h)
return;
px = (c->tty.sx - w) / 2;
py = (c->tty.sy - h) / 2;
if (popup_display(POPUP_CLOSEANYKEY|POPUP_NOJOB, BOX_LINES_DEFAULT,
NULL, px, py, w, h, NULL, NULL, 0, NULL, NULL, NULL, c, s, NULL,
NULL, NULL, NULL) != 0)
return;
popup_write(c, "\033[H\033[?25l\033[?7l\033)0", 17);
for (line = mode_tree_help_start; *line != NULL; line++) {
new_line = cmd_template_replace(*line, item, 1);
popup_write(c, new_line, strlen(new_line));
free(new_line);
}
for (line = lines; line != NULL && *line != NULL; line++) {
new_line = cmd_template_replace(*line, item, 1);
popup_write(c, new_line, strlen(new_line));
free(new_line);
}
for (line = mode_tree_help_end; *line != NULL; line++) {
new_line = cmd_template_replace(*line, item, 1);
popup_write(c, new_line, strlen(new_line));
free(new_line);
}
popup_write(c, "\033[H", 3);
}
int int
mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
struct mouse_event *m, u_int *xp, u_int *yp) struct mouse_event *m, u_int *xp, u_int *yp)
@@ -1139,7 +1221,12 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
struct mode_tree_line *line; struct mode_tree_line *line;
struct mode_tree_item *current, *parent, *mti; struct mode_tree_item *current, *parent, *mti;
u_int i, x, y; u_int i, x, y;
int choice; int choice, preview;
if (mtd->line_size == 0) {
*key = KEYC_NONE;
return (1);
}
if (KEYC_IS_MOUSE(*key) && m != NULL) { if (KEYC_IS_MOUSE(*key) && m != NULL) {
if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) { if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
@@ -1151,9 +1238,10 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
if (yp != NULL) if (yp != NULL)
*yp = y; *yp = y;
if (x > mtd->width || y > mtd->height) { if (x > mtd->width || y > mtd->height) {
preview = mtd->preview;
if (*key == KEYC_MOUSEDOWN3_PANE) if (*key == KEYC_MOUSEDOWN3_PANE)
mode_tree_display_menu(mtd, c, x, y, 1); mode_tree_display_menu(mtd, c, x, y, 1);
if (mtd->preview == MODE_TREE_PREVIEW_OFF) if (preview == MODE_TREE_PREVIEW_OFF)
*key = KEYC_NONE; *key = KEYC_NONE;
return (0); return (0);
} }
@@ -1200,8 +1288,13 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
switch (*key) { switch (*key) {
case 'q': case 'q':
case '\033': /* Escape */ case '\033': /* Escape */
case '['|KEYC_CTRL:
case 'g'|KEYC_CTRL: case 'g'|KEYC_CTRL:
return (1); return (1);
case KEYC_F1:
case 'h'|KEYC_CTRL:
mode_tree_display_help(mtd, c);
break;
case KEYC_UP: case KEYC_UP:
case 'k': case 'k':
case KEYC_WHEELUP_PANE: case KEYC_WHEELUP_PANE:
@@ -1287,9 +1380,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
} }
break; break;
case 'O': case 'O':
mtd->sort_crit.field++; sort_next_order(&mtd->sort_crit);
if (mtd->sort_crit.field >= mtd->sort_size)
mtd->sort_crit.field = 0;
mode_tree_build(mtd); mode_tree_build(mtd);
break; break;
case 'r': case 'r':

View File

@@ -95,7 +95,7 @@ check_window_name(struct window *w)
name = format_window_name(w); name = format_window_name(w);
if (strcmp(name, w->name) != 0) { if (strcmp(name, w->name) != 0) {
log_debug("@%u new name %s (was %s)", w->id, name, w->name); log_debug("@%u new name %s (was %s)", w->id, name, w->name);
window_set_name(w, name); window_set_name(w, name, 1);
server_redraw_window_borders(w); server_redraw_window_borders(w);
server_status_window(w); server_status_window(w);
} else } else
@@ -166,7 +166,9 @@ parse_window_name(const char *in)
if (*name == '/') if (*name == '/')
name = basename(name); name = basename(name);
name = xstrdup(name); name = clean_name(name, 0);
free(copy); free(copy);
if (name == NULL)
return (xstrdup(""));
return (name); return (name);
} }

View File

@@ -208,8 +208,12 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
format_add(ne->formats, "hook_window", "@%u", w->id); format_add(ne->formats, "hook_window", "@%u", w->id);
format_add(ne->formats, "hook_window_name", "%s", w->name); format_add(ne->formats, "hook_window_name", "%s", w->name);
} }
if (wp != NULL) if (wp != NULL) {
format_add(ne->formats, "hook_pane", "%%%d", wp->id); format_add(ne->formats, "hook_pane", "%%%d", wp->id);
format_add(ne->formats, "hook_window", "@%u", wp->window->id);
format_add(ne->formats, "hook_window_name", "%s",
wp->window->name);
}
format_log_debug(ne->formats, __func__); format_log_debug(ne->formats, __func__);
if (c != NULL) if (c != NULL)

View File

@@ -84,11 +84,14 @@ static const char *options_table_popup_border_lines_list[] = {
static const char *options_table_set_clipboard_list[] = { static const char *options_table_set_clipboard_list[] = {
"off", "external", "on", NULL "off", "external", "on", NULL
}; };
static const char *options_table_get_clipboard_list[] = {
"off", "buffer", "request", "both", NULL
};
static const char *options_table_window_size_list[] = { static const char *options_table_window_size_list[] = {
"largest", "smallest", "manual", "latest", NULL "largest", "smallest", "manual", "latest", NULL
}; };
static const char *options_table_remain_on_exit_list[] = { static const char *options_table_remain_on_exit_list[] = {
"off", "on", "failed", NULL "off", "on", "failed", "key", NULL
}; };
static const char *options_table_destroy_unattached_list[] = { static const char *options_table_destroy_unattached_list[] = {
"off", "on", "keep-last", "keep-group", NULL "off", "on", "keep-last", "keep-group", NULL
@@ -105,6 +108,9 @@ static const char *options_table_extended_keys_format_list[] = {
static const char *options_table_allow_passthrough_list[] = { static const char *options_table_allow_passthrough_list[] = {
"off", "on", "all", NULL "off", "on", "all", NULL
}; };
static const char *options_table_copy_mode_line_numbers_list[] = {
"off", "default", "absolute", "relative", "hybrid", NULL
};
/* Status line format. */ /* Status line format. */
#define OPTIONS_TABLE_STATUS_FORMAT1 \ #define OPTIONS_TABLE_STATUS_FORMAT1 \
@@ -138,7 +144,7 @@ static const char *options_table_allow_passthrough_list[] = {
"#{T:window-status-format}" \ "#{T:window-status-format}" \
"#[pop-default]" \ "#[pop-default]" \
"#[norange default]" \ "#[norange default]" \
"#{?loop_last_flag,,#{window-status-separator}}" \ "#{?loop_last_flag,,#{E:window-status-separator}}" \
"," \ "," \
"#[range=window|#{window_index} list=focus " \ "#[range=window|#{window_index} list=focus " \
"#{?#{!=:#{E:window-status-current-style},default}," \ "#{?#{!=:#{E:window-status-current-style},default}," \
@@ -165,7 +171,7 @@ static const char *options_table_allow_passthrough_list[] = {
"#{T:window-status-current-format}" \ "#{T:window-status-current-format}" \
"#[pop-default]" \ "#[pop-default]" \
"#[norange list=on default]" \ "#[norange list=on default]" \
"#{?loop_last_flag,,#{window-status-separator}}" \ "#{?loop_last_flag,,#{E:window-status-separator}}" \
"}" \ "}" \
"#[nolist align=right range=right #{E:status-right-style}]" \ "#[nolist align=right range=right #{E:status-right-style}]" \
"#[push-default]" \ "#[push-default]" \
@@ -182,7 +188,7 @@ static const char *options_table_allow_passthrough_list[] = {
"#{E:pane-status-style}" \ "#{E:pane-status-style}" \
"]" \ "]" \
"#[push-default]" \ "#[push-default]" \
"#P[#{pane_width}x#{pane_height}]" \ "#{T:window-pane-status-format}" \
"#[pop-default]" \ "#[pop-default]" \
"#[norange list=on default] " \ "#[norange list=on default] " \
"," \ "," \
@@ -193,7 +199,7 @@ static const char *options_table_allow_passthrough_list[] = {
"}" \ "}" \
"]" \ "]" \
"#[push-default]" \ "#[push-default]" \
"#P[#{pane_width}x#{pane_height}]*" \ "#{T:window-pane-current-status-format}" \
"#[pop-default]" \ "#[pop-default]" \
"#[norange list=on default] " \ "#[norange list=on default] " \
"}" "}"
@@ -405,6 +411,18 @@ const struct options_table_entry options_table[] = {
.text = "Whether to send focus events to applications." .text = "Whether to send focus events to applications."
}, },
{ .name = "get-clipboard",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SERVER,
.choices = options_table_get_clipboard_list,
.default_num = 1,
.text = "When an application requests the clipboard, whether to "
"ignore the request ('off'); respond with the newest buffer "
"('buffer'); request the clipboard from the most recently "
"used terminal ('request'); or to request the clipboard, "
"create a buffer, and send it to the application ('both')."
},
{ .name = "history-file", { .name = "history-file",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER, .scope = OPTIONS_TABLE_SERVER,
@@ -649,6 +667,13 @@ const struct options_table_entry options_table[] = {
.text = "Time for which status line messages should appear." .text = "Time for which status line messages should appear."
}, },
{ .name = "focus-follows-mouse",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 0,
.text = "Whether moving the mouse into a pane selects it."
},
{ .name = "history-limit", { .name = "history-limit",
.type = OPTIONS_TABLE_NUMBER, .type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
@@ -701,13 +726,24 @@ const struct options_table_entry options_table[] = {
{ .name = "message-command-style", { .name = "message-command-style",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=black,fg=yellow", .default_str = "bg=black,fg=yellow,fill=black",
.flags = OPTIONS_TABLE_IS_STYLE, .flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",", .separator = ",",
.text = "Style of the command prompt when in command mode, if " .text = "Style of the command prompt when in command mode, if "
"'mode-keys' is set to 'vi'." "'mode-keys' is set to 'vi'."
}, },
{ .name = "message-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "#[#{?#{command_prompt},"
"#{E:message-command-style},"
"#{E:message-style}}]"
"#{message}",
.text = "Format string for the prompt and message area. "
"The '#{message}' placeholder is replaced with the content."
},
{ .name = "message-line", { .name = "message-line",
.type = OPTIONS_TABLE_CHOICE, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
@@ -719,10 +755,13 @@ const struct options_table_entry options_table[] = {
{ .name = "message-style", { .name = "message-style",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=yellow,fg=black", .default_str = "bg=yellow,fg=black,fill=yellow",
.flags = OPTIONS_TABLE_IS_STYLE, .flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",", .separator = ",",
.text = "Style of messages and the command prompt." .text = "Style of messages and the command prompt. "
"A 'fill' attribute controls background clearing and "
"a 'width' attribute (fixed or percentage) constrains "
"the prompt area width."
}, },
{ .name = "mouse", { .name = "mouse",
@@ -943,7 +982,7 @@ const struct options_table_entry options_table[] = {
{ .name = "prompt-cursor-colour", { .name = "prompt-cursor-colour",
.type = OPTIONS_TABLE_COLOUR, .type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.default_num = 6, .default_num = -1,
.text = "Colour of the cursor when in the command prompt." .text = "Colour of the cursor when in the command prompt."
}, },
@@ -955,6 +994,15 @@ const struct options_table_entry options_table[] = {
.text = "Style of the cursor when in the command prompt." .text = "Style of the cursor when in the command prompt."
}, },
{ .name = "prompt-command-cursor-style",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_cursor_style_list,
.default_num = 0,
.text = "Style of the cursor in the command prompt when in command "
"mode, if 'status-keys' is set to 'vi'."
},
{ .name = "session-status-current-style", { .name = "session-status-current-style",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW,
@@ -979,7 +1027,9 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.flags = OPTIONS_TABLE_IS_ARRAY, .flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "DISPLAY KRB5CCNAME MSYSTEM SSH_ASKPASS SSH_AUTH_SOCK " .default_str = "DISPLAY KRB5CCNAME MSYSTEM SSH_ASKPASS SSH_AUTH_SOCK "
"SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY", "SSH_AGENT_PID SSH_CONNECTION WAYLAND_DISPLAY "
"WINDOWID XAUTHORITY XDG_CURRENT_DESKTOP "
"XDG_SESSION_DESKTOP XDG_SESSION_TYPE",
.text = "List of environment variables to update in the session " .text = "List of environment variables to update in the session "
"environment when a client is attached." "environment when a client is attached."
}, },
@@ -1130,7 +1180,7 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "#[align=right]" .default_str = "#[align=right]"
"#{t/p:top_line_time}#{?#{e|>:#{top_line_time},0}, ,}" "#{t/p:top_line_time}#{?#{e|>:#{top_line_time},0}, ,}"
"[#{scroll_position}/#{history_size}]" "[#{copy_position}/#{copy_position_limit}]"
"#{?search_timed_out, (timed out)," "#{?search_timed_out, (timed out),"
"#{?search_count, (#{search_count}" "#{?search_count, (#{search_count}"
"#{?search_count_partial,+,} results),}}", "#{?search_count_partial,+,} results),}}",
@@ -1155,6 +1205,32 @@ const struct options_table_entry options_table[] = {
.text = "Style of selection in copy mode." .text = "Style of selection in copy mode."
}, },
{ .name = "copy-mode-current-line-number-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "fg=yellow",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of current line number in copy mode."
},
{ .name = "copy-mode-line-number-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "fg=white,dim",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of line numbers in copy mode."
},
{ .name = "copy-mode-line-numbers",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_copy_mode_line_numbers_list,
.default_num = 0,
.text = "Line number mode in copy mode."
},
{ .name = "fill-character", { .name = "fill-character",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW,
@@ -1238,7 +1314,7 @@ const struct options_table_entry options_table[] = {
{ .name = "pane-active-border-style", { .name = "pane-active-border-style",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "#{?pane_in_mode,fg=yellow,#{?synchronize-panes,fg=red,fg=green}}", .default_str = "#{?pane_in_mode,fg=yellow,#{?synchronize-panes,fg=red,fg=green}}",
.flags = OPTIONS_TABLE_IS_STYLE, .flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",", .separator = ",",
@@ -1258,7 +1334,14 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "#{?pane_active,#[reverse],}#{pane_index}#[default] " .default_str = "#{?pane_active,#[reverse],}#{pane_index}#[default] "
"\"#{pane_title}\"", "\"#{pane_title}\""
"#{?#{mouse},"
"#[align=right]"
"#[range=control|8]["
"#{?#{window_zoomed_flag},u,z}"
"]#[norange]"
"#[range=control|9][x]#[norange]"
",}",
.text = "Format of text in the pane status lines." .text = "Format of text in the pane status lines."
}, },
@@ -1290,7 +1373,7 @@ const struct options_table_entry options_table[] = {
{ .name = "pane-border-style", { .name = "pane-border-style",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "default", .default_str = "default",
.flags = OPTIONS_TABLE_IS_STYLE, .flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",", .separator = ",",
@@ -1362,8 +1445,9 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.choices = options_table_remain_on_exit_list, .choices = options_table_remain_on_exit_list,
.default_num = 0, .default_num = 0,
.text = "Whether panes should remain ('on') or be automatically " .text = "Whether panes should remain ('on'), remain until a key is "
"killed ('off' or 'failed') when the program inside exits." "pressed ('key') or be automatically killed ('off' or "
"'failed') when the program inside exits."
}, },
{ .name = "remain-on-exit-format", { .name = "remain-on-exit-format",
@@ -1404,6 +1488,28 @@ const struct options_table_entry options_table[] = {
"A value of 0 means no limit." "A value of 0 means no limit."
}, },
{ .name = "tree-mode-preview-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "#{?pane_format,"
"#{pane_index}:#{pane_title},"
"#{window_index}:#{window_name}}",
.text = "Format of the preview indicator in tree mode."
},
{ .name = "tree-mode-preview-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "fg=#{?#{||:"
"#{&&:#{pane_format},#{pane_active}},"
"#{&&:#{window_format},#{window_active}}},"
"#{display-panes-active-colour},"
"#{display-panes-colour}}",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of preview indicator in tree mode."
},
{ .name = "window-active-style", { .name = "window-active-style",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
@@ -1413,6 +1519,21 @@ const struct options_table_entry options_table[] = {
.text = "Default style of the active pane." .text = "Default style of the active pane."
}, },
{ .name = "window-pane-current-status-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "#P:[#T]#{?pane_flags,#{pane_flags}, }",
.text = "Format of the current window pane in the status line."
},
{ .name = "window-pane-status-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "#P:[#T]#{?pane_flags,#{pane_flags}, }",
.text = "Format of window panes in the status line, except the "
"current pane."
},
{ .name = "window-size", { .name = "window-size",
.type = OPTIONS_TABLE_CHOICE, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW,

View File

@@ -749,7 +749,7 @@ options_get_number(struct options *oo, const char *name)
return (o->value.number); return (o->value.number);
} }
const struct cmd_list * struct cmd_list *
options_get_command(struct options *oo, const char *name) options_get_command(struct options *oo, const char *name)
{ {
struct options_entry *o; struct options_entry *o;
@@ -1226,6 +1226,10 @@ options_push_changes(const char *name)
RB_FOREACH(wp, window_pane_tree, &all_window_panes) RB_FOREACH(wp, window_pane_tree, &all_window_panes)
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
} }
if (*name == '@') {
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
wp->flags |= PANE_STYLECHANGED;
}
if (strcmp(name, "pane-colours") == 0) { if (strcmp(name, "pane-colours") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes) RB_FOREACH(wp, window_pane_tree, &all_window_panes)
colour_palette_from_option(&wp->palette, wp->options); colour_palette_from_option(&wp->palette, wp->options);
@@ -1233,8 +1237,13 @@ options_push_changes(const char *name)
if (strcmp(name, "pane-border-status") == 0 || if (strcmp(name, "pane-border-status") == 0 ||
strcmp(name, "pane-scrollbars") == 0 || strcmp(name, "pane-scrollbars") == 0 ||
strcmp(name, "pane-scrollbars-position") == 0) { strcmp(name, "pane-scrollbars-position") == 0) {
RB_FOREACH(w, windows, &windows) RB_FOREACH(w, windows, &windows) {
w->sb = options_get_number(w->options,
"pane-scrollbars");
w->sb_pos = options_get_number(w->options,
"pane-scrollbars-position");
layout_fix_panes(w, NULL); layout_fix_panes(w, NULL);
}
} }
if (strcmp(name, "pane-scrollbars-style") == 0) { if (strcmp(name, "pane-scrollbars-style") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes) { RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
@@ -1248,6 +1257,10 @@ options_push_changes(const char *name)
utf8_update_width_cache(); utf8_update_width_cache();
if (strcmp(name, "input-buffer-size") == 0) if (strcmp(name, "input-buffer-size") == 0)
input_set_buffer_size(options_get_number(global_options, name)); input_set_buffer_size(options_get_number(global_options, name));
if (strcmp(name, "history-limit") == 0) {
RB_FOREACH(s, sessions, &sessions)
session_update_history(s);
}
RB_FOREACH(s, sessions, &sessions) RB_FOREACH(s, sessions, &sessions)
status_update_cache(s); status_update_cache(s);

52
paste.c
View File

@@ -29,19 +29,6 @@
* string! * string!
*/ */
struct paste_buffer {
char *data;
size_t size;
char *name;
time_t created;
int automatic;
u_int order;
RB_ENTRY(paste_buffer) name_entry;
RB_ENTRY(paste_buffer) time_entry;
};
static u_int paste_next_index; static u_int paste_next_index;
static u_int paste_next_order; static u_int paste_next_order;
static u_int paste_num_automatic; static u_int paste_num_automatic;
@@ -119,7 +106,7 @@ paste_is_empty(void)
/* Get the most recent automatic buffer. */ /* Get the most recent automatic buffer. */
struct paste_buffer * struct paste_buffer *
paste_get_top(const char **name) paste_get_top(char **name)
{ {
struct paste_buffer *pb; struct paste_buffer *pb;
@@ -129,7 +116,7 @@ paste_get_top(const char **name)
if (pb == NULL) if (pb == NULL)
return (NULL); return (NULL);
if (name != NULL) if (name != NULL)
*name = pb->name; *name = xstrdup(pb->name);
return (pb); return (pb);
} }
@@ -189,7 +176,6 @@ paste_add(const char *prefix, char *data, size_t size)
} }
pb = xmalloc(sizeof *pb); pb = xmalloc(sizeof *pb);
pb->name = NULL; pb->name = NULL;
do { do {
free(pb->name); free(pb->name);
@@ -217,6 +203,7 @@ int
paste_rename(const char *oldname, const char *newname, char **cause) paste_rename(const char *oldname, const char *newname, char **cause)
{ {
struct paste_buffer *pb, *pb_new; struct paste_buffer *pb, *pb_new;
char *name;
if (cause != NULL) if (cause != NULL)
*cause = NULL; *cause = NULL;
@@ -232,23 +219,33 @@ paste_rename(const char *oldname, const char *newname, char **cause)
return (-1); return (-1);
} }
name = clean_name(newname, 0);
if (name == NULL) {
if (cause != NULL)
xasprintf(cause, "invalid buffer name: %s", newname);
return (-1);
}
pb = paste_get_name(oldname); pb = paste_get_name(oldname);
if (pb == NULL) { if (pb == NULL) {
if (cause != NULL) if (cause != NULL)
xasprintf(cause, "no buffer %s", oldname); xasprintf(cause, "no buffer %s", oldname);
free(name);
return (-1); return (-1);
} }
pb_new = paste_get_name(newname); pb_new = paste_get_name(name);
if (pb_new == pb) if (pb_new == pb) {
free(name);
return (0); return (0);
}
if (pb_new != NULL) if (pb_new != NULL)
paste_free(pb_new); paste_free(pb_new);
RB_REMOVE(paste_name_tree, &paste_by_name, pb); RB_REMOVE(paste_name_tree, &paste_by_name, pb);
free(pb->name); free(pb->name);
pb->name = xstrdup(newname); pb->name = name;
if (pb->automatic) if (pb->automatic)
paste_num_automatic--; paste_num_automatic--;
@@ -257,7 +254,7 @@ paste_rename(const char *oldname, const char *newname, char **cause)
RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_name_tree, &paste_by_name, pb);
notify_paste_buffer(oldname, 1); notify_paste_buffer(oldname, 1);
notify_paste_buffer(newname, 0); notify_paste_buffer(pb->name, 0);
return (0); return (0);
} }
@@ -270,6 +267,7 @@ int
paste_set(char *data, size_t size, const char *name, char **cause) paste_set(char *data, size_t size, const char *name, char **cause)
{ {
struct paste_buffer *pb, *old; struct paste_buffer *pb, *old;
char *newname;
if (cause != NULL) if (cause != NULL)
*cause = NULL; *cause = NULL;
@@ -289,9 +287,15 @@ paste_set(char *data, size_t size, const char *name, char **cause)
return (-1); return (-1);
} }
pb = xmalloc(sizeof *pb); newname = clean_name(name, 0);
if (newname == NULL) {
if (cause != NULL)
xasprintf(cause, "invalid buffer name: %s", name);
return (-1);
}
pb->name = xstrdup(name); pb = xmalloc(sizeof *pb);
pb->name = newname;
pb->data = data; pb->data = data;
pb->size = size; pb->size = size;
@@ -301,13 +305,13 @@ paste_set(char *data, size_t size, const char *name, char **cause)
pb->created = time(NULL); pb->created = time(NULL);
if ((old = paste_get_name(name)) != NULL) if ((old = paste_get_name(pb->name)) != NULL)
paste_free(old); paste_free(old);
RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_name_tree, &paste_by_name, pb);
RB_INSERT(paste_time_tree, &paste_by_time, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb);
notify_paste_buffer(name, 0); notify_paste_buffer(pb->name, 0);
return (0); return (0);
} }

196
popup.c
View File

@@ -32,6 +32,8 @@ struct popup_data {
int flags; int flags;
char *title; char *title;
char *style;
char *border_style;
struct grid_cell border_cell; struct grid_cell border_cell;
enum box_lines border_lines; enum box_lines border_lines;
@@ -39,6 +41,9 @@ struct popup_data {
struct grid_cell defaults; struct grid_cell defaults;
struct colour_palette palette; struct colour_palette palette;
struct visible_ranges r;
struct visible_ranges or[2];
struct job *job; struct job *job;
struct input_ctx *ictx; struct input_ctx *ictx;
int status; int status;
@@ -98,6 +103,70 @@ static const struct menu_item popup_internal_menu_items[] = {
{ NULL, KEYC_NONE, NULL } { NULL, KEYC_NONE, NULL }
}; };
static void
popup_free(struct popup_data *pd)
{
server_client_unref(pd->c);
if (pd->job != NULL)
job_free(pd->job);
input_free(pd->ictx);
free(pd->or[0].ranges);
free(pd->or[1].ranges);
free(pd->r.ranges);
screen_free(&pd->s);
colour_palette_free(&pd->palette);
free(pd->title);
free(pd->style);
free(pd->border_style);
free(pd);
}
static void
popup_reapply_styles(struct popup_data *pd)
{
struct client *c = pd->c;
struct session *s = c->session;
struct options *o;
struct format_tree *ft;
struct style sytmp;
if (s == NULL)
return;
o = s->curw->window->options;
ft = format_create_defaults(NULL, c, s, s->curw, NULL);
/* Reapply popup style from options. */
memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults);
style_apply(&pd->defaults, o, "popup-style", ft);
if (pd->style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &pd->defaults, pd->style) == 0) {
pd->defaults.fg = sytmp.gc.fg;
pd->defaults.bg = sytmp.gc.bg;
}
}
pd->defaults.attr = 0;
/* Reapply border style from options. */
memcpy(&pd->border_cell, &grid_default_cell, sizeof pd->border_cell);
style_apply(&pd->border_cell, o, "popup-border-style", ft);
if (pd->border_style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, &pd->border_cell,
pd->border_style) == 0) {
pd->border_cell.fg = sytmp.gc.fg;
pd->border_cell.bg = sytmp.gc.bg;
}
}
pd->border_cell.attr = 0;
format_free(ft);
}
static void static void
popup_redraw_cb(const struct tty_ctx *ttyctx) popup_redraw_cb(const struct tty_ctx *ttyctx)
{ {
@@ -116,7 +185,6 @@ popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
if (pd->c->flags & CLIENT_REDRAWOVERLAY) if (pd->c->flags & CLIENT_REDRAWOVERLAY)
return (0); return (0);
ttyctx->bigger = 0;
ttyctx->wox = 0; ttyctx->wox = 0;
ttyctx->woy = 0; ttyctx->woy = 0;
ttyctx->wsx = c->tty.sx; ttyctx->wsx = c->tty.sx;
@@ -139,6 +207,7 @@ popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
struct popup_data *pd = ctx->arg; struct popup_data *pd = ctx->arg;
memcpy(&ttyctx->defaults, &pd->defaults, sizeof ttyctx->defaults); memcpy(&ttyctx->defaults, &pd->defaults, sizeof ttyctx->defaults);
ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER;
ttyctx->palette = &pd->palette; ttyctx->palette = &pd->palette;
ttyctx->redraw_cb = popup_redraw_cb; ttyctx->redraw_cb = popup_redraw_cb;
ttyctx->set_client_cb = popup_set_client_cb; ttyctx->set_client_cb = popup_set_client_cb;
@@ -164,48 +233,57 @@ popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
} }
/* Return parts of the input range which are not obstructed by the popup. */ /* Return parts of the input range which are not obstructed by the popup. */
static void static struct visible_ranges *
popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx, popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx)
struct overlay_ranges *r)
{ {
struct popup_data *pd = data; struct popup_data *pd = data;
struct overlay_ranges or[2]; struct visible_ranges *r = &pd->r;
struct visible_ranges *mr;
u_int i, j, k = 0; u_int i, j, k = 0;
if (pd->md != NULL) { if (pd->md != NULL) {
/* Check each returned range for the menu against the popup. */ /*
menu_check_cb(c, pd->md, px, py, nx, r); * Work out the visible ranges for the menu (that is, the
for (i = 0; i < 2; i++) { * ranges not covered by the menu). A menu should have at most
* two ranges and we rely on this being the case.
*/
mr = menu_check_cb(c, pd->md, px, py, nx);
if (mr->used > 2)
fatalx("too many menu ranges");
/*
* Walk the ranges still visible under the menu and check if
* each is visible under the popup as well. At most there can be
* three total ranges if popup and menu do not intersect.
*/
for (i = 0; i < mr->used; i++) {
server_client_overlay_range(pd->px, pd->py, pd->sx, server_client_overlay_range(pd->px, pd->py, pd->sx,
pd->sy, r->px[i], py, r->nx[i], &or[i]); pd->sy, r->ranges[i].px, py, r->ranges[i].nx,
&pd->or[i]);
} }
/* /*
* or has up to OVERLAY_MAX_RANGES non-overlapping ranges, * We now have nonoverlapping ranges from left to right.
* ordered from left to right. Collect them in the output. * Combine them together into the output.
*/ */
for (i = 0; i < 2; i++) { server_client_ensure_ranges(r, 3);
/* Each or[i] only has 2 ranges. */ for (i = 0; i < mr->used; i++) {
for (j = 0; j < 2; j++) { for (j = 0; j < pd->or[i].used; j++) {
if (or[i].nx[j] > 0) { if (pd->or[i].ranges[j].nx == 0)
r->px[k] = or[i].px[j]; continue;
r->nx[k] = or[i].nx[j]; if (k >= 3)
k++; fatalx("too many popup & menu ranges");
} r->ranges[k].px = pd->or[i].ranges[j].px;
r->ranges[k].nx = pd->or[i].ranges[j].nx;
k++;
} }
} }
return (r);
/* Zero remaining ranges if any. */
for (i = k; i < OVERLAY_MAX_RANGES; i++) {
r->px[i] = 0;
r->nx[i] = 0;
}
return;
} }
server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx, server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx,
r); r);
return (r);
} }
static void static void
@@ -219,7 +297,13 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
struct colour_palette *palette = &pd->palette; struct colour_palette *palette = &pd->palette;
struct grid_cell defaults; struct grid_cell defaults;
popup_reapply_styles(pd);
screen_init(&s, pd->sx, pd->sy, 0); screen_init(&s, pd->sx, pd->sy, 0);
if (pd->s.hyperlinks != NULL) {
hyperlinks_free(s.hyperlinks);
s.hyperlinks = hyperlinks_copy(pd->s.hyperlinks);
}
screen_write_start(&ctx, &s); screen_write_start(&ctx, &s);
screen_write_clearscreen(&ctx, 8); screen_write_clearscreen(&ctx, 8);
@@ -280,17 +364,8 @@ popup_free_cb(struct client *c, void *data)
cmdq_get_client(item)->retval = pd->status; cmdq_get_client(item)->retval = pd->status;
cmdq_continue(item); cmdq_continue(item);
} }
server_client_unref(pd->c);
if (pd->job != NULL) popup_free(pd);
job_free(pd->job);
input_free(pd->ictx);
screen_free(&pd->s);
colour_palette_free(&pd->palette);
free(pd->title);
free(pd);
} }
static void static void
@@ -360,7 +435,7 @@ popup_make_pane(struct popup_data *pd, enum layout_type type)
pd->job = NULL; pd->job = NULL;
} }
screen_set_title(&pd->s, new_wp->base.title); screen_set_title(&pd->s, new_wp->base.title, 0);
screen_free(&new_wp->base); screen_free(&new_wp->base);
memcpy(&new_wp->base, &pd->s, sizeof wp->base); memcpy(&new_wp->base, &pd->s, sizeof wp->base);
screen_resize(&new_wp->base, new_wp->sx, new_wp->sy, 1); screen_resize(&new_wp->base, new_wp->sx, new_wp->sy, 1);
@@ -549,7 +624,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event)
(event->key == '\033' || event->key == ('c'|KEYC_CTRL))) (event->key == '\033' || event->key == ('c'|KEYC_CTRL)))
return (1); return (1);
if (pd->job == NULL && (pd->flags & POPUP_CLOSEANYKEY) && if (pd->job == NULL && (pd->flags & POPUP_CLOSEANYKEY) &&
!KEYC_IS_MOUSE(event->key) && !KEYC_IS_PASTE(event->key)) !KEYC_IS_MOUSE(event->key) && !KEYC_IS_PASTE(event->key))
return (1); return (1);
if (pd->job != NULL) { if (pd->job != NULL) {
if (KEYC_IS_MOUSE(event->key)) { if (KEYC_IS_MOUSE(event->key)) {
@@ -658,6 +733,8 @@ popup_modify(struct client *c, const char *title, const char *style,
pd->title = xstrdup(title); pd->title = xstrdup(title);
} }
if (border_style != NULL) { if (border_style != NULL) {
free(pd->border_style);
pd->border_style = xstrdup(border_style);
style_set(&sytmp, &pd->border_cell); style_set(&sytmp, &pd->border_cell);
if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) { if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) {
pd->border_cell.fg = sytmp.gc.fg; pd->border_cell.fg = sytmp.gc.fg;
@@ -665,6 +742,8 @@ popup_modify(struct client *c, const char *title, const char *style,
} }
} }
if (style != NULL) { if (style != NULL) {
free(pd->style);
pd->style = xstrdup(style);
style_set(&sytmp, &pd->defaults); style_set(&sytmp, &pd->defaults);
if (style_parse(&sytmp, &pd->defaults, style) == 0) { if (style_parse(&sytmp, &pd->defaults, style) == 0) {
pd->defaults.fg = sytmp.gc.fg; pd->defaults.fg = sytmp.gc.fg;
@@ -675,7 +754,8 @@ popup_modify(struct client *c, const char *title, const char *style,
if (lines == BOX_LINES_NONE && pd->border_lines != lines) { if (lines == BOX_LINES_NONE && pd->border_lines != lines) {
screen_resize(&pd->s, pd->sx, pd->sy, 1); screen_resize(&pd->s, pd->sx, pd->sy, 1);
job_resize(pd->job, pd->sx, pd->sy); job_resize(pd->job, pd->sx, pd->sy);
} else if (pd->border_lines == BOX_LINES_NONE && pd->border_lines != lines) { } else if (pd->border_lines == BOX_LINES_NONE &&
pd->border_lines != lines) {
screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 1); screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 1);
job_resize(pd->job, pd->sx - 2, pd->sy - 2); job_resize(pd->job, pd->sx - 2, pd->sy - 2);
} }
@@ -725,8 +805,13 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
pd = xcalloc(1, sizeof *pd); pd = xcalloc(1, sizeof *pd);
pd->item = item; pd->item = item;
pd->flags = flags; pd->flags = flags;
if (title != NULL) if (title != NULL)
pd->title = xstrdup(title); pd->title = xstrdup(title);
if (style != NULL)
pd->style = xstrdup(style);
if (border_style != NULL)
pd->border_style = xstrdup(border_style);
pd->c = c; pd->c = c;
pd->c->references++; pd->c->references++;
@@ -773,16 +858,39 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
pd->psx = sx; pd->psx = sx;
pd->psy = sy; pd->psy = sy;
pd->job = job_run(shellcmd, argc, argv, env, s, cwd, if (flags & POPUP_NOJOB)
popup_job_update_cb, popup_job_complete_cb, NULL, pd, pd->ictx = input_init(NULL, NULL, &pd->palette, NULL);
JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy); else {
pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette); pd->job = job_run(shellcmd, argc, argv, env, s, cwd,
popup_job_update_cb, popup_job_complete_cb, NULL, pd,
JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy);
if (pd->job == NULL) {
popup_free(pd);
return (-1);
}
pd->ictx = input_init(NULL, job_get_event(pd->job),
&pd->palette, c);
}
server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd); popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd);
return (0); return (0);
} }
void
popup_write(struct client *c, const char *data, size_t size)
{
struct popup_data *pd = c->overlay_data;
if (!popup_present(c))
return;
c->overlay_check = NULL;
c->overlay_data = NULL;
input_parse_screen(pd->ictx, &pd->s, popup_init_ctx_cb, pd, data, size);
c->overlay_check = popup_check_cb;
c->overlay_data = pd;
}
static void static void
popup_editor_free(struct popup_editor *pe) popup_editor_free(struct popup_editor *pe)
{ {

View File

@@ -4,9 +4,9 @@ PATH=/bin:/usr/bin
TERM=screen TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest" TMUX="$TEST_TMUX -Ltest -f/dev/null"
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null
TMUX2="$TEST_TMUX -Ltest2" TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
$TMUX2 kill-server 2>/dev/null $TMUX2 kill-server 2>/dev/null
TMP=$(mktemp) TMP=$(mktemp)

52
regress/border-arrows.sh Normal file
View File

@@ -0,0 +1,52 @@
#!/bin/sh
# Test for GitHub issue #4780 - pane-border-indicators both arrows missing
# on second pane in a two-pane horizontal split.
#
# When pane-border-indicators is set to "both", arrow indicators should
# appear when EITHER pane is selected. Before the fix, arrows only appeared
# when the LEFT pane was selected.
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
$TMUX kill-server 2>/dev/null
TMUX_OUTER="$TEST_TMUX -Ltest2 -f/dev/null"
$TMUX_OUTER kill-server 2>/dev/null
trap "$TMUX kill-server 2>/dev/null; $TMUX_OUTER kill-server 2>/dev/null" 0 1 15
# Start outer tmux that will capture the inner tmux's rendering
$TMUX_OUTER -f/dev/null new -d -x80 -y24 "$TMUX -f/dev/null new -x78 -y22" || exit 1
sleep 1
# Set pane-border-indicators to "both" in inner tmux
$TMUX set -g pane-border-indicators both || exit 1
# Create horizontal split (two panes side by side)
$TMUX splitw -h || exit 1
sleep 1
# Helper to check for arrow characters in captured output
has_arrow() {
echo "$1" | grep -qE '(←|→|↑|↓)'
}
# Test 1: Select left pane (pane 0) and check for arrows
$TMUX selectp -t 0
sleep 1
left_output=$($TMUX_OUTER capturep -Cep 2>/dev/null)
has_arrow "$left_output" || exit 1
# Test 2: Select right pane (pane 1) and check for arrows
# This is the case that failed before the fix
$TMUX selectp -t 1
sleep 1
right_output=$($TMUX_OUTER capturep -Cep 2>/dev/null)
has_arrow "$right_output" || exit 1
$TMUX kill-server 2>/dev/null
$TMUX_OUTER kill-server 2>/dev/null
exit 0

View File

@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
TERM=screen TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest" TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMP=$(mktemp) TMP=$(mktemp)
TMP2=$(mktemp) TMP2=$(mktemp)
trap "rm -f $TMP $TMP2" 0 1 15 trap "rm -f $TMP $TMP2" 0 1 15

View File

@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
TERM=screen TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest" TMUX="$TEST_TMUX -Ltest -f/dev/null"
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null
TMP=$(mktemp) TMP=$(mktemp)

View File

@@ -4,7 +4,7 @@
Λ̊1 Λ̊1
🏻2 🏻2
👍🏻3 👍🏻3
👍🏻 👍🏻4 👍🏻 👍🏻4
🤷5 🤷5
7 7
🤷8 🤷8

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest" TMUX="$TEST_TMUX -Ltest -f/dev/null"
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null
TMP=$(mktemp) TMP=$(mktemp)

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest" TMUX="$TEST_TMUX -Ltest -f/dev/null"
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null
TMP=$(mktemp) TMP=$(mktemp)

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest" TMUX="$TEST_TMUX -Ltest -f/dev/null"
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null
for i in conf/*.conf; do for i in conf/*.conf; do

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest" TMUX="$TEST_TMUX -Ltest -f/dev/null"
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null
TMP=$(mktemp) TMP=$(mktemp)

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