Skip to content

Commit 68c0abe

Browse files
committed
Whole lotta stuff
1 parent f98841f commit 68c0abe

11 files changed

+434
-118
lines changed

SSH.sln

+1-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 14
4-
VisualStudioVersion = 14.0.25123.0
4+
VisualStudioVersion = 14.0.25420.1
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ssh", "SSH\Ssh.csproj", "{CC7A5CAF-0A37-4205-8945-23A453CBE7C7}"
77
EndProject
@@ -13,8 +13,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerminalTest", "TerminalTes
1313
EndProject
1414
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsshnet", "libsshnet\libsshnet.vcxproj", "{A61A5B82-B404-45A1-B087-E7C4A1FB464D}"
1515
EndProject
16-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet", "Ssh.Net\Renci.SshNet\Renci.SshNet.csproj", "{2F5F8C90-0BD1-424F-997C-7BC6280919D1}"
17-
EndProject
1816
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerminalControlTests", "TerminalControlTests\TerminalControlTests.csproj", "{224BDE3E-614A-4DA4-8DAC-3460F6BA781E}"
1917
EndProject
2018
Global
@@ -81,18 +79,6 @@ Global
8179
{A61A5B82-B404-45A1-B087-E7C4A1FB464D}.Release|Any CPU.ActiveCfg = Release|x64
8280
{A61A5B82-B404-45A1-B087-E7C4A1FB464D}.Release|x64.ActiveCfg = Release|x64
8381
{A61A5B82-B404-45A1-B087-E7C4A1FB464D}.Release|x86.ActiveCfg = Release|Win32
84-
{2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
85-
{2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
86-
{2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.ActiveCfg = Release|Any CPU
87-
{2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.Build.0 = Release|Any CPU
88-
{2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.ActiveCfg = Release|Any CPU
89-
{2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.Build.0 = Release|Any CPU
90-
{2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
91-
{2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.Build.0 = Release|Any CPU
92-
{2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.ActiveCfg = Release|Any CPU
93-
{2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.Build.0 = Release|Any CPU
94-
{2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.ActiveCfg = Release|Any CPU
95-
{2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.Build.0 = Release|Any CPU
9682
{224BDE3E-614A-4DA4-8DAC-3460F6BA781E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
9783
{224BDE3E-614A-4DA4-8DAC-3460F6BA781E}.Debug|Any CPU.Build.0 = Debug|Any CPU
9884
{224BDE3E-614A-4DA4-8DAC-3460F6BA781E}.Debug|x64.ActiveCfg = Debug|Any CPU

SSH/MainWindow.xaml.cs

+13-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ namespace npcook.Ssh
2929
public partial class MainWindow : Window
3030
{
3131
MainWindowViewModel viewModel;
32+
IWindowStreamNotifier notifier;
3233

3334
bool initialSized = false;
3435
public MainWindow()
@@ -53,13 +54,23 @@ private void ViewModel_Error(object sender, ErrorEventArgs e)
5354

5455
public void Connect(ShellStream stream, ConnectionSettings settings)
5556
{
56-
var notifier = new ShellStreamNotifier(terminalControl, stream);
57-
viewModel.Connect(notifier, settings);
57+
notifier?.Stop();
58+
notifier = new ShellStreamNotifier(terminalControl, stream);
59+
//notifier = new LoadingShellStreamNotifier(terminalControl, File.OpenRead("input.log"));
60+
//notifier = new SavingShellStreamNotifier(terminalControl, stream, File.OpenWrite("input.log"));
61+
viewModel.Connect(notifier, stream, settings);
5862
notifier.Start();
5963

6064
terminalControl.Terminal = viewModel.Terminal;
6165
}
6266

67+
protected override void OnClosed(EventArgs e)
68+
{
69+
base.OnClosed(e);
70+
71+
notifier?.Stop();
72+
}
73+
6374
private void this_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
6475
{
6576
IsVisibleChanged -= this_IsVisibleChanged;

SSH/MainWindowViewModel.cs

+162-19
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
using npcook.Terminal.Controls;
33
using Renci.SshNet;
44
using System;
5+
using System.Collections.Concurrent;
56
using System.Collections.Generic;
67
using System.ComponentModel;
78
using System.IO;
89
using System.Linq;
910
using System.Runtime.CompilerServices;
1011
using System.Text;
12+
using System.Threading;
1113
using System.Threading.Tasks;
1214
using System.Windows;
1315
using System.Windows.Input;
@@ -26,6 +28,12 @@ public ErrorEventArgs(string message)
2628
}
2729
}
2830

31+
interface IWindowStreamNotifier : IStreamNotifier
32+
{
33+
void Start();
34+
void Stop();
35+
}
36+
2937
#if USE_LIBSSHNET
3038
class LibSshNetStreamNotifier : IStreamNotifier
3139
{
@@ -52,33 +60,174 @@ private void Stream_DataReceived(object sender, EventArgs e)
5260
}
5361
}
5462
#else
55-
class ShellStreamNotifier : IStreamNotifier
63+
class ShellStreamNotifier : IWindowStreamNotifier
5664
{
57-
TerminalControl terminal;
65+
readonly TerminalControl terminal;
66+
readonly ShellStream stream;
67+
readonly SemaphoreSlim received = new SemaphoreSlim(0);
68+
bool stopped;
5869

5970
public Stream Stream
60-
{ get; }
71+
{ get { return stream; } }
6172

62-
public event EventHandler DataAvailable;
73+
public event EventHandler<DataAvailableEventArgs> DataAvailable;
6374

6475
public ShellStreamNotifier(TerminalControl terminal, ShellStream stream)
6576
{
6677
this.terminal = terminal;
67-
Stream = stream;
78+
this.stream = stream;
79+
}
80+
81+
public void Start()
82+
{
83+
stopped = false;
6884
stream.DataReceived += Stream_DataReceived;
85+
Task.Run(() =>
86+
{
87+
while (true)
88+
{
89+
received.Wait();
90+
if (stopped)
91+
break;
92+
93+
while (stream.DataAvailable)
94+
{
95+
terminal.BeginChange();
96+
if (DataAvailable != null)
97+
{
98+
bool done = false;
99+
Task.Run(() =>
100+
{
101+
while (!done)
102+
{
103+
Thread.Sleep(50);
104+
if (done)
105+
break;
106+
terminal.CycleChange();
107+
}
108+
});
109+
DataAvailable(this, new DataAvailableEventArgs(0));
110+
done = true;
111+
}
112+
else
113+
throw new InvalidOperationException("Data was received but no one was listening.");
114+
terminal.EndChange();
115+
}
116+
}
117+
});
118+
received.Release();
119+
}
120+
121+
public void Stop()
122+
{
123+
stopped = true;
124+
stream.Close();
125+
received.Release();
126+
}
127+
128+
private void Stream_DataReceived(object sender, Renci.SshNet.Common.ShellDataEventArgs e)
129+
{
130+
received.Release();
131+
}
132+
}
133+
134+
class SavingShellStreamNotifier : IWindowStreamNotifier
135+
{
136+
readonly TerminalControl terminal;
137+
readonly ShellStream input;
138+
readonly Stream output;
139+
readonly ProxyStream middle;
140+
readonly ConcurrentQueue<byte[]> outputQueue = new ConcurrentQueue<byte[]>();
141+
readonly EventWaitHandle outputWait = new EventWaitHandle(false, EventResetMode.AutoReset);
142+
readonly Task outputTask;
143+
144+
public Stream Stream
145+
{ get { return middle; } }
146+
147+
public event EventHandler<DataAvailableEventArgs> DataAvailable;
148+
149+
public SavingShellStreamNotifier(TerminalControl terminal, ShellStream stream, Stream output)
150+
{
151+
this.terminal = terminal;
152+
input = stream;
153+
this.output = output;
154+
middle = new ProxyStream(input);
155+
stream.DataReceived += Stream_DataReceived;
156+
outputTask = Task.Run(() =>
157+
{
158+
do
159+
{
160+
outputWait.WaitOne();
161+
if (outputQueue.IsEmpty)
162+
break;
163+
164+
byte[] result;
165+
while (!outputQueue.TryDequeue(out result)) ;
166+
167+
output.Write(result, 0, result.Length);
168+
} while (true);
169+
});
69170
}
70171

71172
public void Start()
72173
{
73-
if ((Stream as ShellStream).DataAvailable && DataAvailable != null)
174+
if (input.DataAvailable && DataAvailable != null)
74175
Stream_DataReceived(this, null);
75176
}
76177

178+
public void Stop()
179+
{
180+
output.Close();
181+
}
182+
77183
private void Stream_DataReceived(object sender, Renci.SshNet.Common.ShellDataEventArgs e)
78184
{
79185
terminal.BeginChange();
80186
if (DataAvailable != null)
81-
DataAvailable(this, EventArgs.Empty);
187+
{
188+
DataAvailable(this, new DataAvailableEventArgs(e.Data.Length));
189+
outputQueue.Enqueue(middle.GetCopy());
190+
outputWait.Set();
191+
}
192+
else
193+
throw new InvalidOperationException("Data was received but no one was listening.");
194+
terminal.EndChange();
195+
}
196+
}
197+
198+
class LoadingShellStreamNotifier : IWindowStreamNotifier
199+
{
200+
TerminalControl terminal;
201+
202+
public Stream Stream
203+
{ get; }
204+
205+
public event EventHandler<DataAvailableEventArgs> DataAvailable;
206+
207+
public LoadingShellStreamNotifier(TerminalControl terminal, Stream input)
208+
{
209+
this.terminal = terminal;
210+
Stream = new ProxyStream(input, false, true);
211+
}
212+
213+
public void Start()
214+
{
215+
if (DataAvailable != null)
216+
{
217+
Stream_DataReceived(this, null);
218+
}
219+
}
220+
221+
public void Stop()
222+
{
223+
Stream.Close();
224+
}
225+
226+
private void Stream_DataReceived(object sender, Renci.SshNet.Common.ShellDataEventArgs e)
227+
{
228+
terminal.BeginChange();
229+
if (DataAvailable != null)
230+
DataAvailable(this, new DataAvailableEventArgs(e.Data.Length));
82231
else
83232
throw new InvalidOperationException("Data was received but no one was listening.");
84233
terminal.EndChange();
@@ -101,9 +250,7 @@ protected void notifyPropertyChanged([CallerMemberName] string memberName = null
101250
{
102251
App.Current.Dispatcher.BeginInvoke(new Action(() =>
103252
{
104-
var handler = PropertyChanged;
105-
if (handler != null)
106-
handler(this, new PropertyChangedEventArgs(memberName));
253+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(memberName));
107254
}));
108255
}
109256

@@ -181,9 +328,7 @@ async void onReopenSession(object _)
181328
}
182329
catch (ConnectException ex)
183330
{
184-
var handler = Error;
185-
if (handler != null)
186-
handler(this, new ErrorEventArgs(ex.Message));
331+
Error?.Invoke(this, new ErrorEventArgs(ex.Message));
187332
}
188333
}
189334

@@ -202,10 +347,10 @@ void onOptions(object _)
202347

203348
}
204349

205-
public void Connect(IStreamNotifier notifier, ConnectionSettings settings)
350+
public void Connect(IStreamNotifier notifier, ShellStream stream, ConnectionSettings settings)
206351
{
207352
this.settings = settings;
208-
stream = notifier.Stream as ShellStream;
353+
this.stream = stream;
209354
var terminal = new XtermTerminal(notifier)
210355
{
211356
Size = new Terminal.Point(App.DefaultTerminalCols, App.DefaultTerminalRows),
@@ -236,17 +381,15 @@ private void Terminal_StreamException(object sender, StreamExceptionEventArgs e)
236381
else
237382
throw new Exception("An unidentified error occurred.", e.Exception);
238383

239-
var handler = Error;
240-
if (handler != null)
241-
handler(this, new ErrorEventArgs(message));
384+
Error?.Invoke(this, new ErrorEventArgs(message));
242385
}
243386

244387
public void ChangeSize(int cols, int rows)
245388
{
246389
if (cols != terminal.Size.Col || rows != terminal.Size.Row)
247390
{
248391
terminal.Size = new Terminal.Point(cols, rows);
249-
stream.Channel.SendWindowChangeRequest((uint) cols, (uint) rows, 0, 0);
392+
//stream.Channel.SendWindowChangeRequest((uint) cols, (uint) rows, 0, 0);
250393
}
251394
}
252395
}

0 commit comments

Comments
 (0)