@@ -63,6 +63,68 @@ def func(value):
6363 return func
6464
6565
66+ def set_layout_hook (session , hook_name ):
67+ """Set layout hooks to normalize layout.
68+
69+ References:
70+
71+ - tmuxp issue: https://github.com/tony/tmuxp/issues/309
72+ - tmux issue: https://github.com/tmux/tmux/issues/1106
73+
74+ tmux 2.6+ requires that the window be viewed with the client before
75+ select-layout adjustments can take effect.
76+
77+ To handle this, this function creates temporary hook for this session to
78+ iterate through all windows and select the layout.
79+
80+ In order for layout changes to take effect, a client must at the very
81+ least be attached to the window (not just the session).
82+
83+ hook_name is provided to allow this to set to multiple scenarios, such
84+ as 'client-attached' (which the user attaches the session). You may
85+ also want 'after-switch-client' for cases where the user loads tmuxp
86+ sessions inside tmux since tmuxp offers to switch for them.
87+
88+ Also, the hooks are set immediately unbind after they're invoked via -u.
89+
90+ :param session: session to bind hook to
91+ :type session: :class:`libtmux.session.Session`
92+ :param hook_name: hook name to bind to, e.g. 'client-attached'
93+ :type hook_name: str
94+ """
95+ cmd = [
96+ 'set-hook' ,
97+ '-t' , session .id ,
98+ hook_name
99+ ]
100+ hook_cmd = []
101+ for window in session .windows :
102+ # unfortunately, select-layout won't work unless
103+ # we've literally selected the window at least once
104+ # with the client
105+ hook_cmd .append ('selectw -t {}' .format (window .id ))
106+ # edit: removed -t, or else it won't respect main-pane-w/h
107+ hook_cmd .append ('selectl' .format (window .id ))
108+ hook_cmd .append ('selectw -p' .format (window .id ))
109+
110+ # unset the hook immediately after executing
111+ hook_cmd .append (
112+ 'set-hook -u -t {target_session} {hook_name}' .format (
113+ target_session = session .id ,
114+ hook_name = hook_name
115+ )
116+ )
117+
118+ # join the hook's commands with semicolons
119+ hook_cmd = '{}' .format ('; ' .join (hook_cmd ))
120+
121+ # append the hook command
122+ cmd .append (hook_cmd )
123+
124+ # create the hook
125+ session .cmd (* cmd )
126+
127+
66128def is_pure_name (path ):
67129 """Return True if path is a name and not a file path."""
68130 return (
@@ -265,58 +327,36 @@ def reattach(session):
265327 click .style (config_file , fg = 'blue' , bold = True ))
266328 builder .build ()
267329
268- if 'TMUX' in os .environ :
330+ if 'TMUX' in os .environ : # tmuxp ran from inside tmux
269331 if not detached and (answer_yes or click .confirm (
270332 'Already inside TMUX, switch to session?'
271333 )):
272334 tmux_env = os .environ .pop ('TMUX' )
335+
336+ if has_gte_version ('2.6' ):
337+ # if using -d from inside tmux session + switching inside
338+ # https://github.com/tmux/tmux/blob/2.6/cmd-switch-client.c#L132
339+ set_layout_hook (builder .session , 'client-session-changed' )
340+
273341 builder .session .switch_client ()
274342
275343 os .environ ['TMUX' ] = tmux_env
276344 return builder .session
277- else :
278- sys .exit ('Session created in detached state.' )
345+ else : # session created in the background, from within tmux
346+ if has_gte_version ('2.6' ): # prepare for both cases
347+ set_layout_hook (builder .session , 'client-attached' )
348+ set_layout_hook (builder .session , 'client-session-changed' )
279349
280- if has_gte_version ('2.6' ):
281- # tmuxp issue: https://github.com/tony/tmuxp/issues/309
282- # tmux issue: https://github.com/tmux/tmux/issues/1106
283- #
284- # tmux now requires that the window be viewed with the client
285- # before select-layout adjustments can be meaningful
286- #
287- # To handle this, let's create a temporary hook for this
288- # session to iterage and run select-layout on all windows
289- # after client attaches.
290- cmd = [
291- 'set-hook' ,
292- '-t' , builder .session .id ,
293- 'client-attached'
294- ]
295- hook_cmd = []
296- for window in builder .session .windows :
297- # unfortunately, select-layout won't work unless
298- # we've literally selected the window at least once
299- # with the client
300- hook_cmd .append ('selectw -t {}' .format (window .id ))
301- # edit: removed -t, or else it won't respect main-pane-w/h
302- hook_cmd .append ('selectl' .format (window .id ))
303- hook_cmd .append ('selectw -p' .format (window .id ))
304-
305- # unset the hook immediately after executing
306- hook_cmd .append (
307- 'set-hook -u -t {target_session} client-attached' .format (
308- target_session = builder .session .id
309- )
310- )
350+ sys .exit ('Session created in detached state.' )
311351
312- # join the hook's commands with semicolons
313- hook_cmd = '{}' .format ('; ' .join (hook_cmd ))
352+ # below: tmuxp ran outside of tmux
314353
315- # append the hook command
316- cmd .append (hook_cmd )
354+ if has_gte_version ('2.6' ):
355+ # if attaching for first time
356+ set_layout_hook (builder .session , 'client-attached' )
317357
318- # create the hook
319- builder .session . cmd ( * cmd )
358+ # for cases where user switches client for first time
359+ set_layout_hook ( builder .session , 'client-session-changed' )
320360
321361 if not detached :
322362 builder .session .attach_session ()
0 commit comments