Browse Source

Add traffic chart and traffic icon support (#595)

* Implement the line chart on Log View window for inbound/outbound traffic per second

* Implement dynamic tray icon to indicate Inbound and Outbound

* Refine linechart

* Bugfix for dynamic icon

* Refine chart presentation

* Change the data points' type for more accurate calculation

* Tweak inbound/outbound indicator image for tray icon
tags/3.2
Allen Zhu Syrone Wong 8 years ago
parent
commit
b0f71c43f5
11 changed files with 308 additions and 24 deletions
  1. +59
    -1
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  2. +21
    -1
      shadowsocks-csharp/Properties/Resources.Designer.cs
  3. +6
    -0
      shadowsocks-csharp/Properties/Resources.resx
  4. BIN
      shadowsocks-csharp/Resources/ssIn24.png
  5. BIN
      shadowsocks-csharp/Resources/ssOut24.png
  6. +21
    -1
      shadowsocks-csharp/Util/Util.cs
  7. +89
    -14
      shadowsocks-csharp/View/LogForm.Designer.cs
  8. +52
    -1
      shadowsocks-csharp/View/LogForm.cs
  9. +3
    -0
      shadowsocks-csharp/View/LogForm.resx
  10. +55
    -6
      shadowsocks-csharp/View/MenuViewController.cs
  11. +2
    -0
      shadowsocks-csharp/shadowsocks-csharp.csproj

+ 59
- 1
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -23,6 +23,7 @@ namespace Shadowsocks.Controller
// interacts with low level logic // interacts with low level logic
private Thread _ramThread; private Thread _ramThread;
private Thread _trafficThread;
private Listener _listener; private Listener _listener;
private PACServer _pacServer; private PACServer _pacServer;
@@ -35,6 +36,7 @@ namespace Shadowsocks.Controller
public long inboundCounter = 0; public long inboundCounter = 0;
public long outboundCounter = 0; public long outboundCounter = 0;
public QueueLast<TrafficPerSecond> traffic;
private bool stopped = false; private bool stopped = false;
@@ -45,10 +47,29 @@ namespace Shadowsocks.Controller
public string Path; public string Path;
} }
public class QueueLast<T> : Queue<T>
{
public T Last { get; private set; }
public new void Enqueue(T item)
{
Last = item;
base.Enqueue(item);
}
}
public class TrafficPerSecond
{
public long inboundCounter;
public long outboundCounter;
public long inboundIncreasement;
public long outboundIncreasement;
}
public event EventHandler ConfigChanged; public event EventHandler ConfigChanged;
public event EventHandler EnableStatusChanged; public event EventHandler EnableStatusChanged;
public event EventHandler EnableGlobalChanged; public event EventHandler EnableGlobalChanged;
public event EventHandler ShareOverLANStatusChanged; public event EventHandler ShareOverLANStatusChanged;
public event EventHandler TrafficChanged;
// when user clicked Edit PAC, and PAC file has already created // when user clicked Edit PAC, and PAC file has already created
public event EventHandler<PathEventArgs> PACFileReadyToOpen; public event EventHandler<PathEventArgs> PACFileReadyToOpen;
@@ -66,6 +87,7 @@ namespace Shadowsocks.Controller
StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); StatisticsConfiguration = StatisticsStrategyConfiguration.Load();
_strategyManager = new StrategyManager(this); _strategyManager = new StrategyManager(this);
StartReleasingMemory(); StartReleasingMemory();
StartTrafficStatistics(60);
} }
public void Start() public void Start()
@@ -313,7 +335,7 @@ namespace Shadowsocks.Controller
{ {
if (_config.availabilityStatistics) if (_config.availabilityStatistics)
{ {
new Task(() => availabilityStatistics.UpdateLatency(server, (int) latency.TotalMilliseconds)).Start();
new Task(() => availabilityStatistics.UpdateLatency(server, (int)latency.TotalMilliseconds)).Start();
} }
} }
@@ -516,5 +538,41 @@ namespace Shadowsocks.Controller
} }
} }
private void StartTrafficStatistics(int queueMaxSize)
{
traffic = new QueueLast<TrafficPerSecond>();
for (int i = 0; i < queueMaxSize; i++)
{
traffic.Enqueue(new TrafficPerSecond());
}
_trafficThread = new Thread(new ThreadStart(() => TrafficStatistics(queueMaxSize)));
_trafficThread.IsBackground = true;
_trafficThread.Start();
}
private void TrafficStatistics(int queueMaxSize)
{
while (true)
{
TrafficPerSecond previous = traffic.Last;
TrafficPerSecond current = new TrafficPerSecond();
current.inboundCounter = inboundCounter;
current.outboundCounter = outboundCounter;
current.inboundIncreasement = inboundCounter - previous.inboundCounter;
current.outboundIncreasement = outboundCounter - previous.outboundCounter;
traffic.Enqueue(current);
if (traffic.Count > queueMaxSize)
traffic.Dequeue();
if (TrafficChanged != null)
{
TrafficChanged(this, new EventArgs());
}
Thread.Sleep(1000);
}
}
} }
} }

