mirror of
https://github.com/macports/macports-ports.git
synced 2026-07-12 18:20:25 -07:00
The Mozilla Foundation has generously sponsored a security audit of the iTerm2 source code. As part of this audit, a problem was discovered which could cause iTerm2 to issue commands in response to receiving certain input. This is a serious security issue because in some circumstances it could allow an attacker to execute commands on your machine when you view a file or otherwise receive input they have crafted in iTerm2.
1841 lines
83 KiB
Diff
1841 lines
83 KiB
Diff
From 538d570ea54614d3a2b5724f820953d717fbeb0c Mon Sep 17 00:00:00 2001
|
|
From: George Nachman <gln@whatsapp.com>
|
|
Date: Wed, 25 Sep 2019 23:13:00 -0700
|
|
Subject: [PATCH] Do not send server-controlled values in tmux integration
|
|
mode.
|
|
|
|
CVE-2019-9535
|
|
|
|
- Use session number everywhere rather than session name
|
|
- Do not poll tmux for the set-titles-string, status-left, and status-right and
|
|
then request the values of the returned format strings. Use ${T:} eval
|
|
instead. These features are now only available for tmux 2.9 and later.
|
|
- Hex-encode options saved in the tmux server to make them unexploitable (e.g.,
|
|
hotkeys, window affinities, window origins, etc.). The old values are
|
|
accepted as inputs but will never be produced as output.
|
|
---
|
|
iTerm2.xcodeproj/project.pbxproj | 8 +
|
|
sources/PTYSession.m | 28 ++-
|
|
sources/PTYTab.m | 13 +-
|
|
sources/PseudoTerminal.m | 9 +-
|
|
sources/TmuxController.h | 47 ++--
|
|
sources/TmuxController.m | 305 +++++++++++++++-----------
|
|
sources/TmuxDashboardController.m | 163 +++++++-------
|
|
sources/TmuxSessionsTable.h | 30 +--
|
|
sources/TmuxSessionsTable.m | 106 ++++-----
|
|
sources/TmuxWindowsTable.h | 2 +-
|
|
sources/TmuxWindowsTable.m | 21 +-
|
|
sources/iTermInitialDirectory+Tmux.h | 2 +-
|
|
sources/iTermInitialDirectory+Tmux.m | 20 +-
|
|
sources/iTermTmuxOptionMonitor.h | 4 +
|
|
sources/iTermTmuxOptionMonitor.m | 16 +-
|
|
sources/iTermTmuxSessionObject.h | 17 ++
|
|
sources/iTermTmuxSessionObject.m | 12 +
|
|
sources/iTermTmuxStatusBarMonitor.h | 1 +
|
|
sources/iTermTmuxStatusBarMonitor.m | 28 +--
|
|
sources/iTermWorkingDirectoryPoller.m | 1 +
|
|
20 files changed, 471 insertions(+), 362 deletions(-)
|
|
create mode 100644 sources/iTermTmuxSessionObject.h
|
|
create mode 100644 sources/iTermTmuxSessionObject.m
|
|
|
|
diff --git iTerm2.xcodeproj/project.pbxproj iTerm2.xcodeproj/project.pbxproj
|
|
index 7e2f86a..32e1df2 100644
|
|
--- iTerm2.xcodeproj/project.pbxproj
|
|
+++ iTerm2.xcodeproj/project.pbxproj
|
|
@@ -1376,6 +1376,8 @@
|
|
53E184F21FE32F2800DB78F3 /* iTermMetalBufferPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 53E184F01FE32F2800DB78F3 /* iTermMetalBufferPool.m */; };
|
|
53E8F36F2244A58800F3770F /* iTermActionsModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 53E8F36D2244A58800F3770F /* iTermActionsModel.h */; };
|
|
53E8F3702244A58800F3770F /* iTermActionsModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 53E8F36E2244A58800F3770F /* iTermActionsModel.m */; };
|
|
+ 53E98E7B233C6B760094D8A9 /* iTermTmuxSessionObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 53E98E79233C6B760094D8A9 /* iTermTmuxSessionObject.h */; };
|
|
+ 53E98E7C233C6B760094D8A9 /* iTermTmuxSessionObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 53E98E7A233C6B760094D8A9 /* iTermTmuxSessionObject.m */; };
|
|
53E9DFE0220D518E0070C9C0 /* AlertTrigger.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D9DCC15142D7FC10016228A /* AlertTrigger.m */; };
|
|
53E9DFE1220D51D50070C9C0 /* CoprocessTrigger.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D9DDD91142E5AC600275650 /* CoprocessTrigger.m */; };
|
|
53E9DFE2220D52730070C9C0 /* PasswordTrigger.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DABA03219253FEA00A228D8 /* PasswordTrigger.m */; };
|
|
@@ -4429,6 +4431,8 @@
|
|
53E184F01FE32F2800DB78F3 /* iTermMetalBufferPool.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iTermMetalBufferPool.m; sourceTree = "<group>"; };
|
|
53E8F36D2244A58800F3770F /* iTermActionsModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iTermActionsModel.h; sourceTree = "<group>"; };
|
|
53E8F36E2244A58800F3770F /* iTermActionsModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iTermActionsModel.m; sourceTree = "<group>"; };
|
|
+ 53E98E79233C6B760094D8A9 /* iTermTmuxSessionObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iTermTmuxSessionObject.h; sourceTree = "<group>"; };
|
|
+ 53E98E7A233C6B760094D8A9 /* iTermTmuxSessionObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iTermTmuxSessionObject.m; sourceTree = "<group>"; };
|
|
53EBF29B1DCBFF7C00766613 /* iTermAPIServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTermAPIServer.h; sourceTree = "<group>"; };
|
|
53EBF29C1DCBFF7C00766613 /* iTermAPIServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iTermAPIServer.m; sourceTree = "<group>"; };
|
|
53EBF29F1DCCF4AC00766613 /* iTermIPV4Address.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTermIPV4Address.h; sourceTree = "<group>"; };
|
|
@@ -7010,6 +7014,8 @@
|
|
A6F718B4226438FC0053488E /* iTermInitialDirectory+Tmux.m */,
|
|
53D68F7E2283EE7C0018710D /* iTermTmuxLayoutBuilder.h */,
|
|
53D68F7F2283EE7C0018710D /* iTermTmuxLayoutBuilder.m */,
|
|
+ 53E98E79233C6B760094D8A9 /* iTermTmuxSessionObject.h */,
|
|
+ 53E98E7A233C6B760094D8A9 /* iTermTmuxSessionObject.m */,
|
|
);
|
|
name = tmux;
|
|
sourceTree = "<group>";
|
|
@@ -9987,6 +9993,7 @@
|
|
A6153D4C21F30A9C002976FC /* iTermJobTreeViewController.h in Headers */,
|
|
5370678F21C9D2780088D0F3 /* SIGSHA2VerificationAlgorithm.h in Headers */,
|
|
A66719161DCE36C3000CE608 /* NSURL+iTerm.h in Headers */,
|
|
+ 53E98E7B233C6B760094D8A9 /* iTermTmuxSessionObject.h in Headers */,
|
|
A66719171DCE36C3000CE608 /* NSDate+iTerm.h in Headers */,
|
|
A6BCAAD821F6F53E0000CD29 /* iTermTextPopoverViewController.h in Headers */,
|
|
A66719181DCE36C3000CE608 /* iTermBaseHotKey.h in Headers */,
|
|
@@ -12429,6 +12436,7 @@
|
|
A6AC5D241E9036D70097C0A7 /* iTermURLMark.m in Sources */,
|
|
535EA51420D8C1EB00FC81E0 /* iTermProfilesWindowController.m in Sources */,
|
|
A6A4866F20B67A1600493302 /* iTermSemanticHistoryPrefsController.m in Sources */,
|
|
+ 53E98E7C233C6B760094D8A9 /* iTermTmuxSessionObject.m in Sources */,
|
|
A66719621DCE3772000CE608 /* iTermSocketAddress.m in Sources */,
|
|
A6A4867E20B67FD100493302 /* SmartSelectionController.m in Sources */,
|
|
53AFFC8E1DD2A04100E6CEC6 /* iTermLSOF.m in Sources */,
|
|
diff --git sources/PTYSession.m sources/PTYSession.m
|
|
index 9b64e03..0332d8c 100644
|
|
--- sources/PTYSession.m
|
|
+++ sources/PTYSession.m
|
|
@@ -4726,7 +4726,7 @@ ITERM_WEAKLY_REFERENCEABLE
|
|
} else {
|
|
// Legacy code path for pre tmux 2.6
|
|
[_tmuxController renameWindowWithId:_delegate.tmuxWindow
|
|
- inSession:nil
|
|
+ inSessionNumber:nil
|
|
toName:profile[KEY_NAME]];
|
|
}
|
|
_tmuxTitleOutOfSync = NO;
|
|
@@ -5910,14 +5910,18 @@ scrollToFirstResult:(BOOL)scrollToFirstResult {
|
|
|
|
- (void)installTmuxStatusBarMonitor {
|
|
assert(!_tmuxStatusBarMonitor);
|
|
- _tmuxStatusBarMonitor = [[iTermTmuxStatusBarMonitor alloc] initWithGateway:_tmuxController.gateway
|
|
- scope:self.variablesScope];
|
|
- _tmuxStatusBarMonitor.active = [iTermProfilePreferences boolForKey:KEY_SHOW_STATUS_BAR inProfile:self.profile];
|
|
- if ([iTermPreferences boolForKey:kPreferenceKeyUseTmuxStatusBar] ||
|
|
- [iTermStatusBarLayout shouldOverrideLayout:self.profile[KEY_STATUS_BAR_LAYOUT]]) {
|
|
- [self setSessionSpecificProfileValues:@{ KEY_STATUS_BAR_LAYOUT: [[iTermStatusBarLayout tmuxLayoutWithController:_tmuxController
|
|
- scope:nil
|
|
- window:self.delegate.tmuxWindow] dictionaryValue] }];
|
|
+
|
|
+ if (_tmuxController.gateway.minimumServerVersion.doubleValue >= 2.9) {
|
|
+ // Just use the built-in status bar for older versions of tmux because they don't support ${T:xxx} or ${E:xxx}
|
|
+ _tmuxStatusBarMonitor = [[iTermTmuxStatusBarMonitor alloc] initWithGateway:_tmuxController.gateway
|
|
+ scope:self.variablesScope];
|
|
+ _tmuxStatusBarMonitor.active = [iTermProfilePreferences boolForKey:KEY_SHOW_STATUS_BAR inProfile:self.profile];
|
|
+ if ([iTermPreferences boolForKey:kPreferenceKeyUseTmuxStatusBar] ||
|
|
+ [iTermStatusBarLayout shouldOverrideLayout:self.profile[KEY_STATUS_BAR_LAYOUT]]) {
|
|
+ [self setSessionSpecificProfileValues:@{ KEY_STATUS_BAR_LAYOUT: [[iTermStatusBarLayout tmuxLayoutWithController:_tmuxController
|
|
+ scope:nil
|
|
+ window:self.delegate.tmuxWindow] dictionaryValue] }];
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -5929,7 +5933,8 @@ scrollToFirstResult:(BOOL)scrollToFirstResult {
|
|
}
|
|
__weak __typeof(self) weakSelf = self;
|
|
_tmuxTitleMonitor = [[iTermTmuxOptionMonitor alloc] initWithGateway:_tmuxController.gateway
|
|
- scope:self.variablesScope
|
|
+ scope:self.variablesScope
|
|
+ fallbackVariableName:nil
|
|
format:@"#{pane_title}"
|
|
target:[NSString stringWithFormat:@"%%%@", @(self.tmuxPane)]
|
|
variableName:iTermVariableKeySessionTmuxPaneTitle
|
|
@@ -6308,8 +6313,7 @@ scrollToFirstResult:(BOOL)scrollToFirstResult {
|
|
[_tmuxController ping];
|
|
[_tmuxController validateOptions];
|
|
[_tmuxController checkForUTF8];
|
|
- [_tmuxController guessVersion];
|
|
- [_tmuxController loadTitleFormat];
|
|
+ [_tmuxController guessVersion]; // NOTE: This kicks off more stuff that depends on knowing the version number.
|
|
}
|
|
|
|
- (void)tmuxInitialCommandDidFailWithError:(NSString *)error {
|
|
diff --git sources/PTYTab.m sources/PTYTab.m
|
|
index a8f8b48..6c4766e 100644
|
|
--- sources/PTYTab.m
|
|
+++ sources/PTYTab.m
|
|
@@ -3690,11 +3690,12 @@ static void SetAgainstGrainDim(BOOL isVertical, NSSize *dest, CGFloat value) {
|
|
return;
|
|
}
|
|
_tmuxTitleMonitor = [[iTermTmuxOptionMonitor alloc] initWithGateway:tmuxController_.gateway
|
|
- scope:self.variablesScope
|
|
- format:tmuxController_.setTitlesString
|
|
- target:[NSString stringWithFormat:@"@%@", @(self.tmuxWindow)]
|
|
- variableName:iTermVariableKeyTabTmuxWindowTitle
|
|
- block:nil];
|
|
+ scope:self.variablesScope
|
|
+ fallbackVariableName:iTermVariableKeySessionWindowName
|
|
+ format:@"#{T:set-titles-string}"
|
|
+ target:[NSString stringWithFormat:@"@%@", @(self.tmuxWindow)]
|
|
+ variableName:iTermVariableKeyTabTmuxWindowTitle
|
|
+ block:nil];
|
|
[_tmuxTitleMonitor updateOnce];
|
|
if (self.titleOverride.length == 0) {
|
|
// Show the tmux window title if both the tmux option set-titles is on and the user hasn't
|
|
@@ -4312,7 +4313,7 @@ static void SetAgainstGrainDim(BOOL isVertical, NSSize *dest, CGFloat value) {
|
|
if (self.tmuxTab) {
|
|
if (titleOverride) {
|
|
[self.tmuxController renameWindowWithId:self.tmuxWindow
|
|
- inSession:nil
|
|
+ inSessionNumber:nil
|
|
toName:titleOverride];
|
|
}
|
|
return;
|
|
diff --git sources/PseudoTerminal.m sources/PseudoTerminal.m
|
|
index b90e733..24f041a 100644
|
|
--- sources/PseudoTerminal.m
|
|
+++ sources/PseudoTerminal.m
|
|
@@ -3018,8 +3018,8 @@ ITERM_WEAKLY_REFERENCEABLE
|
|
if ([arrangement objectForKey:TERMINAL_GUID] &&
|
|
[[arrangement objectForKey:TERMINAL_GUID] isKindOfClass:[NSString class]]) {
|
|
NSString *savedGUID = [arrangement objectForKey:TERMINAL_GUID];
|
|
- if ([[iTermController sharedInstance] terminalWithGuid:savedGUID]) {
|
|
- // Refuse to create a window with an already-used guid.
|
|
+ if ([[iTermController sharedInstance] terminalWithGuid:savedGUID] || ![self stringIsValidTerminalGuid:savedGUID]) {
|
|
+ // Refuse to create a window with an already-used or invalid guid.
|
|
self.terminalGuid = [NSString stringWithFormat:@"pty-%@", [NSString uuid]];
|
|
} else {
|
|
self.terminalGuid = savedGUID;
|
|
@@ -3039,6 +3039,11 @@ ITERM_WEAKLY_REFERENCEABLE
|
|
return YES;
|
|
}
|
|
|
|
+- (BOOL)stringIsValidTerminalGuid:(NSString *)string {
|
|
+ NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-"];
|
|
+ return [string rangeOfCharacterFromSet:characterSet.invertedSet].location == NSNotFound;
|
|
+}
|
|
+
|
|
- (BOOL)restoreTabsFromArrangement:(NSDictionary *)arrangement sessions:(NSArray<PTYSession *> *)sessions {
|
|
for (NSDictionary *tabArrangement in arrangement[TERMINAL_ARRANGEMENT_TABS]) {
|
|
NSDictionary<NSString *, PTYSession *> *sessionMap = nil;
|
|
diff --git sources/TmuxController.h sources/TmuxController.h
|
|
index f27bacc..f1e5ad3 100644
|
|
--- sources/TmuxController.h
|
|
+++ sources/TmuxController.h
|
|
@@ -8,6 +8,7 @@
|
|
#import <Cocoa/Cocoa.h>
|
|
#import "ProfileModel.h"
|
|
#import "iTermInitialDirectory.h"
|
|
+#import "iTermTmuxSessionObject.h"
|
|
#import "TmuxGateway.h"
|
|
#import "WindowControllerInterface.h"
|
|
|
|
@@ -33,7 +34,7 @@ extern NSString *const kTmuxControllerWindowDidClose;
|
|
extern NSString *const kTmuxControllerAttachedSessionDidChange;
|
|
// Posted when a session changes name
|
|
extern NSString *const kTmuxControllerSessionWasRenamed;
|
|
-// Posted when set-titles-string option changes. Object is tmux controller.
|
|
+// Posted when set-titles option changes. Object is tmux controller.
|
|
extern NSString *const kTmuxControllerDidFetchSetTitlesStringOption;
|
|
|
|
@interface TmuxController : NSObject
|
|
@@ -41,7 +42,7 @@ extern NSString *const kTmuxControllerDidFetchSetTitlesStringOption;
|
|
@property(nonatomic, readonly) TmuxGateway *gateway;
|
|
@property(nonatomic, retain) NSMutableDictionary *windowPositions;
|
|
@property(nonatomic, copy) NSString *sessionName;
|
|
-@property(nonatomic, retain) NSArray *sessions;
|
|
+@property(nonatomic, copy) NSArray<iTermTmuxSessionObject *> *sessionObjects;
|
|
@property(nonatomic, assign) BOOL ambiguousIsDoubleWidth;
|
|
@property(nonatomic, assign) NSInteger unicodeVersion;
|
|
@property(nonatomic, readonly) NSString *clientName;
|
|
@@ -53,7 +54,6 @@ extern NSString *const kTmuxControllerDidFetchSetTitlesStringOption;
|
|
@property(nonatomic, readonly) NSDictionary *sharedFontOverrides;
|
|
@property(nonatomic, readonly) NSString *sessionGuid;
|
|
@property(nonatomic, readonly) BOOL variableWindowSize;
|
|
-@property(nonatomic, readonly) NSString *setTitlesString;
|
|
@property(nonatomic, readonly) BOOL shouldSetTitles;
|
|
@property(nonatomic, readonly) BOOL serverIsLocal;
|
|
|
|
@@ -120,9 +120,9 @@ extern NSString *const kTmuxControllerDidFetchSetTitlesStringOption;
|
|
scope:(iTermVariableScope *)scope
|
|
initialDirectory:(iTermInitialDirectory *)initialDirectory;
|
|
|
|
-- (void)newWindowInSession:(NSString *)targetSession
|
|
- scope:(iTermVariableScope *)scope
|
|
- initialDirectory:(iTermInitialDirectory *)initialDirectory;
|
|
+- (void)newWindowInSessionNumber:(NSNumber *)sessionNumber
|
|
+ scope:(iTermVariableScope *)scope
|
|
+ initialDirectory:(iTermInitialDirectory *)initialDirectory;
|
|
|
|
- (void)selectPane:(int)windowPane;
|
|
|
|
@@ -150,9 +150,11 @@ extern NSString *const kTmuxControllerDidFetchSetTitlesStringOption;
|
|
|
|
- (void)killWindowPane:(int)windowPane;
|
|
- (void)killWindow:(int)window;
|
|
-- (void)unlinkWindowWithId:(int)windowId inSession:(NSString *)sessionName;
|
|
+- (void)unlinkWindowWithId:(int)windowId;
|
|
- (void)requestDetach;
|
|
-- (void)renameWindowWithId:(int)windowId inSession:(NSString *)sessionName toName:(NSString *)newName;
|
|
+- (void)renameWindowWithId:(int)windowId
|
|
+ inSessionNumber:(NSNumber *)sessionNumber
|
|
+ toName:(NSString *)newName;
|
|
- (BOOL)canRenamePane;
|
|
- (void)renamePane:(int)windowPane toTitle:(NSString *)newTitle;
|
|
- (void)setHotkeyForWindowPane:(int)windowPane to:(NSDictionary *)hotkey;
|
|
@@ -162,21 +164,25 @@ extern NSString *const kTmuxControllerDidFetchSetTitlesStringOption;
|
|
- (NSString *)tabColorStringForWindowPane:(int)windowPane;
|
|
|
|
- (void)linkWindowId:(int)windowId
|
|
- inSession:(NSString *)sessionName
|
|
- toSession:(NSString *)targetSession;
|
|
+ inSessionNumber:(int)sessionNumber
|
|
+ toSessionNumber:(int)targetSession;
|
|
+
|
|
- (void)moveWindowId:(int)windowId
|
|
- inSession:(NSString *)sessionName
|
|
- toSession:(NSString *)targetSession;
|
|
+ inSessionNumber:(int)sessionNumber
|
|
+ toSessionNumber:(int)targetSessionNumber;
|
|
+
|
|
+- (void)renameSessionNumber:(int)sessionNumber
|
|
+ to:(NSString *)newName;
|
|
|
|
-- (void)renameSession:(NSString *)oldName to:(NSString *)newName;
|
|
-- (void)killSession:(NSString *)sessionName;
|
|
-- (void)attachToSession:(NSString *)sessionName;
|
|
+- (void)killSessionNumber:(int)sessionNumber;
|
|
+- (void)attachToSessionWithNumber:(int)sessionNumber;
|
|
- (void)addSessionWithName:(NSString *)sessionName;
|
|
-// NOTE: If the session name is bogus (or any other error occurs) the selector will not be called.
|
|
-- (void)listWindowsInSession:(NSString *)sessionName
|
|
- target:(id)target
|
|
- selector:(SEL)selector
|
|
- object:(id)object;
|
|
+// NOTE: If anything goes wrong the selector will not be called.
|
|
+- (void)listWindowsInSessionNumber:(int)sessionNumber
|
|
+ target:(id)target
|
|
+ selector:(SEL)selector
|
|
+ object:(id)object;
|
|
+
|
|
- (void)listSessions;
|
|
- (void)saveAffinities;
|
|
- (void)saveWindowOrigins;
|
|
@@ -200,7 +206,6 @@ extern NSString *const kTmuxControllerDidFetchSetTitlesStringOption;
|
|
- (void)setLayoutInWindow:(int)window toLayout:(NSString *)layout;
|
|
- (NSArray<PTYSession *> *)clientSessions;
|
|
|
|
-- (void)setSize:(NSSize)size windows:(NSArray<NSString *> *)windows;
|
|
- (void)setSize:(NSSize)size window:(int)window;
|
|
|
|
@end
|
|
diff --git sources/TmuxController.m sources/TmuxController.m
|
|
index 08cbd91..77b0424 100644
|
|
--- sources/TmuxController.m
|
|
+++ sources/TmuxController.m
|
|
@@ -20,6 +20,7 @@
|
|
#import "iTermShortcut.h"
|
|
#import "iTermTuple.h"
|
|
#import "NSArray+iTerm.h"
|
|
+#import "NSData+iTerm.h"
|
|
#import "NSFont+iTerm.h"
|
|
#import "NSStringITerm.h"
|
|
#import "PreferencePanel.h"
|
|
@@ -42,6 +43,12 @@ NSString *const kTmuxControllerWindowDidClose = @"kTmuxControllerWindowDidClose"
|
|
NSString *const kTmuxControllerSessionWasRenamed = @"kTmuxControllerSessionWasRenamed";
|
|
NSString *const kTmuxControllerDidFetchSetTitlesStringOption = @"kTmuxControllerDidFetchSetTitlesStringOption";
|
|
|
|
+static NSString *const iTermTmuxControllerEncodingPrefixHotkeys = @"h_";
|
|
+static NSString *const iTermTmuxControllerEncodingPrefixTabColors = @"t_";
|
|
+static NSString *const iTermTmuxControllerEncodingPrefixAffinities = @"a_";
|
|
+static NSString *const iTermTmuxControllerEncodingPrefixOrigins = @"o_";
|
|
+static NSString *const iTermTmuxControllerEncodingPrefixHidden = @"i_";
|
|
+
|
|
// Unsupported global options:
|
|
static NSString *const kAggressiveResize = @"aggressive-resize";
|
|
|
|
@@ -81,7 +88,7 @@ static NSString *kListWindowsFormat = @"\"#{session_name}\t#{window_id}\t"
|
|
TmuxGateway *gateway_;
|
|
NSMutableDictionary *windowPanes_; // paneId -> PTYSession *
|
|
NSMutableDictionary<NSNumber *, iTermTmuxWindowState *> *_windowStates; // Key is window number
|
|
- NSArray *sessions_;
|
|
+ NSArray<iTermTmuxSessionObject *> *sessionObjects_;
|
|
int numOutstandingWindowResizes_;
|
|
NSMutableDictionary *windowPositions_;
|
|
NSSize lastSize_; // last size for windowDidChange:
|
|
@@ -97,7 +104,7 @@ static NSString *kListWindowsFormat = @"\"#{session_name}\t#{window_id}\t"
|
|
BOOL windowOriginsDirty_;
|
|
BOOL haveOutstandingSaveWindowOrigins_;
|
|
NSMutableDictionary *origins_; // window id -> NSValue(Point) window origin
|
|
- NSMutableSet *hiddenWindows_;
|
|
+ NSMutableSet<NSNumber *> *hiddenWindows_;
|
|
NSTimer *listSessionsTimer_; // Used to do a cancelable delayed perform of listSessions.
|
|
NSTimer *listWindowsTimer_; // Used to do a cancelable delayed perform of listWindows.
|
|
BOOL ambiguousIsDoubleWidth_;
|
|
@@ -119,7 +126,7 @@ static NSString *kListWindowsFormat = @"\"#{session_name}\t#{window_id}\t"
|
|
@synthesize gateway = gateway_;
|
|
@synthesize windowPositions = windowPositions_;
|
|
@synthesize sessionName = sessionName_;
|
|
-@synthesize sessions = sessions_;
|
|
+@synthesize sessionObjects = sessionObjects_;
|
|
@synthesize ambiguousIsDoubleWidth = ambiguousIsDoubleWidth_;
|
|
@synthesize sessionId = sessionId_;
|
|
|
|
@@ -180,8 +187,7 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
[_sharedFontOverrides release];
|
|
[_pendingWindows release];
|
|
[sessionName_ release];
|
|
- [sessions_ release];
|
|
- [_setTitlesString release];
|
|
+ [sessionObjects_ release];
|
|
|
|
[super dealloc];
|
|
}
|
|
@@ -392,16 +398,22 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
}
|
|
[windowsToOpen addObject:record];
|
|
}
|
|
+ BOOL tooMany = NO;
|
|
if (windowsToOpen.count > [iTermPreferences intForKey:kPreferenceKeyTmuxDashboardLimit]) {
|
|
DLog(@"There are too many windows to open so just show the dashboard");
|
|
haveHidden = YES;
|
|
+ tooMany = YES;
|
|
[windowsToOpen removeAllObjects];
|
|
}
|
|
if (haveHidden) {
|
|
DLog(@"Hidden windows existing, showing dashboard");
|
|
[[TmuxDashboardController sharedInstance] showWindow:nil];
|
|
[[[TmuxDashboardController sharedInstance] window] makeKeyAndOrderFront:nil];
|
|
- [[iTermNotificationController sharedInstance] notify:@"Too many tmux windows!" withDescription:@"Use the tmux dashboard to select which to open."];
|
|
+ if (tooMany) {
|
|
+ [[iTermNotificationController sharedInstance] notify:@"Too many tmux windows!" withDescription:@"Use the tmux dashboard to select which to open."];
|
|
+ } else {
|
|
+ [[iTermNotificationController sharedInstance] notify:@"Some tmux windows were hidden." withDescription:@"Use the tmux dashboard to select which to open."];
|
|
+ }
|
|
}
|
|
for (NSArray *record in windowsToOpen) {
|
|
DLog(@"Open window %@", record);
|
|
@@ -459,7 +471,7 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
NSString *setSizeCommand = [NSString stringWithFormat:@"refresh-client -C %d,%d",
|
|
size.width, [self adjustHeightForStatusBar:size.height]];
|
|
NSString *listWindowsCommand = [NSString stringWithFormat:@"list-windows -F %@", kListWindowsFormat];
|
|
- NSString *listSessionsCommand = @"list-sessions -F \"#{session_name}\"";
|
|
+ NSString *listSessionsCommand = @"list-sessions -F \"#{session_id} #{session_name}\"";
|
|
NSString *getAffinitiesCommand = [NSString stringWithFormat:@"show -v -q -t $%d @affinities", sessionId_];
|
|
NSString *getOriginsCommand = [NSString stringWithFormat:@"show -v -q -t $%d @origins", sessionId_];
|
|
NSString *getHotkeysCommand = [NSString stringWithFormat:@"show -v -q -t $%d @hotkeys", sessionId_];
|
|
@@ -715,10 +727,6 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
}
|
|
}
|
|
|
|
-- (void)setSize:(NSSize)size windows:(NSArray<NSString *> *)windows {
|
|
- [gateway_ sendCommandList:[self commandListToSetSize:size ofWindows:windows]];
|
|
-}
|
|
-
|
|
- (void)setWindowSizes:(NSArray<iTermTuple<NSString *, NSValue *> *> *)windowSizes {
|
|
[gateway_ sendCommandList:[self commandListToSetWindowSizes:windowSizes]];
|
|
}
|
|
@@ -727,14 +735,14 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
return [windowSizes mapWithBlock:^NSDictionary *(iTermTuple<NSString *,NSValue *> *tuple) {
|
|
NSString *window = tuple.firstObject;
|
|
NSSize size = tuple.secondObject.sizeValue;
|
|
- if ([window hasPrefix:@"pty"]) {
|
|
+ if ([window hasPrefix:@"pty"] || [window hasSuffix:@"_ph"]) {
|
|
return nil;
|
|
}
|
|
// 10000 comes from WINDOW_MAXIMUM in tmux.h
|
|
if (size.width < 1 || size.height < 1 || size.width >= 10000 || size.height >= 10000) {
|
|
return nil;
|
|
}
|
|
- NSString *command = [NSString stringWithFormat:@"resize-window -x %@ -y %@ -t @%@", @((int)size.width), @((int)size.height), window];
|
|
+ NSString *command = [NSString stringWithFormat:@"resize-window -x %@ -y %@ -t @%d", @((int)size.width), @((int)size.height), window.intValue];
|
|
NSDictionary *dict = [gateway_ dictionaryForCommand:command
|
|
responseTarget:self
|
|
responseSelector:@selector(handleResizeWindowResponse:)
|
|
@@ -857,11 +865,6 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
[gateway_ sendCommandList:@[ [gateway_ dictionaryForCommand:@"show-options -v -g set-titles"
|
|
responseTarget:self
|
|
responseSelector:@selector(handleShowSetTitles:)
|
|
- responseObject:nil
|
|
- flags:0],
|
|
- [gateway_ dictionaryForCommand:@"show-options -v -g set-titles-string"
|
|
- responseTarget:self
|
|
- responseSelector:@selector(handleShowSetTitlesString:)
|
|
responseObject:nil
|
|
flags:0] ]];
|
|
}
|
|
@@ -872,10 +875,6 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
object:self];
|
|
}
|
|
|
|
-- (void)handleShowSetTitlesString:(NSString *)setTitlesString {
|
|
- _setTitlesString = [setTitlesString copy];
|
|
-}
|
|
-
|
|
- (void)guessVersion {
|
|
// Run commands that will fail in successively older versions.
|
|
// show-window-options pane-border-format will succeed in 2.3 and later (presumably. 2.3 isn't out yet)
|
|
@@ -1013,17 +1012,25 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
}
|
|
|
|
// This is the oldest version supported. By the time you get here you know the version.
|
|
+ [self didGuessVersion];
|
|
+}
|
|
+
|
|
+// Actions to perform after the version number is known.
|
|
+- (void)didGuessVersion {
|
|
[self loadServerPID];
|
|
+ [self loadTitleFormat];
|
|
}
|
|
|
|
-- (BOOL)recyclingSupported {
|
|
- NSDecimalNumber *version1_9 = [NSDecimalNumber decimalNumberWithString:@"1.9"];
|
|
- if (gateway_.minimumServerVersion != nil) {
|
|
- return ([gateway_.minimumServerVersion compare:version1_9] != NSOrderedAscending);
|
|
- } else {
|
|
- // Assume 1.8
|
|
+- (BOOL)versionAtLeastDecimalNumberWithString:(NSString *)string {
|
|
+ NSDecimalNumber *version = [NSDecimalNumber decimalNumberWithString:string];
|
|
+ if (gateway_.minimumServerVersion == nil) {
|
|
return NO;
|
|
}
|
|
+ return ([gateway_.minimumServerVersion compare:version] != NSOrderedAscending);
|
|
+}
|
|
+
|
|
+- (BOOL)recyclingSupported {
|
|
+ return [self versionAtLeastDecimalNumberWithString:@"1.9"];
|
|
}
|
|
|
|
// Show an error and terminate the connection because tmux has an unsupported option turned on.
|
|
@@ -1143,10 +1150,10 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
responseSelector:nil];
|
|
}
|
|
|
|
-- (void)newWindowInSession:(NSString *)targetSession
|
|
- scope:(iTermVariableScope *)scope
|
|
- initialDirectory:(iTermInitialDirectory *)initialDirectory {
|
|
- [initialDirectory tmuxNewWindowCommandInSession:targetSession
|
|
+- (void)newWindowInSessionNumber:(NSNumber *)sessionNumber
|
|
+ scope:(iTermVariableScope *)scope
|
|
+ initialDirectory:(iTermInitialDirectory *)initialDirectory {
|
|
+ [initialDirectory tmuxNewWindowCommandInSessionNumber:sessionNumber
|
|
recyclingSupported:self.recyclingSupported
|
|
scope:scope
|
|
completion:
|
|
@@ -1164,7 +1171,11 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
responseObject:nil
|
|
flags:0]];
|
|
}
|
|
- [commands addObject:[gateway_ dictionaryForCommand:command responseTarget:nil responseSelector:nil responseObject:nil flags:0]];
|
|
+ [commands addObject:[gateway_ dictionaryForCommand:command
|
|
+ responseTarget:nil
|
|
+ responseSelector:nil
|
|
+ responseObject:nil
|
|
+ flags:0]];
|
|
[gateway_ sendCommandList:commands];
|
|
}];
|
|
}
|
|
@@ -1218,8 +1229,7 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
responseSelector:nil];
|
|
}
|
|
|
|
-- (void)unlinkWindowWithId:(int)windowId inSession:(NSString *)sessionName
|
|
-{
|
|
+- (void)unlinkWindowWithId:(int)windowId {
|
|
[gateway_ sendCommand:[NSString stringWithFormat:@"unlink-window -k -t @%d", windowId]
|
|
responseTarget:nil
|
|
responseSelector:nil
|
|
@@ -1232,11 +1242,14 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
}
|
|
|
|
- (void)renameWindowWithId:(int)windowId
|
|
- inSession:(NSString *)sessionName
|
|
+ inSessionNumber:(NSNumber *)sessionNumber
|
|
toName:(NSString *)newName {
|
|
NSString *theCommand;
|
|
- if (sessionName) {
|
|
- theCommand = [NSString stringWithFormat:@"rename-window -t \"%@:@%d\" \"%@\"", sessionName, windowId, [self stringByEscapingBackslashesAndRemovingNewlines:newName]];
|
|
+ if (sessionNumber) {
|
|
+ theCommand = [NSString stringWithFormat:@"rename-window -t \"$%d:@%d\" \"%@\"",
|
|
+ sessionNumber.intValue,
|
|
+ windowId,
|
|
+ [self stringByEscapingBackslashesAndRemovingNewlines:newName]];
|
|
} else {
|
|
theCommand = [NSString stringWithFormat:@"rename-window -t @%d \"%@\"", windowId, [self stringByEscapingBackslashesAndRemovingNewlines:newName]];
|
|
}
|
|
@@ -1312,9 +1325,23 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
[self sendCommandToSetTabColors];
|
|
}
|
|
|
|
+- (NSString *)encodedString:(NSString *)string prefix:(NSString *)prefix {
|
|
+ return [prefix stringByAppendingString:[[string dataUsingEncoding:NSUTF8StringEncoding] it_hexEncoded]];
|
|
+}
|
|
+
|
|
+- (NSString *)decodedString:(NSString *)string optionalPrefix:(NSString *)prefix {
|
|
+ if (![string hasPrefix:prefix]) {
|
|
+ return string;
|
|
+ }
|
|
+ return [[[NSString alloc] initWithData:[[string substringFromIndex:prefix.length] dataFromHexValues]
|
|
+ encoding:NSUTF8StringEncoding] autorelease];
|
|
+}
|
|
+
|
|
- (void)sendCommandToSetHotkeys {
|
|
+ NSString *hexEncoded = [self encodedString:[self.hotkeysString stringByEscapingQuotes]
|
|
+ prefix:iTermTmuxControllerEncodingPrefixHotkeys];
|
|
NSString *command = [NSString stringWithFormat:@"set -t $%d @hotkeys \"%@\"",
|
|
- sessionId_, [self.hotkeysString stringByEscapingQuotes]];
|
|
+ sessionId_, hexEncoded];
|
|
[gateway_ sendCommand:command
|
|
responseTarget:nil
|
|
responseSelector:nil
|
|
@@ -1323,8 +1350,10 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
}
|
|
|
|
- (void)sendCommandToSetTabColors {
|
|
+
|
|
NSString *command = [NSString stringWithFormat:@"set -t $%d @tab_colors \"%@\"",
|
|
- sessionId_, [self.tabColorsString stringByEscapingQuotes]];
|
|
+ sessionId_, [self encodedString:[self.tabColorsString stringByEscapingQuotes]
|
|
+ prefix:iTermTmuxControllerEncodingPrefixTabColors]];
|
|
[gateway_ sendCommand:command
|
|
responseTarget:nil
|
|
responseSelector:nil
|
|
@@ -1376,7 +1405,8 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
|
|
- (void)breakOutWindowPane:(int)windowPane toTabAside:(NSString *)sibling
|
|
{
|
|
- [gateway_ sendCommand:[NSString stringWithFormat:@"break-pane -P -F \"#{window_id}\" %@ \"%%%d\"", [self breakPaneWindowPaneFlag], windowPane]
|
|
+ [gateway_ sendCommand:[NSString stringWithFormat:@"break-pane -P -F \"#{window_id}\" %@ \"%%%d\"",
|
|
+ [self breakPaneWindowPaneFlag], windowPane]
|
|
responseTarget:self
|
|
responseSelector:@selector(windowPaneBrokeOutWithWindowId:setAffinityTo:)
|
|
responseObject:sibling
|
|
@@ -1435,19 +1465,19 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
}
|
|
|
|
- (void)linkWindowId:(int)windowId
|
|
- inSession:(NSString *)sessionName
|
|
- toSession:(NSString *)targetSession {
|
|
- [gateway_ sendCommand:[NSString stringWithFormat:@"link-window -s \"%@:@%d\" -t \"%@:+\"",
|
|
- sessionName, windowId, targetSession]
|
|
+ inSessionNumber:(int)sessionNumber
|
|
+ toSessionNumber:(int)targetSessionNumber {
|
|
+ [gateway_ sendCommand:[NSString stringWithFormat:@"link-window -s \"$%d:@%d\" -t \"$%d:+\"",
|
|
+ sessionNumber, windowId, targetSessionNumber]
|
|
responseTarget:nil
|
|
responseSelector:nil];
|
|
}
|
|
|
|
- (void)moveWindowId:(int)windowId
|
|
- inSession:(NSString *)sessionName
|
|
- toSession:(NSString *)targetSession {
|
|
- [gateway_ sendCommand:[NSString stringWithFormat:@"move-window -s \"%@:@%d\" -t \"%@:+\"",
|
|
- sessionName, windowId, targetSession]
|
|
+ inSessionNumber:(int)sessionNumber
|
|
+ toSessionNumber:(int)targetSessionNumber {
|
|
+ [gateway_ sendCommand:[NSString stringWithFormat:@"move-window -s \"$%d:@%d\" -t \"$%d:+\"",
|
|
+ sessionNumber, windowId, targetSessionNumber]
|
|
responseTarget:nil
|
|
responseSelector:nil];
|
|
}
|
|
@@ -1467,19 +1497,19 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
return pos;
|
|
}
|
|
|
|
-- (void)renameSession:(NSString *)oldName to:(NSString *)newName
|
|
-{
|
|
- NSString *renameCommand = [NSString stringWithFormat:@"rename-session -t \"%@\" \"%@\"",
|
|
- [oldName stringByEscapingQuotes],
|
|
+- (void)renameSessionNumber:(int)sessionNumber
|
|
+ to:(NSString *)newName {
|
|
+ NSString *renameCommand = [NSString stringWithFormat:@"rename-session -t \"$%d\" \"%@\"",
|
|
+ sessionNumber,
|
|
[newName stringByEscapingQuotes]];
|
|
[gateway_ sendCommand:renameCommand responseTarget:nil responseSelector:nil];
|
|
}
|
|
|
|
-- (void)killSession:(NSString *)sessionName
|
|
-{
|
|
- NSString *killCommand = [NSString stringWithFormat:@"kill-session -t \"%@\"",
|
|
- [sessionName stringByEscapingQuotes]];
|
|
- [gateway_ sendCommand:killCommand responseTarget:nil responseSelector:nil];
|
|
+- (void)killSessionNumber:(int)sessionNumber {
|
|
+ NSString *killCommand = [NSString stringWithFormat:@"kill-session -t \"$%d\"", sessionNumber];
|
|
+ [gateway_ sendCommand:killCommand
|
|
+ responseTarget:nil
|
|
+ responseSelector:nil];
|
|
[self listSessions];
|
|
}
|
|
|
|
@@ -1493,44 +1523,46 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
[self listSessions];
|
|
}
|
|
|
|
-- (void)attachToSession:(NSString *)sessionName
|
|
-{
|
|
- NSString *attachCommand = [NSString stringWithFormat:@"attach-session -t \"%@\"",
|
|
- [sessionName stringByEscapingQuotes]];
|
|
+- (void)attachToSessionWithNumber:(int)sessionNumber {
|
|
+ NSString *attachCommand = [NSString stringWithFormat:@"attach-session -t \"$%d\"", sessionNumber];
|
|
[gateway_ sendCommand:attachCommand
|
|
responseTarget:nil
|
|
responseSelector:nil];
|
|
}
|
|
|
|
-- (void)listWindowsInSession:(NSString *)sessionName
|
|
- target:(id)target
|
|
- selector:(SEL)selector
|
|
- object:(id)object {
|
|
+- (void)listWindowsInSessionNumber:(int)sessionNumber
|
|
+ target:(id)target
|
|
+ selector:(SEL)selector
|
|
+ object:(id)object {
|
|
if (detached_ || !object) {
|
|
// This can happen if you're not attached to a session.
|
|
return;
|
|
}
|
|
- NSString *listWindowsCommand = [NSString stringWithFormat:@"list-windows -F %@ -t \"%@\"",
|
|
- kListWindowsFormat, sessionName];
|
|
+ NSString *listWindowsCommand = [NSString stringWithFormat:@"list-windows -F %@ -t \"$%d\"",
|
|
+ kListWindowsFormat, sessionNumber];
|
|
// Wait a few seconds. We always get a windows-close notification when the last window in
|
|
// a window closes. To avoid spamming the command line with list-windows, we wait a bit to see
|
|
// if there is an exit notification coming down the pipe.
|
|
const CGFloat kListWindowsDelay = 1.5;
|
|
[listWindowsTimer_ invalidate];
|
|
- listWindowsTimer_ = [NSTimer scheduledTimerWithTimeInterval:kListWindowsDelay
|
|
- target:self
|
|
- selector:@selector(listWindowsTimerFired:)
|
|
- userInfo:[NSArray arrayWithObjects:listWindowsCommand, object, target, NSStringFromSelector(selector), nil]
|
|
- repeats:NO];
|
|
+ listWindowsTimer_ =
|
|
+ [NSTimer scheduledTimerWithTimeInterval:kListWindowsDelay
|
|
+ target:self
|
|
+ selector:@selector(listWindowsTimerFired:)
|
|
+ userInfo:@[listWindowsCommand,
|
|
+ object,
|
|
+ target,
|
|
+ NSStringFromSelector(selector) ]
|
|
+ repeats:NO];
|
|
}
|
|
|
|
- (void)listWindowsTimerFired:(NSTimer *)timer
|
|
{
|
|
NSArray *array = [timer userInfo];
|
|
- NSString *command = [array objectAtIndex:0];
|
|
- id object = [array objectAtIndex:1];
|
|
- id target = [array objectAtIndex:2];
|
|
- NSString *selector = [array objectAtIndex:3];
|
|
+ NSString *command = array[0];
|
|
+ id object = array[1];
|
|
+ id target = array[2];
|
|
+ NSString *selector = array[3];
|
|
|
|
[listWindowsTimer_ invalidate];
|
|
listWindowsTimer_ = nil;
|
|
@@ -1538,10 +1570,7 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
[gateway_ sendCommand:command
|
|
responseTarget:self
|
|
responseSelector:@selector(didListWindows:userData:)
|
|
- responseObject:[NSArray arrayWithObjects:object,
|
|
- selector,
|
|
- target,
|
|
- nil]
|
|
+ responseObject:@[object, selector, target]
|
|
flags:kTmuxGatewayCommandShouldTolerateErrors]; // Tolerates errors because the session may have been detached by the time we get the notification or the timer fires.
|
|
}
|
|
|
|
@@ -1549,8 +1578,10 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
{
|
|
NSString *hidden = [[hiddenWindows_ allObjects] componentsJoinedByString:@","];
|
|
NSString *command = [NSString stringWithFormat:
|
|
- @"set -t $%d @hidden \"%@\"",
|
|
- sessionId_, hidden];
|
|
+ @"set -t $%d @hidden \"%@\"",
|
|
+ sessionId_,
|
|
+ [self encodedString:hidden
|
|
+ prefix:iTermTmuxControllerEncodingPrefixHidden]];
|
|
[gateway_ sendCommand:command
|
|
responseTarget:nil
|
|
responseSelector:nil
|
|
@@ -1594,7 +1625,9 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
NSString *enc = [maps componentsJoinedByString:@" "];
|
|
DLog(@"Save window origins to %@ called from %@", enc, [NSThread callStackSymbols]);
|
|
NSString *command = [NSString stringWithFormat:@"set -t $%d @origins \"%@\"",
|
|
- sessionId_, [enc stringByEscapingQuotes]];
|
|
+ sessionId_,
|
|
+ [self encodedString:[enc stringByEscapingQuotes]
|
|
+ prefix:iTermTmuxControllerEncodingPrefixOrigins]];
|
|
if (!lastOrigins_ || ![command isEqualToString:lastOrigins_]) {
|
|
[lastOrigins_ release];
|
|
lastOrigins_ = [command copy];
|
|
@@ -1603,7 +1636,8 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
responseTarget:self
|
|
responseSelector:@selector(saveWindowOriginsResponse:)];
|
|
}
|
|
- [self getOriginsResponse:enc];
|
|
+ [self getOriginsResponse:[self encodedString:[enc stringByEscapingQuotes]
|
|
+ prefix:iTermTmuxControllerEncodingPrefixOrigins]];
|
|
}
|
|
|
|
- (void)saveWindowOriginsResponse:(NSString *)response
|
|
@@ -1651,7 +1685,8 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
// Update affinities if any have changed.
|
|
NSString *arg = [affinities componentsJoinedByString:@" "];
|
|
NSString *command = [NSString stringWithFormat:@"set -t $%d @affinities \"%@\"",
|
|
- sessionId_, [arg stringByEscapingQuotes]];
|
|
+ sessionId_, [self encodedString:[arg stringByEscapingQuotes]
|
|
+ prefix:iTermTmuxControllerEncodingPrefixAffinities]];
|
|
if ([command isEqualToString:lastSaveAffinityCommand_]) {
|
|
return;
|
|
}
|
|
@@ -1763,7 +1798,7 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
responseSelector:@selector(didSetLayout:)
|
|
responseObject:nil
|
|
flags:0],
|
|
- [gateway_ dictionaryForCommand:[NSString stringWithFormat:@"list-windows -F \"#{window_id} #{window_layout} #{window_flags}\" -t \"%@\"", sessionName_]
|
|
+ [gateway_ dictionaryForCommand:[NSString stringWithFormat:@"list-windows -F \"#{window_id} #{window_layout} #{window_flags}\" -t \"$%d\"", sessionId_]
|
|
responseTarget:self
|
|
responseSelector:@selector(didListWindowsSubsequentToSettingLayout:)
|
|
responseObject:nil
|
|
@@ -1777,7 +1812,7 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
responseSelector:@selector(didSetLayout:)
|
|
responseObject:nil
|
|
flags:0],
|
|
- [gateway_ dictionaryForCommand:[NSString stringWithFormat:@"list-windows -F \"#{window_id} #{window_layout} #{window_flags}\" -t \"%@\"", sessionName_]
|
|
+ [gateway_ dictionaryForCommand:[NSString stringWithFormat:@"list-windows -F \"#{window_id} #{window_layout} #{window_flags}\" -t \"$%d\"", sessionId_]
|
|
responseTarget:self
|
|
responseSelector:@selector(didListWindowsSubsequentToSettingLayout:)
|
|
responseObject:nil
|
|
@@ -1798,31 +1833,32 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
|
|
#pragma mark - Private
|
|
|
|
-- (void)getOriginsResponse:(NSString *)result
|
|
-{
|
|
- [origins_ removeAllObjects];
|
|
- if ([result length] > 0) {
|
|
- NSArray *windows = [result componentsSeparatedByString:@" "];
|
|
- for (NSString *wstr in windows) {
|
|
- NSArray *tuple = [wstr componentsSeparatedByString:@":"];
|
|
- if (tuple.count != 2) {
|
|
- continue;
|
|
- }
|
|
- NSString *windowsStr = [tuple objectAtIndex:0];
|
|
- NSString *coords = [tuple objectAtIndex:1];
|
|
- NSArray *windowIds = [windowsStr componentsSeparatedByString:@","];
|
|
- NSArray *xy = [coords componentsSeparatedByString:@","];
|
|
- if (xy.count != 2) {
|
|
- continue;
|
|
- }
|
|
- NSPoint origin = NSMakePoint([[xy objectAtIndex:0] intValue],
|
|
- [[xy objectAtIndex:1] intValue]);
|
|
- for (NSString *wid in windowIds) {
|
|
- [origins_ setObject:[NSValue valueWithPoint:origin]
|
|
- forKey:[NSNumber numberWithInt:[wid intValue]]];
|
|
- }
|
|
- }
|
|
- }
|
|
+- (void)getOriginsResponse:(NSString *)encodedResult {
|
|
+ NSString *result = [self decodedString:encodedResult
|
|
+ optionalPrefix:iTermTmuxControllerEncodingPrefixOrigins];
|
|
+ [origins_ removeAllObjects];
|
|
+ if ([result length] > 0) {
|
|
+ NSArray *windows = [result componentsSeparatedByString:@" "];
|
|
+ for (NSString *wstr in windows) {
|
|
+ NSArray *tuple = [wstr componentsSeparatedByString:@":"];
|
|
+ if (tuple.count != 2) {
|
|
+ continue;
|
|
+ }
|
|
+ NSString *windowsStr = [tuple objectAtIndex:0];
|
|
+ NSString *coords = [tuple objectAtIndex:1];
|
|
+ NSArray *windowIds = [windowsStr componentsSeparatedByString:@","];
|
|
+ NSArray *xy = [coords componentsSeparatedByString:@","];
|
|
+ if (xy.count != 2) {
|
|
+ continue;
|
|
+ }
|
|
+ NSPoint origin = NSMakePoint([[xy objectAtIndex:0] intValue],
|
|
+ [[xy objectAtIndex:1] intValue]);
|
|
+ for (NSString *wid in windowIds) {
|
|
+ [origins_ setObject:[NSValue valueWithPoint:origin]
|
|
+ forKey:[NSNumber numberWithInt:[wid intValue]]];
|
|
+ }
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
- (NSString *)shortStringForHotkeyDictionary:(NSDictionary *)dict paneID:(int)wp {
|
|
@@ -1851,7 +1887,8 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
return [parts componentsJoinedByString:@" "];
|
|
}
|
|
|
|
-- (void)getHotkeysResponse:(NSString *)result {
|
|
+- (void)getHotkeysResponse:(NSString *)encodedResult {
|
|
+ NSString *result = [self decodedString:encodedResult optionalPrefix:iTermTmuxControllerEncodingPrefixHotkeys];
|
|
[_hotkeys removeAllObjects];
|
|
if (result.length > 0) {
|
|
[_hotkeys removeAllObjects];
|
|
@@ -1870,7 +1907,9 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
}
|
|
}
|
|
|
|
-- (void)getTabColorsResponse:(NSString *)result {
|
|
+- (void)getTabColorsResponse:(NSString *)encodedResult {
|
|
+ NSString *result = [self decodedString:encodedResult
|
|
+ optionalPrefix:iTermTmuxControllerEncodingPrefixTabColors];
|
|
[_tabColors removeAllObjects];
|
|
if (result.length > 0) {
|
|
[_tabColors removeAllObjects];
|
|
@@ -1903,14 +1942,15 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
response = @"";
|
|
}
|
|
TSVDocument *doc = [response tsvDocumentWithFields:[self listWindowFields]];
|
|
- id object = [userData objectAtIndex:0];
|
|
- SEL selector = NSSelectorFromString([userData objectAtIndex:1]);
|
|
- id target = [userData objectAtIndex:2];
|
|
+ id object = userData[0];
|
|
+ SEL selector = NSSelectorFromString(userData[1]);
|
|
+ id target = userData[2];
|
|
[target performSelector:selector withObject:doc withObject:object];
|
|
}
|
|
|
|
-- (void)getHiddenWindowsResponse:(NSString *)response
|
|
-{
|
|
+- (void)getHiddenWindowsResponse:(NSString *)encodedResponse {
|
|
+ NSString *response = [self decodedString:encodedResponse
|
|
+ optionalPrefix:iTermTmuxControllerEncodingPrefixHidden];
|
|
[hiddenWindows_ removeAllObjects];
|
|
if ([response length] > 0) {
|
|
NSArray *windowIds = [response componentsSeparatedByString:@","];
|
|
@@ -1922,7 +1962,7 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
}
|
|
|
|
- (void)getAffinitiesResponse:(NSString *)result {
|
|
- [self setAffinitiesFromString:result];
|
|
+ [self setAffinitiesFromString:[self decodedString:result optionalPrefix:iTermTmuxControllerEncodingPrefixAffinities]];
|
|
}
|
|
|
|
- (NSArray *)componentsOfAffinities:(NSString *)affinities {
|
|
@@ -1991,9 +2031,28 @@ static NSDictionary *iTermTmuxControllerDefaultFontOverridesFromProfile(Profile
|
|
|
|
- (void)listSessionsResponse:(NSString *)result
|
|
{
|
|
- self.sessions = [result componentsSeparatedByRegex:@"\n"];
|
|
+ self.sessionObjects = [[result componentsSeparatedByRegex:@"\n"] mapWithBlock:^iTermTmuxSessionObject *(NSString *line) {
|
|
+ const NSInteger space = [line rangeOfString:@" "].location;
|
|
+ if (space == NSNotFound) {
|
|
+ return nil;
|
|
+ }
|
|
+ NSString *sessionID = [line substringToIndex:space];
|
|
+ NSString *sessionName = [line substringFromIndex:space + 1];
|
|
+ if (![sessionID hasPrefix:@"$"]) {
|
|
+ return nil;
|
|
+ }
|
|
+ NSScanner *scanner = [NSScanner scannerWithString:[sessionID substringFromIndex:1]];
|
|
+ int sessionNumber = -1;
|
|
+ if (![scanner scanInt:&sessionNumber] || sessionNumber < 0) {
|
|
+ return nil;
|
|
+ }
|
|
+ iTermTmuxSessionObject *obj = [[[iTermTmuxSessionObject alloc] init] autorelease];
|
|
+ obj.name = sessionName;
|
|
+ obj.number = sessionNumber;
|
|
+ return obj;
|
|
+ }];
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kTmuxControllerSessionsDidChange
|
|
- object:self.sessions];
|
|
+ object:nil];
|
|
}
|
|
|
|
- (void)listedWindowsToOpenOne:(NSString *)response
|
|
diff --git sources/TmuxDashboardController.m sources/TmuxDashboardController.m
|
|
index b83bfe6..b2ed7b7 100644
|
|
--- sources/TmuxDashboardController.m
|
|
+++ sources/TmuxDashboardController.m
|
|
@@ -49,7 +49,7 @@
|
|
if (self) {
|
|
[self window];
|
|
|
|
- [sessionsTable_ selectSessionWithName:[[self tmuxController] sessionName]];
|
|
+ [sessionsTable_ selectSessionNumber:[[self tmuxController] sessionId]];
|
|
[self reloadWindows];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(tmuxControllerDetached:)
|
|
@@ -140,78 +140,79 @@
|
|
|
|
#pragma mark TmuxSessionsTableProtocol
|
|
|
|
-- (void)renameSessionWithName:(NSString *)oldName toName:(NSString *)newName
|
|
-{
|
|
- [[self tmuxController] renameSession:oldName to:newName];
|
|
+- (void)renameSessionWithNumber:(int)sessionNumber toName:(NSString *)newName {
|
|
+ [[self tmuxController] renameSessionNumber:sessionNumber
|
|
+ to:newName];
|
|
}
|
|
|
|
-- (void)removeSessionWithName:(NSString *)sessionName
|
|
-{
|
|
- [[self tmuxController] killSession:sessionName];
|
|
+- (void)removeSessionWithNumber:(int)sessionNumber {
|
|
+ [[self tmuxController] killSessionNumber:sessionNumber];
|
|
}
|
|
|
|
-- (void)addSessionWithName:(NSString *)sessionName
|
|
-{
|
|
+- (void)addSessionWithName:(NSString *)sessionName {
|
|
[[self tmuxController] addSessionWithName:sessionName];
|
|
}
|
|
|
|
-- (void)attachToSessionWithName:(NSString *)sessionName
|
|
-{
|
|
- [[self tmuxController] attachToSession:sessionName];
|
|
+- (void)attachToSessionWithNumber:(int)sessionNumber {
|
|
+ [[self tmuxController] attachToSessionWithNumber:sessionNumber];
|
|
}
|
|
|
|
-- (void)detach
|
|
-{
|
|
+- (void)detach {
|
|
[[self tmuxController] requestDetach];
|
|
}
|
|
|
|
-- (NSString *)nameOfAttachedSession
|
|
-{
|
|
- return [[self tmuxController] sessionName];
|
|
+- (NSNumber *)numberOfAttachedSession {
|
|
+ TmuxController *controller = [self tmuxController];
|
|
+ if (!controller) {
|
|
+ return nil;
|
|
+ }
|
|
+ return @([controller sessionId]);
|
|
}
|
|
|
|
-- (NSArray *)sessions
|
|
-{
|
|
- return [[self tmuxController] sessions];
|
|
+- (NSArray<iTermTmuxSessionObject *> *)sessionsTableModelValues:(id)sender {
|
|
+ return [self.tmuxController sessionObjects];
|
|
}
|
|
|
|
-- (void)selectedSessionChangedTo:(NSString *)newSessionName
|
|
-{
|
|
+- (NSArray<iTermTmuxSessionObject *> *)sessionsTableObjects:(TmuxSessionsTable *)sender {
|
|
+ return [[self tmuxController] sessionObjects];
|
|
+}
|
|
+
|
|
+- (void)selectedSessionDidChange {
|
|
[windowsTable_ setWindows:[NSArray array]];
|
|
[self reloadWindows];
|
|
}
|
|
|
|
- (void)linkWindowId:(int)windowId
|
|
- inSession:(NSString *)sessionName
|
|
- toSession:(NSString *)targetSession
|
|
-{
|
|
+ inSessionNumber:(int)sourceSessionNumber
|
|
+ toSessionNumber:(int)targetSessionNumber {
|
|
[[self tmuxController] linkWindowId:windowId
|
|
- inSession:sessionName
|
|
- toSession:targetSession];
|
|
+ inSessionNumber:sourceSessionNumber
|
|
+ toSessionNumber:targetSessionNumber];
|
|
}
|
|
|
|
- (void)moveWindowId:(int)windowId
|
|
- inSession:(NSString *)sessionName
|
|
- toSession:(NSString *)targetSession
|
|
-{
|
|
+ inSessionNumber:(int)sessionNumber
|
|
+ toSessionNumber:(int)targetSessionNumber {
|
|
[[self tmuxController] moveWindowId:windowId
|
|
- inSession:sessionName
|
|
- toSession:targetSession];
|
|
+ inSessionNumber:sessionNumber
|
|
+ toSessionNumber:targetSessionNumber];
|
|
}
|
|
|
|
#pragma mark TmuxWindowsTableProtocol
|
|
|
|
-- (void)reloadWindows
|
|
-{
|
|
- [[self tmuxController] listWindowsInSession:[sessionsTable_ selectedSessionName]
|
|
- target:self
|
|
- selector:@selector(setWindows:forSession:)
|
|
- object:[sessionsTable_ selectedSessionName]];
|
|
+- (void)reloadWindows {
|
|
+ NSNumber *sessionNumber = [sessionsTable_ selectedSessionNumber];
|
|
+ if (!sessionNumber) {
|
|
+ return;
|
|
+ }
|
|
+ [[self tmuxController] listWindowsInSessionNumber:sessionNumber.intValue
|
|
+ target:self
|
|
+ selector:@selector(setWindows:forSession:)
|
|
+ object:[sessionsTable_ selectedSessionNumber]];
|
|
}
|
|
|
|
-- (void)setWindows:(TSVDocument *)doc forSession:(NSString *)sessionName
|
|
-{
|
|
- if ([sessionName isEqualToString:[sessionsTable_ selectedSessionName]]) {
|
|
+- (void)setWindows:(TSVDocument *)doc forSession:(NSNumber *)sessionNumber {
|
|
+ if ([sessionNumber isEqual:[sessionsTable_ selectedSessionNumber]]) {
|
|
NSMutableArray *windows = [NSMutableArray array];
|
|
for (NSArray *record in doc.records) {
|
|
[windows addObject:[NSMutableArray arrayWithObjects:
|
|
@@ -223,18 +224,19 @@
|
|
}
|
|
}
|
|
|
|
-- (void)renameWindowWithId:(int)windowId toName:(NSString *)newName
|
|
-{
|
|
+- (void)renameWindowWithId:(int)windowId toName:(NSString *)newName {
|
|
+ NSNumber *sessionNumber = [sessionsTable_ selectedSessionNumber];
|
|
+ if (!sessionNumber) {
|
|
+ return;
|
|
+ }
|
|
[[self tmuxController] renameWindowWithId:windowId
|
|
- inSession:[sessionsTable_ selectedSessionName]
|
|
+ inSessionNumber:sessionNumber
|
|
toName:newName];
|
|
[self reloadWindows];
|
|
}
|
|
|
|
-- (void)unlinkWindowWithId:(int)windowId
|
|
-{
|
|
- [[self tmuxController] unlinkWindowWithId:windowId
|
|
- inSession:[sessionsTable_ selectedSessionName]];
|
|
+- (void)unlinkWindowWithId:(int)windowId {
|
|
+ [[self tmuxController] unlinkWindowWithId:windowId];
|
|
[self reloadWindows];
|
|
}
|
|
|
|
@@ -242,15 +244,14 @@
|
|
NSString *lastName = [[windowsTable_ names] lastObject];
|
|
if (lastName) {
|
|
TmuxController *tmuxController = self.tmuxController;
|
|
- [tmuxController newWindowInSession:[sessionsTable_ selectedSessionName]
|
|
- scope:[iTermVariableScope globalsScope]
|
|
- initialDirectory:[iTermInitialDirectory initialDirectoryFromProfile:tmuxController.sharedProfile
|
|
- objectType:iTermWindowObject]];
|
|
+ [tmuxController newWindowInSessionNumber:[sessionsTable_ selectedSessionNumber]
|
|
+ scope:[iTermVariableScope globalsScope]
|
|
+ initialDirectory:[iTermInitialDirectory initialDirectoryFromProfile:tmuxController.sharedProfile
|
|
+ objectType:iTermWindowObject]];
|
|
}
|
|
}
|
|
|
|
-- (void)showWindowsWithIds:(NSArray *)windowIds inTabs:(BOOL)inTabs
|
|
-{
|
|
+- (void)showWindowsWithIds:(NSArray *)windowIds inTabs:(BOOL)inTabs {
|
|
if (inTabs) {
|
|
for (NSNumber *wid in windowIds) {
|
|
[[self tmuxController] openWindowWithId:[wid intValue]
|
|
@@ -265,27 +266,24 @@
|
|
profile:self.tmuxController.sharedProfile];
|
|
}
|
|
}
|
|
- [[self tmuxController] saveHiddenWindows];
|
|
+ [[self tmuxController] saveHiddenWindows];
|
|
}
|
|
|
|
- (void)hideWindowWithId:(int)windowId
|
|
{
|
|
- [[self tmuxController] hideWindow:windowId];
|
|
+ [[self tmuxController] hideWindow:windowId];
|
|
[windowsTable_ updateEnabledStateOfButtons];
|
|
}
|
|
|
|
-- (BOOL)haveSelectedSession
|
|
-{
|
|
- return [sessionsTable_ selectedSessionName] != nil;
|
|
+- (BOOL)haveSelectedSession {
|
|
+ return [sessionsTable_ selectedSessionNumber] != nil;
|
|
}
|
|
|
|
-- (BOOL)currentSessionSelected
|
|
-{
|
|
- return [[sessionsTable_ selectedSessionName] isEqualToString:[[self tmuxController] sessionName]];
|
|
+- (BOOL)currentSessionSelected {
|
|
+ return [[sessionsTable_ selectedSessionNumber] isEqual:@([[self tmuxController] sessionId])];
|
|
}
|
|
|
|
-- (BOOL)haveOpenWindowWithId:(int)windowId
|
|
-{
|
|
+- (BOOL)haveOpenWindowWithId:(int)windowId {
|
|
return [[self tmuxController] window:windowId] != nil;
|
|
}
|
|
|
|
@@ -294,48 +292,41 @@
|
|
[tab.activeSession reveal];
|
|
}
|
|
|
|
-- (NSString *)selectedSessionName
|
|
-{
|
|
- return [sessionsTable_ selectedSessionName];
|
|
+- (NSNumber *)selectedSessionNumber {
|
|
+ return [sessionsTable_ selectedSessionNumber];
|
|
}
|
|
|
|
#pragma mark - Private
|
|
|
|
-- (void)tmuxControllerDetached:(NSNotification *)notification
|
|
-{
|
|
- [sessionsTable_ setSessions:[NSArray array]];
|
|
+- (void)tmuxControllerDetached:(NSNotification *)notification {
|
|
+ [sessionsTable_ setSessionObjects:@[]];
|
|
}
|
|
|
|
-- (void)tmuxControllerSessionsDidChange:(NSNotification *)notification
|
|
-{
|
|
- [sessionsTable_ setSessions:[[self tmuxController] sessions]];
|
|
+- (void)tmuxControllerSessionsDidChange:(NSNotification *)notification {
|
|
+ [sessionsTable_ setSessionObjects:[[self tmuxController] sessionObjects]];
|
|
}
|
|
|
|
-- (void)tmuxControllerWindowsDidChange:(NSNotification *)notification
|
|
-{
|
|
+- (void)tmuxControllerWindowsDidChange:(NSNotification *)notification {
|
|
if ([[self window] isVisible]) {
|
|
[self reloadWindows];
|
|
}
|
|
}
|
|
|
|
-- (void)tmuxControllerAttachedSessionChanged:(NSNotification *)notification
|
|
-{
|
|
+- (void)tmuxControllerAttachedSessionChanged:(NSNotification *)notification {
|
|
if ([[self window] isVisible]) {
|
|
- [sessionsTable_ selectSessionWithName:[[self tmuxController] sessionName]];
|
|
+ [sessionsTable_ selectSessionNumber:[[self tmuxController] sessionId]];
|
|
[windowsTable_ updateEnabledStateOfButtons];
|
|
}
|
|
}
|
|
|
|
-- (void)tmuxControllerWindowOpenedOrClosed:(NSNotification *)notification
|
|
-{
|
|
+- (void)tmuxControllerWindowOpenedOrClosed:(NSNotification *)notification {
|
|
if ([[self window] isVisible]) {
|
|
[windowsTable_ updateEnabledStateOfButtons];
|
|
[windowsTable_ reloadData];
|
|
}
|
|
}
|
|
|
|
-- (void)tmuxControllerWindowWasRenamed:(NSNotification *)notification
|
|
-{
|
|
+- (void)tmuxControllerWindowWasRenamed:(NSNotification *)notification {
|
|
if ([[self window] isVisible]) {
|
|
NSArray *objects = [notification object];
|
|
int wid = [[objects objectAtIndex:0] intValue];
|
|
@@ -344,8 +335,7 @@
|
|
}
|
|
}
|
|
|
|
-- (void)tmuxControllerSessionWasRenamed:(NSNotification *)notification
|
|
-{
|
|
+- (void)tmuxControllerSessionWasRenamed:(NSNotification *)notification {
|
|
// This is a bit of extra work but the sessions table wasn't built knowing about session IDs.
|
|
[[self tmuxController] listSessions];
|
|
}
|
|
@@ -364,8 +354,7 @@
|
|
[self connectionSelectionDidChange:nil];
|
|
}
|
|
|
|
-- (TmuxController *)tmuxController
|
|
-{
|
|
+- (TmuxController *)tmuxController {
|
|
return [[TmuxControllerRegistry sharedInstance] controllerForClient:[self currentClient]]; // TODO: track the current client when multiples are supported
|
|
}
|
|
|
|
@@ -375,7 +364,7 @@
|
|
|
|
|
|
- (IBAction)connectionSelectionDidChange:(id)sender {
|
|
- [sessionsTable_ setSessions:[[self tmuxController] sessions]];
|
|
+ [sessionsTable_ setSessionObjects:[[self tmuxController] sessionObjects]];
|
|
[self reloadWindows];
|
|
}
|
|
|
|
diff --git sources/TmuxSessionsTable.h sources/TmuxSessionsTable.h
|
|
index 856f0f5..a3e5823 100644
|
|
--- sources/TmuxSessionsTable.h
|
|
+++ sources/TmuxSessionsTable.h
|
|
@@ -9,21 +9,25 @@
|
|
#import <Cocoa/Cocoa.h>
|
|
#import "FutureMethods.h"
|
|
|
|
+@class TmuxSessionsTable;
|
|
+@class iTermTmuxSessionObject;
|
|
+
|
|
@protocol TmuxSessionsTableProtocol <NSObject>
|
|
|
|
-- (NSArray *)sessions;
|
|
-- (void)renameSessionWithName:(NSString *)oldName toName:(NSString *)newName;
|
|
-- (void)removeSessionWithName:(NSString *)sessionName;
|
|
+- (NSArray<iTermTmuxSessionObject *> *)sessionsTableObjects:(TmuxSessionsTable *)sender;
|
|
+- (void)renameSessionWithNumber:(int)sessionNumber
|
|
+ toName:(NSString *)newName;
|
|
+- (void)removeSessionWithNumber:(int)sessionNumber;
|
|
- (void)addSessionWithName:(NSString *)sessionName;
|
|
-- (void)attachToSessionWithName:(NSString *)sessionName;
|
|
-- (NSString *)nameOfAttachedSession;
|
|
-- (void)selectedSessionChangedTo:(NSString *)newName;
|
|
+- (void)attachToSessionWithNumber:(int)sessionNumber;
|
|
+- (NSNumber *)numberOfAttachedSession;
|
|
+- (void)selectedSessionDidChange;
|
|
- (void)linkWindowId:(int)windowId
|
|
- inSession:(NSString *)sessionName
|
|
- toSession:(NSString *)targetSession;
|
|
+ inSessionNumber:(int)sourceSessionNumber
|
|
+ toSessionNumber:(int)targetSessionNumber;
|
|
- (void)moveWindowId:(int)windowId
|
|
- inSession:(NSString *)sessionName
|
|
- toSession:(NSString *)targetSession;
|
|
+ inSessionNumber:(int)sessionNumber
|
|
+ toSessionNumber:(int)targetSessionNumber;
|
|
- (void)detach;
|
|
|
|
@end
|
|
@@ -31,9 +35,9 @@
|
|
@interface TmuxSessionsTable : NSObject <NSTableViewDelegate, NSTableViewDataSource>
|
|
|
|
@property(nonatomic, assign) id<TmuxSessionsTableProtocol> delegate;
|
|
-@property(nonatomic, readonly) NSString *selectedSessionName;
|
|
+@property(nonatomic, readonly) NSNumber *selectedSessionNumber;
|
|
|
|
-- (void)setSessions:(NSArray *)names;
|
|
-- (void)selectSessionWithName:(NSString *)name;
|
|
+- (void)setSessionObjects:(NSArray<iTermTmuxSessionObject *> *)names;
|
|
+- (void)selectSessionNumber:(int)number;
|
|
|
|
@end
|
|
diff --git sources/TmuxSessionsTable.m sources/TmuxSessionsTable.m
|
|
index f9a086f..88e2f72 100644
|
|
--- sources/TmuxSessionsTable.m
|
|
+++ sources/TmuxSessionsTable.m
|
|
@@ -7,12 +7,15 @@
|
|
//
|
|
|
|
#import "TmuxSessionsTable.h"
|
|
+
|
|
#import "FutureMethods.h"
|
|
+#import "iTermTmuxSessionObject.h"
|
|
+#import "NSArray+iTerm.h"
|
|
|
|
extern NSString *kWindowPasteboardType;
|
|
|
|
@implementation TmuxSessionsTable {
|
|
- NSMutableArray *model_;
|
|
+ NSMutableArray<iTermTmuxSessionObject *> *_model;
|
|
BOOL canAttachToSelectedSession_;
|
|
|
|
IBOutlet NSTableColumn *checkColumn_;
|
|
@@ -28,7 +31,7 @@ extern NSString *kWindowPasteboardType;
|
|
- (instancetype)init {
|
|
self = [super init];
|
|
if (self) {
|
|
- model_ = [[NSMutableArray alloc] init];
|
|
+ _model = [[NSMutableArray alloc] init];
|
|
}
|
|
return self;
|
|
}
|
|
@@ -39,27 +42,27 @@ extern NSString *kWindowPasteboardType;
|
|
[tableView_ setDraggingDestinationFeedbackStyle:NSTableViewDraggingDestinationFeedbackStyleRegular];
|
|
}
|
|
|
|
-- (void)dealloc
|
|
-{
|
|
- [model_ release];
|
|
+- (void)dealloc {
|
|
+ [_model release];
|
|
[super dealloc];
|
|
}
|
|
|
|
- (void)setDelegate:(id<TmuxSessionsTableProtocol>)delegate {
|
|
delegate_ = delegate;
|
|
- [self setSessions:[delegate_ sessions]];
|
|
+ [self setSessionObjects:[delegate_ sessionsTableObjects:self]];
|
|
}
|
|
|
|
-- (void)setSessions:(NSArray *)names
|
|
+- (void)setSessionObjects:(NSArray<iTermTmuxSessionObject *> *)sessions
|
|
{
|
|
- [model_ removeAllObjects];
|
|
- [model_ addObjectsFromArray:names];
|
|
+ [_model removeAllObjects];
|
|
+ [_model addObjectsFromArray:sessions];
|
|
[tableView_ reloadData];
|
|
}
|
|
|
|
-- (void)selectSessionWithName:(NSString *)name
|
|
-{
|
|
- NSUInteger i = [model_ indexOfObject:name];
|
|
+- (void)selectSessionNumber:(int)number {
|
|
+ NSInteger i = [_model indexOfObjectPassingTest:^BOOL(iTermTmuxSessionObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
+ return obj.number == number;
|
|
+ }];
|
|
if (i != NSNotFound) {
|
|
[tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:i]
|
|
byExtendingSelection:NO];
|
|
@@ -74,47 +77,45 @@ extern NSString *kWindowPasteboardType;
|
|
|
|
- (IBAction)removeSession:(id)sender
|
|
{
|
|
- NSString *name = [self selectedSessionName];
|
|
- if (name) {
|
|
- [delegate_ removeSessionWithName:name];
|
|
+ NSNumber *number = [self selectedSessionNumber];
|
|
+ if (number) {
|
|
+ [delegate_ removeSessionWithNumber:number.intValue];
|
|
}
|
|
}
|
|
|
|
- (IBAction)attach:(id)sender {
|
|
- NSString *name = [self selectedSessionName];
|
|
- if (name) {
|
|
- [delegate_ attachToSessionWithName:name];
|
|
+ NSNumber *number = [self selectedSessionNumber];
|
|
+ if (number) {
|
|
+ [delegate_ attachToSessionWithNumber:number.intValue];
|
|
}
|
|
}
|
|
|
|
- (IBAction)detach:(id)sender {
|
|
- NSString *name = [self selectedSessionName];
|
|
- if (name) {
|
|
+ NSNumber *number = [self selectedSessionNumber];
|
|
+ if (number) {
|
|
[delegate_ detach];
|
|
}
|
|
}
|
|
|
|
#pragma mark NSTableViewDataSource
|
|
|
|
-- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
|
|
-{
|
|
- return model_.count;
|
|
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView {
|
|
+ return _model.count;
|
|
}
|
|
|
|
- (id)tableView:(NSTableView *)aTableView
|
|
objectValueForTableColumn:(NSTableColumn *)aTableColumn
|
|
- row:(NSInteger)rowIndex
|
|
-{
|
|
- NSString *name = [model_ objectAtIndex:rowIndex];
|
|
+ row:(NSInteger)rowIndex {
|
|
+ iTermTmuxSessionObject *sessionObject = _model[rowIndex];
|
|
if (aTableColumn == checkColumn_) {
|
|
- if ([[delegate_ nameOfAttachedSession] isEqualToString:name]) {
|
|
+ if ([[delegate_ numberOfAttachedSession] isEqual:@(sessionObject.number)]) {
|
|
return @"✓";
|
|
} else {
|
|
return @"";
|
|
}
|
|
} else {
|
|
- if (rowIndex < model_.count) {
|
|
- return name;
|
|
+ if (rowIndex < _model.count) {
|
|
+ return sessionObject.name;
|
|
} else {
|
|
return nil;
|
|
}
|
|
@@ -124,10 +125,9 @@ extern NSString *kWindowPasteboardType;
|
|
- (void)tableView:(NSTableView *)aTableView
|
|
setObjectValue:(id)anObject
|
|
forTableColumn:(NSTableColumn *)aTableColumn
|
|
- row:(NSInteger)rowIndex
|
|
-{
|
|
- [delegate_ renameSessionWithName:[model_ objectAtIndex:rowIndex]
|
|
- toName:(NSString *)anObject];
|
|
+ row:(NSInteger)rowIndex {
|
|
+ [delegate_ renameSessionWithNumber:_model[rowIndex].number
|
|
+ toName:(NSString *)anObject];
|
|
}
|
|
|
|
#pragma mark NSTableViewDataSource
|
|
@@ -138,16 +138,15 @@ extern NSString *kWindowPasteboardType;
|
|
return YES;
|
|
}
|
|
|
|
-- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
|
|
-{
|
|
+- (void)tableViewSelectionDidChange:(NSNotification *)aNotification {
|
|
[self updateEnabledStateOfButtons];
|
|
- [delegate_ selectedSessionChangedTo:[self selectedSessionName]];
|
|
+ [delegate_ selectedSessionDidChange];
|
|
}
|
|
|
|
-- (NSString *)selectedSessionName {
|
|
+- (NSNumber *)selectedSessionNumber {
|
|
int i = [tableView_ selectedRow];
|
|
- if (i >= 0 && i < model_.count) {
|
|
- return [model_ objectAtIndex:i];
|
|
+ if (i >= 0 && i < _model.count) {
|
|
+ return @(_model[i].number);
|
|
} else {
|
|
return nil;
|
|
}
|
|
@@ -159,19 +158,19 @@ extern NSString *kWindowPasteboardType;
|
|
dropOperation:(NSTableViewDropOperation)operation {
|
|
NSPasteboard *pb = [info draggingPasteboard];
|
|
NSArray* pair = [pb propertyListForType:kWindowPasteboardType];
|
|
- NSString *sessionName = [pair objectAtIndex:0];
|
|
- NSArray *draggedItems = [pair objectAtIndex:1];
|
|
- NSString *targetSession = [model_ objectAtIndex:row];
|
|
+ NSNumber *sessionNumber = pair[0];
|
|
+ NSArray *draggedItems = pair[1];
|
|
+ iTermTmuxSessionObject *targetSessionObject = _model[row];
|
|
for (NSArray *tuple in draggedItems) {
|
|
NSNumber *windowId = [tuple objectAtIndex:1];
|
|
if (info.draggingSourceOperationMask & NSDragOperationLink) {
|
|
[delegate_ linkWindowId:[windowId intValue]
|
|
- inSession:sessionName
|
|
- toSession:targetSession];
|
|
+ inSessionNumber:sessionNumber.intValue
|
|
+ toSessionNumber:targetSessionObject.number];
|
|
} else {
|
|
[delegate_ moveWindowId:[windowId intValue]
|
|
- inSession:sessionName
|
|
- toSession:targetSession];
|
|
+ inSessionNumber:sessionNumber.intValue
|
|
+ toSessionNumber:targetSessionObject.number];
|
|
}
|
|
}
|
|
return YES;
|
|
@@ -205,11 +204,16 @@ extern NSString *kWindowPasteboardType;
|
|
}
|
|
}
|
|
|
|
-- (NSString *)nameForNewSession
|
|
-{
|
|
+- (BOOL)haveSessionWithName:(NSString *)name {
|
|
+ return [_model anyWithBlock:^BOOL(iTermTmuxSessionObject *anObject) {
|
|
+ return [anObject.name isEqualToString:name];
|
|
+ }];
|
|
+}
|
|
+
|
|
+- (NSString *)nameForNewSession {
|
|
int n = 0;
|
|
NSString *candidate = [self nameForNewSessionWithNumber:n];
|
|
- while ([model_ indexOfObject:candidate] != NSNotFound) {
|
|
+ while ([self haveSessionWithName:candidate]) {
|
|
n++;
|
|
candidate = [self nameForNewSessionWithNumber:n];
|
|
}
|
|
@@ -223,7 +227,9 @@ extern NSString *kWindowPasteboardType;
|
|
[detachButton_ setEnabled:NO];
|
|
[removeButton_ setEnabled:NO];
|
|
} else {
|
|
- BOOL isAttachedSession = [[delegate_ nameOfAttachedSession] isEqualToString:[self selectedSessionName]];
|
|
+ NSNumber *selected = [self selectedSessionNumber];
|
|
+ BOOL isAttachedSession = (selected != nil &&
|
|
+ [[delegate_ numberOfAttachedSession] isEqual:@(selected.intValue)]);
|
|
[attachButton_ setEnabled:!isAttachedSession];
|
|
[detachButton_ setEnabled:isAttachedSession];
|
|
[removeButton_ setEnabled:YES];
|
|
diff --git sources/TmuxWindowsTable.h sources/TmuxWindowsTable.h
|
|
index 7c3e16c..6e9604e 100644
|
|
--- sources/TmuxWindowsTable.h
|
|
+++ sources/TmuxWindowsTable.h
|
|
@@ -22,7 +22,7 @@ extern NSString *kWindowPasteboardType;
|
|
- (BOOL)haveSelectedSession;
|
|
- (BOOL)currentSessionSelected;
|
|
- (BOOL)haveOpenWindowWithId:(int)windowId;
|
|
-- (NSString *)selectedSessionName;
|
|
+- (NSNumber *)selectedSessionNumber;
|
|
- (void)tmuxWindowsTableDidSelectWindowWithId:(int)windowId;
|
|
|
|
@end
|
|
diff --git sources/TmuxWindowsTable.m sources/TmuxWindowsTable.m
|
|
index 56f4954..8f92993 100644
|
|
--- sources/TmuxWindowsTable.m
|
|
+++ sources/TmuxWindowsTable.m
|
|
@@ -173,9 +173,9 @@ NSString *kWindowPasteboardType = @"kWindowPasteboardType";
|
|
- (void)tableView:(NSTableView *)aTableView
|
|
setObjectValue:(id)anObject
|
|
forTableColumn:(NSTableColumn *)aTableColumn
|
|
- row:(NSInteger)rowIndex
|
|
-{
|
|
- [delegate_ renameWindowWithId:[[[[self filteredModel] objectAtIndex:rowIndex] objectAtIndex:1] intValue]
|
|
+ row:(NSInteger)rowIndex {
|
|
+ const int windowID = [[[[self filteredModel] objectAtIndex:rowIndex] objectAtIndex:1] intValue];
|
|
+ [delegate_ renameWindowWithId:windowID
|
|
toName:anObject];
|
|
}
|
|
|
|
@@ -200,7 +200,7 @@ NSString *kWindowPasteboardType = @"kWindowPasteboardType";
|
|
NSArray* selectedItems = [[self filteredModel] objectsAtIndexes:rowIndexes];
|
|
[pboard declareTypes:[NSArray arrayWithObject:kWindowPasteboardType] owner:self];
|
|
[pboard setPropertyList:[NSArray arrayWithObjects:
|
|
- [delegate_ selectedSessionName],
|
|
+ [delegate_ selectedSessionNumber],
|
|
selectedItems,
|
|
nil]
|
|
forType:kWindowPasteboardType];
|
|
@@ -219,14 +219,13 @@ NSString *kWindowPasteboardType = @"kWindowPasteboardType";
|
|
|
|
#pragma mark - Private
|
|
|
|
-- (NSArray *)selectedWindowIdsAsStrings
|
|
-{
|
|
- NSArray *ids = [self selectedWindowIds];
|
|
+- (NSArray *)selectedWindowIdsAsStrings {
|
|
+ NSArray *ids = [self selectedWindowIds];
|
|
NSMutableArray *result = [NSMutableArray array];
|
|
- for (NSString *n in ids) {
|
|
- [result addObject:n];
|
|
- }
|
|
- return result;
|
|
+ for (NSString *n in ids) {
|
|
+ [result addObject:n];
|
|
+ }
|
|
+ return result;
|
|
}
|
|
|
|
- (NSArray *)selectedWindowIds
|
|
diff --git sources/iTermInitialDirectory+Tmux.h sources/iTermInitialDirectory+Tmux.h
|
|
index a470a56..586ade6 100644
|
|
--- sources/iTermInitialDirectory+Tmux.h
|
|
+++ sources/iTermInitialDirectory+Tmux.h
|
|
@@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
|
@interface iTermInitialDirectory (Tmux)
|
|
|
|
-- (void)tmuxNewWindowCommandInSession:(nullable NSString *)session
|
|
+- (void)tmuxNewWindowCommandInSessionNumber:(nullable NSNumber *)sessionNumber
|
|
recyclingSupported:(BOOL)recyclingSupported
|
|
scope:(iTermVariableScope *)scope
|
|
completion:(void (^)(NSString *))completion;
|
|
diff --git sources/iTermInitialDirectory+Tmux.m sources/iTermInitialDirectory+Tmux.m
|
|
index a67bea4..3259eda 100644
|
|
--- sources/iTermInitialDirectory+Tmux.m
|
|
+++ sources/iTermInitialDirectory+Tmux.m
|
|
@@ -11,14 +11,14 @@
|
|
|
|
@implementation iTermInitialDirectory(Tmux)
|
|
|
|
-- (void)tmuxNewWindowCommandInSession:(NSString *)session
|
|
- recyclingSupported:(BOOL)recyclingSupported
|
|
- scope:(iTermVariableScope *)scope
|
|
- completion:(void (^)(NSString *))completion {
|
|
+- (void)tmuxNewWindowCommandInSessionNumber:(nullable NSNumber *)sessionNumber
|
|
+ recyclingSupported:(BOOL)recyclingSupported
|
|
+ scope:(iTermVariableScope *)scope
|
|
+ completion:(void (^)(NSString *))completion {
|
|
NSArray *args = @[ @"new-window", @"-PF '#{window_id}'" ];
|
|
|
|
- if (session) {
|
|
- NSString *targetSessionArg = [NSString stringWithFormat:@"\"%@:+\"", [session stringByEscapingQuotes]];
|
|
+ if (sessionNumber) {
|
|
+ NSString *targetSessionArg = [NSString stringWithFormat:@"\"$%d:+\"", sessionNumber.intValue];
|
|
NSArray *insertionArguments = @[ @"-a",
|
|
@"-t",
|
|
targetSessionArg ];
|
|
@@ -33,10 +33,10 @@
|
|
- (void)tmuxNewWindowCommandRecyclingSupported:(BOOL)recyclingSupported
|
|
scope:(iTermVariableScope *)scope
|
|
completion:(void (^)(NSString *))completion {
|
|
- [self tmuxNewWindowCommandInSession:nil
|
|
- recyclingSupported:recyclingSupported
|
|
- scope:scope
|
|
- completion:completion];
|
|
+ [self tmuxNewWindowCommandInSessionNumber:nil
|
|
+ recyclingSupported:recyclingSupported
|
|
+ scope:scope
|
|
+ completion:completion];
|
|
}
|
|
|
|
- (void)tmuxSplitWindowCommand:(int)wp
|
|
diff --git sources/iTermTmuxOptionMonitor.h sources/iTermTmuxOptionMonitor.h
|
|
index 147fcd6..8dffe52 100644
|
|
--- sources/iTermTmuxOptionMonitor.h
|
|
+++ sources/iTermTmuxOptionMonitor.h
|
|
@@ -18,8 +18,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|
@property (nullable, nonatomic, strong) iTermVariableScope *scope;
|
|
|
|
- (instancetype)init NS_UNAVAILABLE;
|
|
+
|
|
+// If `fallbackVariableName` is nonnil, the value of the variable named
|
|
+// `fallbackVariableName` will be used for tmux 2.8 and earlier.
|
|
- (instancetype)initWithGateway:(TmuxGateway *)gateway
|
|
scope:(iTermVariableScope *)scope
|
|
+ fallbackVariableName:(nullable NSString *)fallbackVariableName
|
|
format:(NSString *)format
|
|
target:(NSString *)tmuxTarget
|
|
variableName:(nullable NSString *)variableName
|
|
diff --git sources/iTermTmuxOptionMonitor.m sources/iTermTmuxOptionMonitor.m
|
|
index 4f72d02..fd6120f 100644
|
|
--- sources/iTermTmuxOptionMonitor.m
|
|
+++ sources/iTermTmuxOptionMonitor.m
|
|
@@ -10,6 +10,7 @@
|
|
#import "DebugLogging.h"
|
|
#import "iTermVariables.h"
|
|
#import "iTermVariableScope+Session.h"
|
|
+#import "NSStringITerm.h"
|
|
#import "NSTimer+iTerm.h"
|
|
#import "TmuxGateway.h"
|
|
|
|
@@ -19,11 +20,13 @@
|
|
BOOL _haveOutstandingRequest;
|
|
NSString *_target;
|
|
NSString *_variableName;
|
|
+ NSString *_fallbackVariableName;
|
|
void (^_block)(NSString *);
|
|
}
|
|
|
|
- (instancetype)initWithGateway:(TmuxGateway *)gateway
|
|
scope:(iTermVariableScope *)scope
|
|
+ fallbackVariableName:(NSString *)fallbackVariableName
|
|
format:(NSString *)format
|
|
target:(NSString *)target
|
|
variableName:(NSString *)variableName
|
|
@@ -36,6 +39,7 @@
|
|
_target = [target copy];
|
|
_variableName = [variableName copy];
|
|
_block = [block copy];
|
|
+ _fallbackVariableName = [fallbackVariableName copy];
|
|
}
|
|
return self;
|
|
}
|
|
@@ -64,13 +68,21 @@
|
|
[self updateOnce];
|
|
}
|
|
|
|
+- (NSString *)command {
|
|
+ return [NSString stringWithFormat:@"display-message -t '%@' -p '%@'", _target, self.escapedFormat];
|
|
+}
|
|
+
|
|
- (void)updateOnce {
|
|
if (_haveOutstandingRequest) {
|
|
DLog(@"Not making a request because one is outstanding");
|
|
return;
|
|
}
|
|
+ if (_fallbackVariableName && self.gateway.minimumServerVersion.doubleValue <= 2.9) {
|
|
+ [self didFetch:[self.scope valueForVariableName:_fallbackVariableName]];
|
|
+ return;
|
|
+ }
|
|
_haveOutstandingRequest = YES;
|
|
- NSString *command = [NSString stringWithFormat:@"display-message -t '%@' -p '%@'", _target, self.escapedFormat];
|
|
+ NSString *command = [self command];
|
|
DLog(@"Request option with command %@", command);
|
|
[self.gateway sendCommand:command
|
|
responseTarget:self
|
|
@@ -80,7 +92,7 @@
|
|
}
|
|
|
|
- (void)didFetch:(NSString *)value {
|
|
- DLog(@"Did fetch %@", value);
|
|
+ DLog(@"%@ -> %@", self.command, value);
|
|
if (!value) {
|
|
// Probably the pane went away and we'll be dealloced soon.
|
|
return;
|
|
diff --git sources/iTermTmuxSessionObject.h sources/iTermTmuxSessionObject.h
|
|
new file mode 100644
|
|
index 0000000..6407d5f
|
|
--- /dev/null
|
|
+++ sources/iTermTmuxSessionObject.h
|
|
@@ -0,0 +1,17 @@
|
|
+//
|
|
+// iTermTmuxSessionObject.h
|
|
+// iTerm2SharedARC
|
|
+//
|
|
+// Created by George Nachman on 9/25/19.
|
|
+//
|
|
+
|
|
+#import <Foundation/Foundation.h>
|
|
+
|
|
+NS_ASSUME_NONNULL_BEGIN
|
|
+
|
|
+@interface iTermTmuxSessionObject : NSObject
|
|
+@property (nonatomic, copy) NSString *name;
|
|
+@property (nonatomic) int number;
|
|
+@end
|
|
+
|
|
+NS_ASSUME_NONNULL_END
|
|
diff --git sources/iTermTmuxSessionObject.m sources/iTermTmuxSessionObject.m
|
|
new file mode 100644
|
|
index 0000000..5ac9130
|
|
--- /dev/null
|
|
+++ sources/iTermTmuxSessionObject.m
|
|
@@ -0,0 +1,12 @@
|
|
+//
|
|
+// iTermTmuxSessionObject.m
|
|
+// iTerm2SharedARC
|
|
+//
|
|
+// Created by George Nachman on 9/25/19.
|
|
+//
|
|
+
|
|
+#import "iTermTmuxSessionObject.h"
|
|
+
|
|
+@implementation iTermTmuxSessionObject
|
|
+
|
|
+@end
|
|
diff --git sources/iTermTmuxStatusBarMonitor.h sources/iTermTmuxStatusBarMonitor.h
|
|
index 8172779..96338ec 100644
|
|
--- sources/iTermTmuxStatusBarMonitor.h
|
|
+++ sources/iTermTmuxStatusBarMonitor.h
|
|
@@ -14,6 +14,7 @@
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
+// NOTE: Requires tmux 2.9
|
|
@interface iTermTmuxStatusBarMonitor : NSObject
|
|
|
|
@property (nonatomic) BOOL active;
|
|
diff --git sources/iTermTmuxStatusBarMonitor.m sources/iTermTmuxStatusBarMonitor.m
|
|
index 50504da..0d3daf3 100644
|
|
--- sources/iTermTmuxStatusBarMonitor.m
|
|
+++ sources/iTermTmuxStatusBarMonitor.m
|
|
@@ -9,6 +9,7 @@
|
|
|
|
#import "DebugLogging.h"
|
|
#import "iTermVariableScope.h"
|
|
+#import "NSStringITerm.h"
|
|
#import "NSTimer+iTerm.h"
|
|
#import "TmuxGateway.h"
|
|
#import "RegexKitLite.h"
|
|
@@ -49,13 +50,8 @@
|
|
|
|
- (void)requestUpdates {
|
|
_accelerated = NO;
|
|
- [_gateway sendCommand:@"display-message -p \"#{status-left}\"" responseTarget:self responseSelector:@selector(handleStatusLeftResponse:)];
|
|
- [_gateway sendCommand:@"display-message -p \"#{status-right}\"" responseTarget:self responseSelector:@selector(handleStatusRightResponse:)];
|
|
-}
|
|
-
|
|
-- (NSString *)escapedString:(NSString *)string {
|
|
- return [[string stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]
|
|
- stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
|
|
+ [_gateway sendCommand:@"display-message -p \"#{T:status-left}\"" responseTarget:self responseSelector:@selector(handleStatusLeftValueExpansionResponse:)];
|
|
+ [_gateway sendCommand:@"display-message -p \"#{T:status-right}\"" responseTarget:self responseSelector:@selector(handleStatusRightValueExpansionResponse:)];
|
|
}
|
|
|
|
- (void)handleStatusIntervalResponse:(NSString *)response {
|
|
@@ -66,28 +62,14 @@
|
|
_timer = [NSTimer scheduledWeakTimerWithTimeInterval:interval target:self selector:@selector(timerDidFire:) userInfo:nil repeats:YES];
|
|
}
|
|
|
|
-- (void)handleStatusLeftResponse:(NSString *)response {
|
|
- if (!response) {
|
|
- return;
|
|
- }
|
|
- NSString *command = [NSString stringWithFormat:@"display-message -p \"%@\"", [self escapedString:response]];
|
|
- [_gateway sendCommand:command responseTarget:self responseSelector:@selector(handleStatusLeftValueExpansionResponse:)];
|
|
-}
|
|
-
|
|
-- (void)handleStatusRightResponse:(NSString *)response {
|
|
- if (!response) {
|
|
- return;
|
|
- }
|
|
- NSString *command = [NSString stringWithFormat:@"display-message -p \"%@\"", [self escapedString:response]];
|
|
- [_gateway sendCommand:command responseTarget:self responseSelector:@selector(handleStatusRightValueExpansionResponse:)];
|
|
-}
|
|
-
|
|
- (void)handleStatusLeftValueExpansionResponse:(NSString *)string {
|
|
+ DLog(@"Left status bar is: %@", string);
|
|
[self.scope setValue:[self sanitizedString:string] ?: @"" forVariableNamed:iTermVariableKeySessionTmuxStatusLeft];
|
|
[self accelerateUpdateIfStringContainsNotReady:string];
|
|
}
|
|
|
|
- (void)handleStatusRightValueExpansionResponse:(NSString *)string {
|
|
+ DLog(@"Right status bar is: %@", string);
|
|
[self.scope setValue:[self sanitizedString:string] ?: @"" forVariableNamed:iTermVariableKeySessionTmuxStatusRight];
|
|
[self accelerateUpdateIfStringContainsNotReady:string];
|
|
}
|
|
diff --git sources/iTermWorkingDirectoryPoller.m sources/iTermWorkingDirectoryPoller.m
|
|
index 5271227..f4d8602 100644
|
|
--- sources/iTermWorkingDirectoryPoller.m
|
|
+++ sources/iTermWorkingDirectoryPoller.m
|
|
@@ -37,6 +37,7 @@
|
|
__weak __typeof(self) weakSelf = self;
|
|
_tmuxOptionMonitor = [[iTermTmuxOptionMonitor alloc] initWithGateway:gateway
|
|
scope:scope
|
|
+ fallbackVariableName:nil
|
|
format:@"#{pane_current_path}"
|
|
target:[NSString stringWithFormat:@"%%%@", @(windowPane)]
|
|
variableName:nil
|
|
--
|
|
2.23.0
|
|
|