+ 21
- 1
shadowsocks-csharp/Properties/Resources.Designer.cs View File

@@ -182,7 +182,27 @@ namespace Shadowsocks.Properties {
return ((System.Drawing.Bitmap)(obj)); return ((System.Drawing.Bitmap)(obj));
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap ssIn24 {
get {
object obj = ResourceManager.GetObject("ssIn24", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap ssOut24 {
get {
object obj = ResourceManager.GetObject("ssOut24", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap. /// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary> /// </summary>


+ 6
- 0
shadowsocks-csharp/Properties/Resources.resx View File

@@ -148,6 +148,12 @@
<data name="ss24" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="ss24" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ss24.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> <value>..\Resources\ss24.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data> </data>
<data name="ssIn24" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ssIn24.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="ssOut24" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ssOut24.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="ssw128" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="ssw128" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ssw128.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> <value>..\Resources\ssw128.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data> </data>


BIN
shadowsocks-csharp/Resources/ssIn24.png View File

Before After
Width: 24  |  Height: 24  |  Size: 224 B

BIN
shadowsocks-csharp/Resources/ssOut24.png View File

Before After
Width: 24  |  Height: 24  |  Size: 211 B

+ 21
- 1
shadowsocks-csharp/Util/Util.cs View File

@@ -100,29 +100,49 @@ namespace Shadowsocks.Util
public static string FormatBandwidth(long n) public static string FormatBandwidth(long n)
{ {
var result = GetBandwidthScale(n);
return $"{result.Item1:0.##}{result.Item2}";
}
/// <summary>
/// Return scaled bandwidth
/// </summary>
/// <param name="n">Raw bandwidth</param>
/// <returns>
/// Item1: float, bandwidth with suitable scale (eg. 56)
/// Item2: string, scale unit name (eg. KiB)
/// Item3: long, scale unit (eg. 1024)
/// </returns>
public static Tuple<float, string, long> GetBandwidthScale(long n)
{
long scale = 1;
float f = n; float f = n;
string unit = "B"; string unit = "B";
if (f > 1024) if (f > 1024)
{ {
f = f / 1024; f = f / 1024;
scale <<= 10;
unit = "KiB"; unit = "KiB";
} }
if (f > 1024) if (f > 1024)
{ {
f = f / 1024; f = f / 1024;
scale <<= 10;
unit = "MiB"; unit = "MiB";
} }
if (f > 1024) if (f > 1024)
{ {
f = f / 1024; f = f / 1024;
scale <<= 10;
unit = "GiB"; unit = "GiB";
} }
if (f > 1024) if (f > 1024)
{ {
f = f / 1024; f = f / 1024;
scale <<= 10;
unit = "TiB"; unit = "TiB";
} }
return $"{f:0.##}{unit}";
return new Tuple<float, string, long>(f, unit, scale);
} }
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]


+ 89
- 14
shadowsocks-csharp/View/LogForm.Designer.cs View File

@@ -29,6 +29,10 @@
private void InitializeComponent() private void InitializeComponent()
{ {
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend();
System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series();
System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series();
this.LogMessageTextBox = new System.Windows.Forms.TextBox(); this.LogMessageTextBox = new System.Windows.Forms.TextBox();
this.MainMenu = new System.Windows.Forms.MainMenu(this.components); this.MainMenu = new System.Windows.Forms.MainMenu(this.components);
this.FileMenuItem = new System.Windows.Forms.MenuItem(); this.FileMenuItem = new System.Windows.Forms.MenuItem();
@@ -47,8 +51,15 @@
this.WrapTextCheckBox = new System.Windows.Forms.CheckBox(); this.WrapTextCheckBox = new System.Windows.Forms.CheckBox();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.ToolbarFlowLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); this.ToolbarFlowLayoutPanel = new System.Windows.Forms.FlowLayoutPanel();
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
this.trafficChart = new System.Windows.Forms.DataVisualization.Charting.Chart();
this.tableLayoutPanel1.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout();
this.ToolbarFlowLayoutPanel.SuspendLayout(); this.ToolbarFlowLayoutPanel.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trafficChart)).BeginInit();
this.SuspendLayout(); this.SuspendLayout();
// //
// LogMessageTextBox // LogMessageTextBox
@@ -57,13 +68,13 @@
this.LogMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill; this.LogMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.LogMessageTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.LogMessageTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.LogMessageTextBox.ForeColor = System.Drawing.Color.White; this.LogMessageTextBox.ForeColor = System.Drawing.Color.White;
this.LogMessageTextBox.Location = new System.Drawing.Point(3, 40);
this.LogMessageTextBox.Location = new System.Drawing.Point(0, 0);
this.LogMessageTextBox.MaxLength = 2147483647; this.LogMessageTextBox.MaxLength = 2147483647;
this.LogMessageTextBox.Multiline = true; this.LogMessageTextBox.Multiline = true;
this.LogMessageTextBox.Name = "LogMessageTextBox"; this.LogMessageTextBox.Name = "LogMessageTextBox";
this.LogMessageTextBox.ReadOnly = true; this.LogMessageTextBox.ReadOnly = true;
this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.LogMessageTextBox.Size = new System.Drawing.Size(378, 131);
this.LogMessageTextBox.Size = new System.Drawing.Size(378, 74);
this.LogMessageTextBox.TabIndex = 0; this.LogMessageTextBox.TabIndex = 0;
// //
// MainMenu // MainMenu
@@ -144,9 +155,9 @@
this.TopMostCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) this.TopMostCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left))); | System.Windows.Forms.AnchorStyles.Left)));
this.TopMostCheckBox.AutoSize = true; this.TopMostCheckBox.AutoSize = true;
this.TopMostCheckBox.Location = new System.Drawing.Point(247, 3);
this.TopMostCheckBox.Location = new System.Drawing.Point(249, 3);
this.TopMostCheckBox.Name = "TopMostCheckBox"; this.TopMostCheckBox.Name = "TopMostCheckBox";
this.TopMostCheckBox.Size = new System.Drawing.Size(71, 25);
this.TopMostCheckBox.Size = new System.Drawing.Size(72, 23);
this.TopMostCheckBox.TabIndex = 3; this.TopMostCheckBox.TabIndex = 3;
this.TopMostCheckBox.Text = "&Top Most"; this.TopMostCheckBox.Text = "&Top Most";
this.TopMostCheckBox.UseVisualStyleBackColor = true; this.TopMostCheckBox.UseVisualStyleBackColor = true;
@@ -157,7 +168,7 @@
this.ChangeFontButton.AutoSize = true; this.ChangeFontButton.AutoSize = true;
this.ChangeFontButton.Location = new System.Drawing.Point(84, 3); this.ChangeFontButton.Location = new System.Drawing.Point(84, 3);
this.ChangeFontButton.Name = "ChangeFontButton"; this.ChangeFontButton.Name = "ChangeFontButton";
this.ChangeFontButton.Size = new System.Drawing.Size(75, 25);
this.ChangeFontButton.Size = new System.Drawing.Size(75, 23);
this.ChangeFontButton.TabIndex = 2; this.ChangeFontButton.TabIndex = 2;
this.ChangeFontButton.Text = "&Font"; this.ChangeFontButton.Text = "&Font";
this.ChangeFontButton.UseVisualStyleBackColor = true; this.ChangeFontButton.UseVisualStyleBackColor = true;
@@ -168,7 +179,7 @@
this.CleanLogsButton.AutoSize = true; this.CleanLogsButton.AutoSize = true;
this.CleanLogsButton.Location = new System.Drawing.Point(3, 3); this.CleanLogsButton.Location = new System.Drawing.Point(3, 3);
this.CleanLogsButton.Name = "CleanLogsButton"; this.CleanLogsButton.Name = "CleanLogsButton";
this.CleanLogsButton.Size = new System.Drawing.Size(75, 25);
this.CleanLogsButton.Size = new System.Drawing.Size(75, 23);
this.CleanLogsButton.TabIndex = 1; this.CleanLogsButton.TabIndex = 1;
this.CleanLogsButton.Text = "&Clean Logs"; this.CleanLogsButton.Text = "&Clean Logs";
this.CleanLogsButton.UseVisualStyleBackColor = true; this.CleanLogsButton.UseVisualStyleBackColor = true;
@@ -181,7 +192,7 @@
this.WrapTextCheckBox.AutoSize = true; this.WrapTextCheckBox.AutoSize = true;
this.WrapTextCheckBox.Location = new System.Drawing.Point(165, 3); this.WrapTextCheckBox.Location = new System.Drawing.Point(165, 3);
this.WrapTextCheckBox.Name = "WrapTextCheckBox"; this.WrapTextCheckBox.Name = "WrapTextCheckBox";
this.WrapTextCheckBox.Size = new System.Drawing.Size(76, 25);
this.WrapTextCheckBox.Size = new System.Drawing.Size(78, 23);
this.WrapTextCheckBox.TabIndex = 0; this.WrapTextCheckBox.TabIndex = 0;
this.WrapTextCheckBox.Text = "&Wrap Text"; this.WrapTextCheckBox.Text = "&Wrap Text";
this.WrapTextCheckBox.UseVisualStyleBackColor = true; this.WrapTextCheckBox.UseVisualStyleBackColor = true;
@@ -191,15 +202,15 @@
// //
this.tableLayoutPanel1.ColumnCount = 1; this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.LogMessageTextBox, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.ToolbarFlowLayoutPanel, 0, 0); this.tableLayoutPanel1.Controls.Add(this.ToolbarFlowLayoutPanel, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.splitContainer1, 0, 1);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 2; this.tableLayoutPanel1.RowCount = 2;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(384, 174);
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(384, 161);
this.tableLayoutPanel1.TabIndex = 2; this.tableLayoutPanel1.TabIndex = 2;
// //
// ToolbarFlowLayoutPanel // ToolbarFlowLayoutPanel
@@ -212,17 +223,73 @@
this.ToolbarFlowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.ToolbarFlowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.ToolbarFlowLayoutPanel.Location = new System.Drawing.Point(3, 3); this.ToolbarFlowLayoutPanel.Location = new System.Drawing.Point(3, 3);
this.ToolbarFlowLayoutPanel.Name = "ToolbarFlowLayoutPanel"; this.ToolbarFlowLayoutPanel.Name = "ToolbarFlowLayoutPanel";
this.ToolbarFlowLayoutPanel.Size = new System.Drawing.Size(378, 31);
this.ToolbarFlowLayoutPanel.Size = new System.Drawing.Size(378, 29);
this.ToolbarFlowLayoutPanel.TabIndex = 2; this.ToolbarFlowLayoutPanel.TabIndex = 2;
// //
// splitContainer1
//
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer1.Location = new System.Drawing.Point(3, 38);
this.splitContainer1.Name = "splitContainer1";
this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// splitContainer1.Panel1
//
this.splitContainer1.Panel1.Controls.Add(this.LogMessageTextBox);
//
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.trafficChart);
this.splitContainer1.Size = new System.Drawing.Size(378, 120);
this.splitContainer1.SplitterDistance = 74;
this.splitContainer1.TabIndex = 3;
//
// trafficChart
//
chartArea1.AxisX.LabelStyle.Enabled = false;
chartArea1.AxisX.MajorGrid.Interval = 5D;
chartArea1.AxisX.MajorGrid.LineColor = System.Drawing.Color.LightGray;
chartArea1.AxisX.MajorTickMark.Enabled = false;
chartArea1.AxisY.IntervalAutoMode = System.Windows.Forms.DataVisualization.Charting.IntervalAutoMode.VariableCount;
chartArea1.AxisY.LabelAutoFitMaxFontSize = 8;
chartArea1.AxisY.LabelStyle.Interval = 0D;
chartArea1.AxisY.MajorGrid.LineColor = System.Drawing.Color.LightGray;
chartArea1.AxisY.MajorTickMark.Enabled = false;
chartArea1.AxisY2.MajorGrid.LineColor = System.Drawing.Color.LightGray;
chartArea1.AxisY2.Minimum = 0D;
chartArea1.Name = "ChartArea1";
this.trafficChart.ChartAreas.Add(chartArea1);
this.trafficChart.Dock = System.Windows.Forms.DockStyle.Fill;
legend1.MaximumAutoSize = 25F;
legend1.Name = "Legend1";
this.trafficChart.Legends.Add(legend1);
this.trafficChart.Location = new System.Drawing.Point(0, 0);
this.trafficChart.Name = "trafficChart";
this.trafficChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.None;
series1.ChartArea = "ChartArea1";
series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
series1.IsXValueIndexed = true;
series1.Legend = "Legend1";
series1.Name = "Inbound";
series2.ChartArea = "ChartArea1";
series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
series2.IsXValueIndexed = true;
series2.Legend = "Legend1";
series2.Name = "Outbound";
this.trafficChart.Series.Add(series1);
this.trafficChart.Series.Add(series2);
this.trafficChart.Size = new System.Drawing.Size(378, 42);
this.trafficChart.TabIndex = 0;
this.trafficChart.Text = "chart1";
//
// LogForm // LogForm
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(384, 174);
this.ClientSize = new System.Drawing.Size(384, 161);
this.Controls.Add(this.tableLayoutPanel1); this.Controls.Add(this.tableLayoutPanel1);
this.Menu = this.MainMenu; this.Menu = this.MainMenu;
this.MinimumSize = new System.Drawing.Size(400, 213);
this.MinimumSize = new System.Drawing.Size(400, 200);
this.Name = "LogForm"; this.Name = "LogForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Log Viewer"; this.Text = "Log Viewer";
@@ -233,6 +300,12 @@
this.tableLayoutPanel1.PerformLayout(); this.tableLayoutPanel1.PerformLayout();
this.ToolbarFlowLayoutPanel.ResumeLayout(false); this.ToolbarFlowLayoutPanel.ResumeLayout(false);
this.ToolbarFlowLayoutPanel.PerformLayout(); this.ToolbarFlowLayoutPanel.PerformLayout();
this.splitContainer1.Panel1.ResumeLayout(false);
this.splitContainer1.Panel1.PerformLayout();
this.splitContainer1.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
this.splitContainer1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.trafficChart)).EndInit();
this.ResumeLayout(false); this.ResumeLayout(false);
} }
@@ -257,5 +330,7 @@
private System.Windows.Forms.FlowLayoutPanel ToolbarFlowLayoutPanel; private System.Windows.Forms.FlowLayoutPanel ToolbarFlowLayoutPanel;
private System.Windows.Forms.MenuItem MenuItemSeparater; private System.Windows.Forms.MenuItem MenuItemSeparater;
private System.Windows.Forms.MenuItem ShowToolbarMenuItem; private System.Windows.Forms.MenuItem ShowToolbarMenuItem;
private System.Windows.Forms.SplitContainer splitContainer1;
private System.Windows.Forms.DataVisualization.Charting.Chart trafficChart;
} }
} }

+ 52
- 1
shadowsocks-csharp/View/LogForm.cs View File

@@ -2,6 +2,9 @@
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using System.Collections.Generic;
using System.Linq;
using Shadowsocks.Controller; using Shadowsocks.Controller;
using Shadowsocks.Properties; using Shadowsocks.Properties;
@@ -18,6 +21,15 @@ namespace Shadowsocks.View
const int BACK_OFFSET = 65536; const int BACK_OFFSET = 65536;
ShadowsocksController controller; ShadowsocksController controller;
#region chart
List<float> inboundPoints = new List<float>();
List<float> outboundPoints = new List<float>();
long maxSpeed = 0;
Tuple<float, string, long> bandwidthScale = new Tuple<float, string, long>(0, "B", 1);
TextAnnotation inboundAnnotation = new TextAnnotation();
TextAnnotation outboundAnnotation = new TextAnnotation();
#endregion
public LogForm(ShadowsocksController controller, string filename) public LogForm(ShadowsocksController controller, string filename)
{ {
this.controller = controller; this.controller = controller;
@@ -30,7 +42,8 @@ namespace Shadowsocks.View
{ {
config = new LogViewerConfig(); config = new LogViewerConfig();
} }
else {
else
{
topMostTrigger = config.topMost; topMostTrigger = config.topMost;
wrapTextTrigger = config.wrapText; wrapTextTrigger = config.wrapText;
toolbarTrigger = config.toolbarShown; toolbarTrigger = config.toolbarShown;
@@ -39,9 +52,46 @@ namespace Shadowsocks.View
LogMessageTextBox.Font = config.GetFont(); LogMessageTextBox.Font = config.GetFont();
} }
controller.TrafficChanged += controller_TrafficChanged;
UpdateTexts(); UpdateTexts();
} }
private void controller_TrafficChanged(object sender, EventArgs e)
{
inboundPoints.Clear();
outboundPoints.Clear();
foreach (var trafficPerSecond in controller.traffic)
{
inboundPoints.Add(trafficPerSecond.inboundIncreasement);
outboundPoints.Add(trafficPerSecond.outboundIncreasement);
maxSpeed = Math.Max(maxSpeed, Math.Max(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement));
}
bandwidthScale = Utils.GetBandwidthScale(maxSpeed);
//rescale the original data points, since it is List<float>, .ForEach does not work
inboundPoints = inboundPoints.Select(p => p / bandwidthScale.Item3).ToList();
outboundPoints = outboundPoints.Select(p => p / bandwidthScale.Item3).ToList();
if (trafficChart.InvokeRequired)
{
trafficChart.Invoke(new Action(() =>
{
trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints);
trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints);
trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.Item2;
inboundAnnotation.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last();
inboundAnnotation.Text = Utils.FormatBandwidth(controller.traffic.Last.inboundIncreasement);
outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last();
outboundAnnotation.Text = Utils.FormatBandwidth(controller.traffic.Last.outboundIncreasement);
trafficChart.Annotations.Clear();
trafficChart.Annotations.Add(inboundAnnotation);
trafficChart.Annotations.Add(outboundAnnotation);
}));
}
}
private void UpdateTexts() private void UpdateTexts()
{ {
FileMenuItem.Text = I18N.GetString("&File"); FileMenuItem.Text = I18N.GetString("&File");
@@ -144,6 +194,7 @@ namespace Shadowsocks.View
private void LogForm_FormClosing(object sender, FormClosingEventArgs e) private void LogForm_FormClosing(object sender, FormClosingEventArgs e)
{ {
timer.Stop(); timer.Stop();
controller.TrafficChanged -= controller_TrafficChanged;
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
if (config == null) if (config == null)
config = new LogViewerConfig(); config = new LogViewerConfig();


+ 3
- 0
shadowsocks-csharp/View/LogForm.resx View File

@@ -144,4 +144,7 @@
<metadata name="$this.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="$this.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value> <value>True</value>
</metadata> </metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>39</value>
</metadata>
</root> </root>

+ 55
- 6
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms; using System.Windows.Forms;
using ZXing; using ZXing;
@@ -25,6 +26,8 @@ namespace Shadowsocks.View
private UpdateChecker updateChecker; private UpdateChecker updateChecker;
private NotifyIcon _notifyIcon; private NotifyIcon _notifyIcon;
private Bitmap icon_baseBitmap;
private Icon icon_base, icon_in, icon_out, icon_both, targetIcon;
private ContextMenu contextMenu1; private ContextMenu contextMenu1;
private bool _isFirstRun; private bool _isFirstRun;
@@ -74,6 +77,7 @@ namespace Shadowsocks.View
_notifyIcon.MouseClick += notifyIcon1_Click; _notifyIcon.MouseClick += notifyIcon1_Click;
_notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick; _notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick;
_notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed; _notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed;
controller.TrafficChanged += controller_TrafficChanged;
this.updateChecker = new UpdateChecker(); this.updateChecker = new UpdateChecker();
updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted; updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted;
@@ -94,6 +98,32 @@ namespace Shadowsocks.View
} }
} }
private void controller_TrafficChanged(object sender, EventArgs e)
{
if (icon_baseBitmap == null)
return;
Icon newIcon;
bool hasInbound = controller.traffic.Last.inboundIncreasement > 0;
bool hasOutbound = controller.traffic.Last.outboundIncreasement > 0;
if (hasInbound && hasOutbound)
newIcon = icon_both;
else if (hasInbound)
newIcon = icon_in;
else if (hasOutbound)
newIcon = icon_out;
else
newIcon = icon_base;
if (newIcon != this.targetIcon)
{
this.targetIcon = newIcon;
_notifyIcon.Icon = newIcon;
}
}
void controller_Errored(object sender, System.IO.ErrorEventArgs e) void controller_Errored(object sender, System.IO.ErrorEventArgs e)
{ {
MessageBox.Show(e.GetException().ToString(), String.Format(I18N.GetString("Shadowsocks Error: {0}"), e.GetException().Message)); MessageBox.Show(e.GetException().ToString(), String.Format(I18N.GetString("Shadowsocks Error: {0}"), e.GetException().Message));
@@ -105,26 +135,32 @@ namespace Shadowsocks.View
Graphics graphics = Graphics.FromHwnd(IntPtr.Zero); Graphics graphics = Graphics.FromHwnd(IntPtr.Zero);
dpi = (int)graphics.DpiX; dpi = (int)graphics.DpiX;
graphics.Dispose(); graphics.Dispose();
Bitmap icon = null;
icon_baseBitmap = null;
if (dpi < 97) if (dpi < 97)
{ {
// dpi = 96; // dpi = 96;
icon = Resources.ss16;
icon_baseBitmap = Resources.ss16;
} }
else if (dpi < 121) else if (dpi < 121)
{ {
// dpi = 120; // dpi = 120;
icon = Resources.ss20;
icon_baseBitmap = Resources.ss20;
} }
else else
{ {
icon = Resources.ss24;
icon_baseBitmap = Resources.ss24;
} }
Configuration config = controller.GetConfigurationCopy(); Configuration config = controller.GetConfigurationCopy();
bool enabled = config.enabled; bool enabled = config.enabled;
bool global = config.global; bool global = config.global;
icon = getTrayIconByState(icon, enabled, global);
_notifyIcon.Icon = Icon.FromHandle(icon.GetHicon());
icon_baseBitmap = getTrayIconByState(icon_baseBitmap, enabled, global);
icon_base = Icon.FromHandle(icon_baseBitmap.GetHicon());
targetIcon = icon_base;
icon_in = Icon.FromHandle(AddBitmapOverlay(icon_baseBitmap, Resources.ssIn24).GetHicon());
icon_out = Icon.FromHandle(AddBitmapOverlay(icon_baseBitmap, Resources.ssOut24).GetHicon());
icon_both = Icon.FromHandle(AddBitmapOverlay(icon_baseBitmap, Resources.ssIn24, Resources.ssOut24).GetHicon());
_notifyIcon.Icon = targetIcon;
string serverInfo = null; string serverInfo = null;
if (controller.GetCurrentStrategy() != null) if (controller.GetCurrentStrategy() != null)
@@ -177,6 +213,19 @@ namespace Shadowsocks.View
return iconCopy; return iconCopy;
} }
private Bitmap AddBitmapOverlay(Bitmap original, params Bitmap[] overlays)
{
Bitmap bitmap = new Bitmap(original.Width, original.Height, PixelFormat.Format64bppArgb);
Graphics canvas = Graphics.FromImage(bitmap);
canvas.DrawImage(original, new Point(0, 0));
foreach (Bitmap overlay in overlays)
{
canvas.DrawImage(new Bitmap(overlay, original.Size), new Point(0, 0));
}
canvas.Save();
return bitmap;
}
private MenuItem CreateMenuItem(string text, EventHandler click) private MenuItem CreateMenuItem(string text, EventHandler click)
{ {
return new MenuItem(I18N.GetString(text), click); return new MenuItem(I18N.GetString(text), click);


+ 2
- 0
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -291,6 +291,8 @@
<Content Include="FodyWeavers.xml"> <Content Include="FodyWeavers.xml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Content> </Content>
<Content Include="Resources\ssIn24.png" />
<Content Include="Resources\ssOut24.png" />
<Content Include="shadowsocks.ico" /> <Content Include="shadowsocks.ico" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>


Loading…
Cancel
Save