diff --git a/nuget.config b/nuget.config
index f5cdc0be..28c78837 100644
--- a/nuget.config
+++ b/nuget.config
@@ -1,6 +1,6 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs
index 975c6df8..2a2b60a7 100644
--- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs
+++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs
@@ -35,10 +35,7 @@ namespace Shadowsocks.Controller
|| (length < 2 || firstPacket[0] != 5))
return false;
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
- TCPHandler handler = new TCPHandler(this, _config);
- handler.connection = socket;
- handler.controller = _controller;
- handler.tcprelay = this;
+ TCPHandler handler = new TCPHandler(_controller, _config, this, socket);
IList handlersToClose = new List();
lock (Handlers)
@@ -130,68 +127,67 @@ namespace Shadowsocks.Controller
public static readonly int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth
public static readonly int BufferSize = RecvSize + RecvReserveSize + 32;
- // public Encryptor encryptor;
- public IEncryptor encryptor;
- public Server server;
- // Client socket.
- private AsyncSession _currentRemoteSession;
+ public DateTime lastActivity;
- public Socket connection;
- public ShadowsocksController controller;
- public TCPRelay tcprelay;
+ private ShadowsocksController _controller;
+ private Configuration _config;
+ private TCPRelay _tcprelay;
+ private Socket _connection;
- public DateTime lastActivity;
+ private IEncryptor _encryptor;
+ private Server _server;
- private const int MaxRetry = 4;
- private int _retryCount = 0;
- private bool _proxyConnected;
- private bool _destConnected;
+ private AsyncSession _currentRemoteSession;
+
+ private const int MaxRetry = 4;
+ private int _retryCount = 0;
+ private bool _proxyConnected;
+ private bool _destConnected;
- private byte _command;
- private byte[] _firstPacket;
- private int _firstPacketLength;
+ private byte _command;
+ private byte[] _firstPacket;
+ private int _firstPacketLength;
- private int _totalRead = 0;
- private int _totalWrite = 0;
+ private int _totalRead = 0;
+ private int _totalWrite = 0;
- private byte[] _remoteRecvBuffer = new byte[BufferSize];
- private byte[] _remoteSendBuffer = new byte[BufferSize];
- private byte[] _connetionRecvBuffer = new byte[BufferSize];
- private byte[] _connetionSendBuffer = new byte[BufferSize];
+ private byte[] _remoteRecvBuffer = new byte[BufferSize];
+ private byte[] _remoteSendBuffer = new byte[BufferSize];
+ private byte[] _connetionRecvBuffer = new byte[BufferSize];
+ private byte[] _connetionSendBuffer = new byte[BufferSize];
- private bool _connectionShutdown = false;
- private bool _remoteShutdown = false;
- private bool _closed = false;
+ private bool _connectionShutdown = false;
+ private bool _remoteShutdown = false;
+ private bool _closed = false;
- private object _encryptionLock = new object();
- private object _decryptionLock = new object();
+ private object _encryptionLock = new object();
+ private object _decryptionLock = new object();
private DateTime _startConnectTime;
private DateTime _startReceivingTime;
private DateTime _startSendingTime;
- private int _bytesToSend;
- private TCPRelay _tcprelay; // TODO: is _tcprelay equals tcprelay declared above?
- private Configuration _config;
- public TCPHandler(TCPRelay tcprelay, Configuration config)
+ public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket)
{
- this._tcprelay = tcprelay;
+ this._controller = controller;
this._config = config;
+ this._tcprelay = tcprelay;
+ this._connection = socket;
}
public void CreateRemote()
{
- Server server = controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)connection.RemoteEndPoint);
+ Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint);
if (server == null || server.server == "")
throw new ArgumentException("No server configured");
lock (_encryptionLock)
{
lock (_decryptionLock)
{
- encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false);
+ _encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false);
}
}
- this.server = server;
+ this._server = server;
}
public void Start(byte[] firstPacket, int length)
@@ -210,18 +206,19 @@ namespace Shadowsocks.Controller
public void Close()
{
- lock (tcprelay.Handlers)
+ lock (this)
{
- tcprelay.Handlers.Remove(this);
- }
- lock (this) {
if (_closed) return;
_closed = true;
}
+ lock (_tcprelay.Handlers)
+ {
+ _tcprelay.Handlers.Remove(this);
+ }
try
{
- connection?.Shutdown(SocketShutdown.Both);
- connection?.Close();
+ _connection?.Shutdown(SocketShutdown.Both);
+ _connection?.Close();
}
catch (Exception e)
{
@@ -241,7 +238,7 @@ namespace Shadowsocks.Controller
{
lock (_decryptionLock)
{
- encryptor?.Dispose();
+ _encryptor?.Dispose();
}
}
}
@@ -261,7 +258,7 @@ namespace Shadowsocks.Controller
response = new byte[] { 0, 91 };
Logging.Error("socks 5 protocol error");
}
- connection?.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null);
+ _connection?.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null);
}
else
Close();
@@ -278,7 +275,7 @@ namespace Shadowsocks.Controller
if (_closed) return;
try
{
- connection.EndSend(ar);
+ _connection.EndSend(ar);
// +-----+-----+-------+------+----------+----------+
// | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
@@ -287,7 +284,7 @@ namespace Shadowsocks.Controller
// +-----+-----+-------+------+----------+----------+
// Skip first 3 bytes
// TODO validate
- connection.BeginReceive(_connetionRecvBuffer, 0, 3, SocketFlags.None, new AsyncCallback(handshakeReceive2Callback), null);
+ _connection.BeginReceive(_connetionRecvBuffer, 0, 3, SocketFlags.None, new AsyncCallback(handshakeReceive2Callback), null);
}
catch (Exception e)
{
@@ -301,14 +298,14 @@ namespace Shadowsocks.Controller
if (_closed) return;
try
{
- int bytesRead = connection.EndReceive(ar);
+ int bytesRead = _connection.EndReceive(ar);
if (bytesRead >= 3)
{
_command = _connetionRecvBuffer[1];
if (_command == 1)
{
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
- connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ResponseCallback), null);
+ _connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ResponseCallback), null);
}
else if (_command == 3)
HandleUDPAssociate();
@@ -328,7 +325,7 @@ namespace Shadowsocks.Controller
private void HandleUDPAssociate()
{
- IPEndPoint endPoint = (IPEndPoint)connection.LocalEndPoint;
+ IPEndPoint endPoint = (IPEndPoint)_connection.LocalEndPoint;
byte[] address = endPoint.Address.GetAddressBytes();
int port = endPoint.Port;
byte[] response = new byte[4 + address.Length + 2];
@@ -345,7 +342,7 @@ namespace Shadowsocks.Controller
address.CopyTo(response, 4);
response[response.Length - 1] = (byte)(port & 0xFF);
response[response.Length - 2] = (byte)((port >> 8) & 0xFF);
- connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true);
+ _connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true);
}
private void ReadAll(IAsyncResult ar)
@@ -355,15 +352,15 @@ namespace Shadowsocks.Controller
{
if (ar.AsyncState != null)
{
- connection.EndSend(ar);
- connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
+ _connection.EndSend(ar);
+ _connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
}
else
{
- int bytesRead = connection.EndReceive(ar);
+ int bytesRead = _connection.EndReceive(ar);
if (bytesRead > 0)
{
- connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
+ _connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
}
else
Close();
@@ -380,7 +377,7 @@ namespace Shadowsocks.Controller
{
try
{
- connection?.EndSend(ar);
+ _connection?.EndSend(ar);
StartConnect();
}
catch (Exception e)
@@ -420,10 +417,10 @@ namespace Shadowsocks.Controller
// Setting up proxy
IProxy remote;
EndPoint proxyEP;
- if (_config.useProxy)
+ if (_config.proxy.useProxy)
{
remote = new Socks5Proxy();
- proxyEP = SocketUtil.GetEndPoint(_config.proxyServer, _config.proxyPort);
+ proxyEP = SocketUtil.GetEndPoint(_config.proxy.proxyServer, _config.proxy.proxyPort);
}
else
{
@@ -440,8 +437,8 @@ namespace Shadowsocks.Controller
proxyTimer.Enabled = true;
proxyTimer.Session = session;
- proxyTimer.DestEndPoint = SocketUtil.GetEndPoint(server.server, server.server_port);
- proxyTimer.Server = server;
+ proxyTimer.DestEndPoint = SocketUtil.GetEndPoint(_server.server, _server.server_port);
+ proxyTimer.Server = _server;
_proxyConnected = false;
@@ -542,7 +539,7 @@ namespace Shadowsocks.Controller
var session = timer.Session;
Server server = timer.Server;
- IStrategy strategy = controller.GetCurrentStrategy();
+ IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.SetFailure(server);
Logging.Info($"{server.FriendlyName()} timed out");
session.Remote.Close();
@@ -568,7 +565,7 @@ namespace Shadowsocks.Controller
{
var session = (AsyncSession) ar.AsyncState;
ServerTimer timer = session.State;
- server = timer.Server;
+ _server = timer.Server;
timer.Elapsed -= destConnectTimer_Elapsed;
timer.Enabled = false;
timer.Dispose();
@@ -581,13 +578,13 @@ namespace Shadowsocks.Controller
if (_config.isVerboseLogging)
{
- Logging.Info($"Socket connected to ss server: {server.FriendlyName()}");
+ Logging.Info($"Socket connected to ss server: {_server.FriendlyName()}");
}
var latency = DateTime.Now - _startConnectTime;
- IStrategy strategy = controller.GetCurrentStrategy();
- strategy?.UpdateLatency(server, latency);
- _tcprelay.UpdateLatency(server, latency);
+ IStrategy strategy = _controller.GetCurrentStrategy();
+ strategy?.UpdateLatency(_server, latency);
+ _tcprelay.UpdateLatency(_server, latency);
StartPipe(session);
}
@@ -596,10 +593,10 @@ namespace Shadowsocks.Controller
}
catch (Exception e)
{
- if (server != null)
+ if (_server != null)
{
- IStrategy strategy = controller.GetCurrentStrategy();
- strategy?.SetFailure(server);
+ IStrategy strategy = _controller.GetCurrentStrategy();
+ strategy?.SetFailure(_server);
}
Logging.LogUsefulException(e);
RetryConnect();
@@ -613,7 +610,7 @@ namespace Shadowsocks.Controller
{
_startReceivingTime = DateTime.Now;
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
- connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback),
+ _connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback),
new AsyncSession(session, true) /* to tell the callback this is the first time reading packet, and we haven't found the header yet. */);
}
catch (Exception e)
@@ -631,7 +628,7 @@ namespace Shadowsocks.Controller
var session = (AsyncSession) ar.AsyncState;
int bytesRead = session.Remote.EndReceive(ar);
_totalRead += bytesRead;
- _tcprelay.UpdateInboundCounter(server, bytesRead);
+ _tcprelay.UpdateInboundCounter(_server, bytesRead);
if (bytesRead > 0)
{
lastActivity = DateTime.Now;
@@ -639,15 +636,15 @@ namespace Shadowsocks.Controller
lock (_decryptionLock)
{
if (_closed) return;
- encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
+ _encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
}
- connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session);
- IStrategy strategy = controller.GetCurrentStrategy();
- strategy?.UpdateLastRead(server);
+ _connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session);
+ IStrategy strategy = _controller.GetCurrentStrategy();
+ strategy?.UpdateLastRead(_server);
}
else
{
- connection.Shutdown(SocketShutdown.Send);
+ _connection.Shutdown(SocketShutdown.Send);
_connectionShutdown = true;
CheckClose();
}
@@ -664,8 +661,8 @@ namespace Shadowsocks.Controller
if (_closed) return;
try
{
- if(connection == null) return;
- int bytesRead = connection.EndReceive(ar);
+ if(_connection == null) return;
+ int bytesRead = _connection.EndReceive(ar);
_totalWrite += bytesRead;
var session = (AsyncSession) ar.AsyncState;
@@ -713,14 +710,13 @@ namespace Shadowsocks.Controller
lock (_encryptionLock)
{
if (_closed) return;
- encryptor.Encrypt(_connetionRecvBuffer, bytesRead, _connetionSendBuffer, out bytesToSend);
+ _encryptor.Encrypt(_connetionRecvBuffer, bytesRead, _connetionSendBuffer, out bytesToSend);
}
- _tcprelay.UpdateOutboundCounter(server, bytesToSend);
+ _tcprelay.UpdateOutboundCounter(_server, bytesToSend);
_startSendingTime = DateTime.Now;
- _bytesToSend = bytesToSend;
remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session);
- IStrategy strategy = controller.GetCurrentStrategy();
- strategy?.UpdateLastWrite(server);
+ IStrategy strategy = _controller.GetCurrentStrategy();
+ strategy?.UpdateLastWrite(_server);
}
else
{
@@ -743,7 +739,7 @@ namespace Shadowsocks.Controller
{
var session = (AsyncSession)ar.AsyncState;
session.Remote.EndSend(ar);
- connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session);
+ _connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session);
}
catch (Exception e)
{
@@ -758,7 +754,7 @@ namespace Shadowsocks.Controller
try
{
var session = (AsyncSession)ar.AsyncState;
- connection?.EndSend(ar);
+ _connection?.EndSend(ar);
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
}
catch (Exception e)
diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs
index 983130a5..d2df00fe 100644
--- a/shadowsocks-csharp/Controller/ShadowsocksController.cs
+++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs
@@ -217,15 +217,15 @@ namespace Shadowsocks.Controller
public void DisableProxy()
{
- _config.useProxy = false;
+ _config.proxy.useProxy = false;
SaveConfig(_config);
}
public void EnableProxy(string proxy, int port)
{
- _config.useProxy = true;
- _config.proxyServer = proxy;
- _config.proxyPort = port;
+ _config.proxy.useProxy = true;
+ _config.proxy.proxyServer = proxy;
+ _config.proxy.proxyPort = port;
SaveConfig(_config);
}
@@ -348,12 +348,40 @@ namespace Shadowsocks.Controller
{
_config.autoCheckUpdate = enabled;
Configuration.Save(_config);
+ if (ConfigChanged != null)
+ {
+ ConfigChanged(this, new EventArgs());
+ }
}
public void SaveLogViewerConfig(LogViewerConfig newConfig)
{
_config.logViewer = newConfig;
Configuration.Save(_config);
+ if (ConfigChanged != null)
+ {
+ ConfigChanged(this, new EventArgs());
+ }
+ }
+
+ public void SaveProxyConfig(ProxyConfig newConfig)
+ {
+ _config.proxy = newConfig;
+ Configuration.Save(_config);
+ if (ConfigChanged != null)
+ {
+ ConfigChanged(this, new EventArgs());
+ }
+ }
+
+ public void SaveHotkeyConfig(HotkeyConfig newConfig)
+ {
+ _config.hotkey = newConfig;
+ SaveConfig(_config);
+ if (ConfigChanged != null)
+ {
+ ConfigChanged(this, new EventArgs());
+ }
}
public void UpdateLatency(Server server, TimeSpan latency)
@@ -547,6 +575,8 @@ namespace Shadowsocks.Controller
File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8);
}
+ #region Memory Management
+
private void StartReleasingMemory()
{
_ramThread = new Thread(new ThreadStart(ReleaseMemory));
@@ -563,6 +593,10 @@ namespace Shadowsocks.Controller
}
}
+ #endregion
+
+ #region Traffic Statistics
+
private void StartTrafficStatistics(int queueMaxSize)
{
traffic = new QueueLast();
@@ -597,5 +631,7 @@ namespace Shadowsocks.Controller
}
}
+ #endregion
+
}
}
diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt
index 4d30e865..001490ce 100644
--- a/shadowsocks-csharp/Data/cn.txt
+++ b/shadowsocks-csharp/Data/cn.txt
@@ -27,6 +27,7 @@ Verbose Logging=详细记录日志
Updates...=更新...
Check for Updates...=检查更新
Check for Updates at Startup=启动时检查更新
+Edit Hotkeys...=编辑快捷键...
About...=关于...
Quit=退出
Edit Servers=编辑服务器
@@ -84,6 +85,17 @@ Edit Online PAC URL=编辑在线 PAC 网址
Edit Online PAC URL...=编辑在线 PAC 网址...
Please input PAC Url=请输入 PAC 网址
+# HotkeySettings Form
+
+Switch system proxy=切换系统代理状态
+Switch to PAC mode=切换到PAC模式
+Switch to Global mode=切换到全局模式
+Switch share over LAN=切换局域网共享
+Show Logs=显示日志
+Switch to prev server=切换上个服务器
+Switch to next server=切换下个服务器
+Reg All=注册全部热键
+
# Messages
Shadowsocks Error: {0}=Shadowsocks 错误: {0}
@@ -116,3 +128,5 @@ Unexpected error, shadowsocks will exit. Please report to=非预期错误,Shad
Unsupported operating system, use Windows Vista at least.=不支持的操作系统版本,最低需求为Windows Vista。
Proxy request failed=代理请求失败
Proxy handshake failed=代理握手失败
+Register hotkey failed=注册热键失败
+Cannot parse hotkey: {0}=解析热键失败: {0}
diff --git a/shadowsocks-csharp/Data/zh_tw.txt b/shadowsocks-csharp/Data/zh_tw.txt
index d2dfd124..337ce4f3 100644
--- a/shadowsocks-csharp/Data/zh_tw.txt
+++ b/shadowsocks-csharp/Data/zh_tw.txt
@@ -27,6 +27,7 @@ Verbose Logging=詳細記錄日誌
Updates...=更新...
Check for Updates...=檢查更新
Check for Updates at Startup=啟動時檢查更新
+Edit Hotkeys...=編輯熱鍵...
About...=關於...
Quit=退出
Edit Servers=編輯伺服器
@@ -84,12 +85,23 @@ Edit Online PAC URL=編輯在線 PAC 網址
Edit Online PAC URL...=編輯在線 PAC 網址...
Please input PAC Url=請輸入 PAC 網址
+# HotkeySettings Form
+
+Switch system proxy=切换系統代理狀態
+Switch to PAC mode=切换為PAC模式
+Switch to Global mode=切换為全局模式
+Switch share over LAN=切换局域網共享
+Show Logs=顯示日誌
+Switch to prev server=切换上個伺服器
+Switch to next server=切换下個伺服器
+Reg All=註冊全部熱鍵
+
# Messages
Shadowsocks Error: {0}=Shadowsocks 錯誤: {0}
Port already in use=連接埠號碼已被占用
Illegal port number format=非法連接埠號碼格式
-Please add at least one server=請新增至少壹個伺服器
+Please add at least one server=請新增至少一個伺服器
Server IP can not be blank=伺服器 IP 不能為空
Password can not be blank=密碼不能為空
Port out of range=連接埠號碼超出範圍
@@ -107,7 +119,7 @@ No updates found. Please report to GFWList if you have problems with it.=未發
No QRCode found. Try to zoom in or move it to the center of the screen.=未發現 QR 碼,嘗試把它放大或移動到靠近屏幕中間的位置
Shadowsocks is already running.=Shadowsocks 已經在運行。
Find Shadowsocks icon in your notify tray.=請在工作列裏尋找 Shadowsocks 圖標。
-If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同時啟動多個,可以另外復制壹份到別的目錄。
+If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同時啟動多個,可以另外復制一份到別的目錄。
Failed to decode QRCode=無法解析 QR 碼
Failed to update registry=無法修改登錄檔
System Proxy On: =系統代理已啟用:
@@ -116,3 +128,5 @@ Unexpected error, shadowsocks will exit. Please report to=非預期錯誤,Shad
Unsupported operating system, use Windows Vista at least.=不支持的作業系統版本,最低需求為Windows Vista。
Proxy request failed=代理請求失敗
Proxy handshake failed=代理握手失敗
+Register hotkey failed=註冊熱鍵失敗
+Cannot parse hotkey: {0}=解析熱鍵失敗: {0}
diff --git a/shadowsocks-csharp/Encryption/EncryptorBase.cs b/shadowsocks-csharp/Encryption/EncryptorBase.cs
index b7466788..f79dbff7 100644
--- a/shadowsocks-csharp/Encryption/EncryptorBase.cs
+++ b/shadowsocks-csharp/Encryption/EncryptorBase.cs
@@ -2,6 +2,22 @@
namespace Shadowsocks.Encryption
{
+ public struct EncryptorInfo
+ {
+ public string name;
+ public int key_size;
+ public int iv_size;
+ public int type;
+
+ public EncryptorInfo(string name, int key_size, int iv_size, int type)
+ {
+ this.name = name;
+ this.key_size = key_size;
+ this.iv_size = iv_size;
+ this.type = type;
+ }
+ }
+
public abstract class EncryptorBase
: IEncryptor
{
diff --git a/shadowsocks-csharp/Encryption/IVEncryptor.cs b/shadowsocks-csharp/Encryption/IVEncryptor.cs
index 4e8a72e0..016045ab 100755
--- a/shadowsocks-csharp/Encryption/IVEncryptor.cs
+++ b/shadowsocks-csharp/Encryption/IVEncryptor.cs
@@ -16,8 +16,7 @@ namespace Shadowsocks.Encryption
protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE];
- protected Dictionary> ciphers;
- protected Dictionary ciphersDetail;
+ protected Dictionary ciphers;
private static readonly ConcurrentDictionary CachedKeys = new ConcurrentDictionary();
protected byte[] _encryptIV;
@@ -28,7 +27,7 @@ namespace Shadowsocks.Encryption
protected int _cipher;
// cipher name in MbedTLS, useless when using LibSodium
protected string _cipherMbedName;
- protected int[] _cipherInfo;
+ protected EncryptorInfo _cipherInfo;
protected byte[] _key;
protected int keyLen;
protected int ivLen;
@@ -39,7 +38,7 @@ namespace Shadowsocks.Encryption
InitKey(method, password);
}
- protected abstract Dictionary> getCiphers();
+ protected abstract Dictionary getCiphers();
private void InitKey(string method, string password)
{
@@ -47,16 +46,15 @@ namespace Shadowsocks.Encryption
_method = method;
string k = method + ":" + password;
ciphers = getCiphers();
- ciphersDetail = ciphers[_method];
- _cipherMbedName = ciphersDetail.Keys.FirstOrDefault();
- _cipherInfo = ciphers[_method][_cipherMbedName];
- _cipher = _cipherInfo[2];
+ _cipherInfo = ciphers[_method];
+ _cipherMbedName = _cipherInfo.name;
+ _cipher = _cipherInfo.type;
if (_cipher == 0)
{
throw new Exception("method not found");
}
- keyLen = _cipherInfo[0];
- ivLen = _cipherInfo[1];
+ keyLen = _cipherInfo.key_size;
+ ivLen = _cipherInfo.iv_size;
_key = CachedKeys.GetOrAdd(k, (nk) =>
{
byte[] passbuf = Encoding.UTF8.GetBytes(password);
diff --git a/shadowsocks-csharp/Encryption/MbedTLSEncryptor.cs b/shadowsocks-csharp/Encryption/MbedTLSEncryptor.cs
index d5d3728c..56669133 100644
--- a/shadowsocks-csharp/Encryption/MbedTLSEncryptor.cs
+++ b/shadowsocks-csharp/Encryption/MbedTLSEncryptor.cs
@@ -20,18 +20,18 @@ namespace Shadowsocks.Encryption
{
}
- private static Dictionary> _ciphers = new Dictionary> {
- { "aes-128-cfb", new Dictionary { { "AES-128-CFB128", new int[] { 16, 16, CIPHER_AES } } } },
- { "aes-192-cfb", new Dictionary { { "AES-192-CFB128", new int[] { 24, 16, CIPHER_AES } } } },
- { "aes-256-cfb", new Dictionary { { "AES-256-CFB128", new int[] { 32, 16, CIPHER_AES } } } },
- { "aes-128-ctr", new Dictionary { { "AES-128-CTR", new int[] { 16, 16, CIPHER_AES } } } },
- { "aes-192-ctr", new Dictionary { { "AES-192-CTR", new int[] { 24, 16, CIPHER_AES } } } },
- { "aes-256-ctr", new Dictionary { { "AES-256-CTR", new int[] { 32, 16, CIPHER_AES } } } },
- { "bf-cfb", new Dictionary { { "BLOWFISH-CFB64", new int[] { 16, 8, CIPHER_BLOWFISH } } } },
- { "camellia-128-cfb", new Dictionary { { "CAMELLIA-128-CFB128", new int[] { 16, 16, CIPHER_CAMELLIA } } } },
- { "camellia-192-cfb", new Dictionary { { "CAMELLIA-192-CFB128", new int[] { 24, 16, CIPHER_CAMELLIA } } } },
- { "camellia-256-cfb", new Dictionary { { "CAMELLIA-256-CFB128", new int[] { 32, 16, CIPHER_CAMELLIA } } } },
- { "rc4-md5", new Dictionary { { "ARC4-128", new int[] { 16, 16, CIPHER_RC4 } } } }
+ private static Dictionary _ciphers = new Dictionary {
+ { "aes-128-cfb", new EncryptorInfo("AES-128-CFB128", 16, 16, CIPHER_AES) },
+ { "aes-192-cfb", new EncryptorInfo("AES-192-CFB128", 24, 16, CIPHER_AES) },
+ { "aes-256-cfb", new EncryptorInfo("AES-256-CFB128", 32, 16, CIPHER_AES) },
+ { "aes-128-ctr", new EncryptorInfo("AES-128-CTR", 16, 16, CIPHER_AES) },
+ { "aes-192-ctr", new EncryptorInfo("AES-192-CTR", 24, 16, CIPHER_AES) },
+ { "aes-256-ctr", new EncryptorInfo("AES-256-CTR", 32, 16, CIPHER_AES) },
+ { "bf-cfb", new EncryptorInfo("BLOWFISH-CFB64", 16, 8, CIPHER_BLOWFISH) },
+ { "camellia-128-cfb", new EncryptorInfo("CAMELLIA-128-CFB128", 16, 16, CIPHER_CAMELLIA) },
+ { "camellia-192-cfb", new EncryptorInfo("CAMELLIA-192-CFB128", 24, 16, CIPHER_CAMELLIA) },
+ { "camellia-256-cfb", new EncryptorInfo("CAMELLIA-256-CFB128", 32, 16, CIPHER_CAMELLIA) },
+ { "rc4-md5", new EncryptorInfo("ARC4-128", 16, 16, CIPHER_RC4) }
};
public static List SupportedCiphers()
@@ -39,7 +39,7 @@ namespace Shadowsocks.Encryption
return new List(_ciphers.Keys);
}
- protected override Dictionary> getCiphers()
+ protected override Dictionary getCiphers()
{
return _ciphers;
}
diff --git a/shadowsocks-csharp/Encryption/SodiumEncryptor.cs b/shadowsocks-csharp/Encryption/SodiumEncryptor.cs
index 476df828..afa794a9 100755
--- a/shadowsocks-csharp/Encryption/SodiumEncryptor.cs
+++ b/shadowsocks-csharp/Encryption/SodiumEncryptor.cs
@@ -24,13 +24,13 @@ namespace Shadowsocks.Encryption
{
}
- private static Dictionary> _ciphers = new Dictionary> {
- { "salsa20", new Dictionary { { "salsa20", new int[] { 32, 8, CIPHER_SALSA20 } } } },
- { "chacha20", new Dictionary { { "chacha20", new int[] { 32, 8, CIPHER_CHACHA20 } } } },
- { "chacha20-ietf", new Dictionary { { "chacha20-ietf", new int[] { 32, 12, CIPHER_CHACHA20_IETF } } } }
+ private static Dictionary _ciphers = new Dictionary {
+ { "salsa20", new EncryptorInfo("salsa20", 32, 8, CIPHER_SALSA20) },
+ { "chacha20", new EncryptorInfo("chacha20", 32, 8, CIPHER_CHACHA20) },
+ { "chacha20-ietf", new EncryptorInfo("chacha20-ietf", 32, 12, CIPHER_CHACHA20_IETF) }
};
- protected override Dictionary> getCiphers()
+ protected override Dictionary getCiphers()
{
return _ciphers;
}
diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs
index 010189ed..78e63238 100755
--- a/shadowsocks-csharp/Model/Configuration.cs
+++ b/shadowsocks-csharp/Model/Configuration.cs
@@ -26,9 +26,8 @@ namespace Shadowsocks.Model
public bool autoCheckUpdate;
public bool isVerboseLogging;
public LogViewerConfig logViewer;
- public bool useProxy;
- public string proxyServer;
- public int proxyPort;
+ public ProxyConfig proxy;
+ public HotkeyConfig hotkey;
private static string CONFIG_FILE = "gui-config.json";
@@ -58,6 +57,12 @@ namespace Shadowsocks.Model
config.localPort = 1080;
if (config.index == -1 && config.strategy == null)
config.index = 0;
+ if (config.logViewer == null)
+ config.logViewer = new LogViewerConfig();
+ if (config.proxy == null)
+ config.proxy = new ProxyConfig();
+ if (config.hotkey == null)
+ config.hotkey = new HotkeyConfig();
return config;
}
catch (Exception e)
@@ -98,7 +103,7 @@ namespace Shadowsocks.Model
}
catch (IOException e)
{
- Console.Error.WriteLine(e);
+ Logging.LogUsefulException(e);
}
}
diff --git a/shadowsocks-csharp/Model/HotKeyConfig.cs b/shadowsocks-csharp/Model/HotKeyConfig.cs
new file mode 100644
index 00000000..2e1d122c
--- /dev/null
+++ b/shadowsocks-csharp/Model/HotKeyConfig.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace Shadowsocks.Model
+{
+ /*
+ * Format:
+ * +
+ *
+ */
+
+ [Serializable]
+ public class HotkeyConfig
+ {
+ public string SwitchSystemProxy;
+ public string ChangeToPac;
+ public string ChangeToGlobal;
+ public string SwitchAllowLan;
+ public string ShowLogs;
+ public string ServerMoveUp;
+ public string ServerMoveDown;
+
+ public HotkeyConfig()
+ {
+ SwitchSystemProxy = "";
+ ChangeToPac = "";
+ ChangeToGlobal = "";
+ SwitchAllowLan = "";
+ ShowLogs = "";
+ ServerMoveUp = "";
+ ServerMoveDown = "";
+ }
+ }
+}
\ No newline at end of file
diff --git a/shadowsocks-csharp/Model/ProxyConfig.cs b/shadowsocks-csharp/Model/ProxyConfig.cs
new file mode 100644
index 00000000..0af17edb
--- /dev/null
+++ b/shadowsocks-csharp/Model/ProxyConfig.cs
@@ -0,0 +1,22 @@
+using Shadowsocks.View;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Shadowsocks.Model
+{
+ [Serializable]
+ public class ProxyConfig
+ {
+ public bool useProxy;
+ public string proxyServer;
+ public int proxyPort;
+
+ public ProxyConfig()
+ {
+ useProxy = false;
+ proxyServer = "";
+ proxyPort = 0;
+ }
+ }
+}
diff --git a/shadowsocks-csharp/Program.cs b/shadowsocks-csharp/Program.cs
index 5df1a2ac..99828ff1 100755
--- a/shadowsocks-csharp/Program.cs
+++ b/shadowsocks-csharp/Program.cs
@@ -13,7 +13,9 @@ namespace Shadowsocks
{
static class Program
{
- static ShadowsocksController controller;
+ private static ShadowsocksController _controller;
+ // XXX: Don't change this name
+ private static MenuViewController _viewController;
///
/// 应用程序的主入口点。
@@ -37,6 +39,7 @@ namespace Shadowsocks
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
+ Application.ApplicationExit += (sender, args) => HotKeys.Destroy();
if (!mutex.WaitOne(0, false))
{
@@ -61,9 +64,10 @@ namespace Shadowsocks
#else
Logging.OpenLogFile();
#endif
- controller = new ShadowsocksController();
- MenuViewController viewController = new MenuViewController(controller);
- controller.Start();
+ _controller = new ShadowsocksController();
+ _viewController = new MenuViewController(_controller);
+ HotKeys.Init();
+ _controller.Start();
Application.Run();
}
}
@@ -88,10 +92,10 @@ namespace Shadowsocks
{
case PowerModes.Resume:
Logging.Info("os wake up");
- controller?.Start();
+ _controller?.Start();
break;
case PowerModes.Suspend:
- controller?.Stop();
+ _controller?.Stop();
Logging.Info("os suspend");
break;
}
@@ -99,10 +103,10 @@ namespace Shadowsocks
private static void Application_ApplicationExit(object sender, EventArgs e)
{
- if (controller != null)
+ if (_controller != null)
{
- controller.Stop();
- controller = null;
+ _controller.Stop();
+ _controller = null;
}
}
}
diff --git a/shadowsocks-csharp/Util/Hotkeys.cs b/shadowsocks-csharp/Util/Hotkeys.cs
new file mode 100644
index 00000000..f5d8e3d8
--- /dev/null
+++ b/shadowsocks-csharp/Util/Hotkeys.cs
@@ -0,0 +1,174 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Windows.Forms;
+using System.Windows.Input;
+using GlobalHotKey;
+
+namespace Shadowsocks.Util
+{
+ public static class HotKeys
+ {
+ private static HotKeyManager _hotKeyManager;
+
+ public delegate void HotKeyCallBackHandler();
+ // map key and corresponding handler function
+ private static Dictionary _keymap = new Dictionary();
+
+ public static void Init()
+ {
+ _hotKeyManager = new HotKeyManager();
+ _hotKeyManager.KeyPressed += HotKeyManagerPressed;
+ }
+
+ public static void Destroy() => _hotKeyManager.Dispose();
+
+ private static void HotKeyManagerPressed(object sender, KeyPressedEventArgs e)
+ {
+ var hotkey = e.HotKey;
+ HotKeyCallBackHandler callback;
+ if (_keymap.TryGetValue(hotkey, out callback))
+ callback();
+ }
+
+ public static bool IsHotkeyExists( HotKey hotKey )
+ {
+ if (hotKey == null) throw new ArgumentNullException(nameof(hotKey));
+ return _keymap.Any( v => v.Key.Equals( hotKey ) );
+ }
+
+ public static bool IsCallbackExists( HotKeyCallBackHandler cb, out HotKey hotkey)
+ {
+ if (cb == null) throw new ArgumentNullException(nameof(cb));
+ try
+ {
+ var key = _keymap.First(x => x.Value == cb).Key;
+ hotkey = key;
+ return true;
+ }
+ catch (InvalidOperationException)
+ {
+ // not found
+ hotkey = null;
+ return false;
+ }
+ }
+ public static string HotKey2Str( HotKey key )
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ return HotKey2Str( key.Key, key.Modifiers );
+ }
+
+ public static string HotKey2Str( Key key, ModifierKeys modifier )
+ {
+ if (!Enum.IsDefined(typeof(Key), key))
+ throw new InvalidEnumArgumentException(nameof(key), (int) key, typeof(Key));
+ try
+ {
+ ModifierKeysConverter mkc = new ModifierKeysConverter();
+ var keyStr = Enum.GetName(typeof(Key), key);
+ var modifierStr = mkc.ConvertToInvariantString(modifier);
+
+ return $"{modifierStr}+{keyStr}";
+ }
+ catch (NotSupportedException)
+ {
+ // converter exception
+ return null;
+ }
+ }
+
+ public static HotKey Str2HotKey( string s ) {
+ try
+ {
+ if (s.IsNullOrEmpty()) return null;
+ int offset = s.LastIndexOf("+", StringComparison.OrdinalIgnoreCase);
+ if (offset <= 0) return null;
+ string modifierStr = s.Substring(0, offset).Trim();
+ string keyStr = s.Substring(offset + 1).Trim();
+
+ KeyConverter kc = new KeyConverter();
+ ModifierKeysConverter mkc = new ModifierKeysConverter();
+ Key key = (Key) kc.ConvertFrom(keyStr.ToUpper());
+ ModifierKeys modifier = (ModifierKeys) mkc.ConvertFrom(modifierStr.ToUpper());
+
+ return new HotKey(key, modifier);
+ }
+ catch (NotSupportedException)
+ {
+ // converter exception
+ return null;
+ }
+ catch (NullReferenceException)
+ {
+ return null;
+ }
+ }
+
+ public static bool Regist( HotKey key, HotKeyCallBackHandler callBack )
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
+ if (callBack == null)
+ throw new ArgumentNullException(nameof(callBack));
+ try
+ {
+ _hotKeyManager.Register(key);
+ _keymap[key] = callBack;
+ return true;
+ }
+ catch (ArgumentException)
+ {
+ // already called this method with the specific hotkey
+ // return success silently
+ return true;
+ }
+ catch (Win32Exception)
+ {
+ // this hotkey already registered by other programs
+ // notify user to change key
+ return false;
+ }
+ }
+
+ public static bool Regist(Key key, ModifierKeys modifiers, HotKeyCallBackHandler callBack)
+ {
+ if (!Enum.IsDefined(typeof(Key), key))
+ throw new InvalidEnumArgumentException(nameof(key), (int) key, typeof(Key));
+ try
+ {
+ var hotkey = _hotKeyManager.Register(key, modifiers);
+ _keymap[hotkey] = callBack;
+ return true;
+ }
+ catch (ArgumentException)
+ {
+ // already called this method with the specific hotkey
+ // return success silently
+ return true;
+ }
+ catch (Win32Exception)
+ {
+ // already registered by other programs
+ // notify user to change key
+ return false;
+ }
+ }
+
+ public static void UnRegist(HotKey key)
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
+ _hotKeyManager.Unregister(key);
+ if(_keymap.ContainsKey(key))
+ _keymap.Remove(key);
+ }
+
+ public static IEnumerable GetChildControls(this Control control) where TControl : Control
+ {
+ var children = control.Controls.Count > 0 ? control.Controls.OfType() : Enumerable.Empty();
+ return children.SelectMany(c => GetChildControls(c)).Concat(children);
+ }
+ }
+}
\ No newline at end of file
diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs
index cf4406b0..d59cfaf1 100755
--- a/shadowsocks-csharp/Util/Util.cs
+++ b/shadowsocks-csharp/Util/Util.cs
@@ -1,7 +1,9 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
+using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.Win32;
@@ -9,6 +11,20 @@ using Shadowsocks.Controller;
namespace Shadowsocks.Util
{
+ public struct BandwidthScaleInfo
+ {
+ public float value;
+ public string unit_name;
+ public long unit;
+
+ public BandwidthScaleInfo(float value, string unit_name, long unit)
+ {
+ this.value = value;
+ this.unit_name = unit_name;
+ this.unit = unit;
+ }
+ }
+
public class Utils
{
private static bool? _portableMode;
@@ -114,7 +130,7 @@ namespace Shadowsocks.Util
public static string FormatBandwidth(long n)
{
var result = GetBandwidthScale(n);
- return $"{result.Item1:0.##}{result.Item2}";
+ return $"{result.value:0.##}{result.unit_name}";
}
public static string FormatBytes(long bytes)
@@ -127,32 +143,32 @@ namespace Shadowsocks.Util
const long E = P * 1024L;
if (bytes >= P * 990)
- return (bytes / (double)E).ToString("F5") + "EB";
+ return (bytes / (double)E).ToString("F5") + "EiB";
if (bytes >= T * 990)
- return (bytes / (double)P).ToString("F5") + "PB";
+ return (bytes / (double)P).ToString("F5") + "PiB";
if (bytes >= G * 990)
- return (bytes / (double)T).ToString("F5") + "TB";
+ return (bytes / (double)T).ToString("F5") + "TiB";
if (bytes >= M * 990)
{
- return (bytes / (double)G).ToString("F4") + "GB";
+ return (bytes / (double)G).ToString("F4") + "GiB";
}
if (bytes >= M * 100)
{
- return (bytes / (double)M).ToString("F1") + "MB";
+ return (bytes / (double)M).ToString("F1") + "MiB";
}
if (bytes >= M * 10)
{
- return (bytes / (double)M).ToString("F2") + "MB";
+ return (bytes / (double)M).ToString("F2") + "MiB";
}
if (bytes >= K * 990)
{
- return (bytes / (double)M).ToString("F3") + "MB";
+ return (bytes / (double)M).ToString("F3") + "MiB";
}
if (bytes > K * 2)
{
- return (bytes / (double)K).ToString("F1") + "KB";
+ return (bytes / (double)K).ToString("F1") + "KiB";
}
- return bytes.ToString();
+ return bytes.ToString() + "B";
}
///
@@ -160,11 +176,9 @@ namespace Shadowsocks.Util
///
/// Raw bandwidth
///
- /// Item1: float, bandwidth with suitable scale (eg. 56)
- /// Item2: string, scale unit name (eg. KiB)
- /// Item3: long, scale unit (eg. 1024)
+ /// The BandwidthScaleInfo struct
///
- public static Tuple GetBandwidthScale(long n)
+ public static BandwidthScaleInfo GetBandwidthScale(long n)
{
long scale = 1;
float f = n;
@@ -193,7 +207,7 @@ namespace Shadowsocks.Util
scale <<= 10;
unit = "TiB";
}
- return new Tuple(f, unit, scale);
+ return new BandwidthScaleInfo(f, unit, scale);
}
public static RegistryKey OpenUserRegKey( string name, bool writable ) {
diff --git a/shadowsocks-csharp/View/HotkeySettingsForm.cs b/shadowsocks-csharp/View/HotkeySettingsForm.cs
new file mode 100644
index 00000000..bdd2beb3
--- /dev/null
+++ b/shadowsocks-csharp/View/HotkeySettingsForm.cs
@@ -0,0 +1,406 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Windows.Forms;
+
+using Shadowsocks.Controller;
+using Shadowsocks.Model;
+using Shadowsocks.Properties;
+using Shadowsocks.Util;
+
+namespace Shadowsocks.View
+{
+ public partial class HotkeySettingsForm : Form
+ {
+ private ShadowsocksController _controller;
+
+ // this is a copy of configuration that we are working on
+ private HotkeyConfig _modifiedConfig;
+
+ private StringBuilder _sb = new StringBuilder();
+
+ private IEnumerable _allTextBoxes;
+
+ private static Label _lb = null;
+ private static HotKeys.HotKeyCallBackHandler _callBack = null;
+
+ public HotkeySettingsForm(ShadowsocksController controller)
+ {
+ InitializeComponent();
+ UpdateTexts();
+ this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
+
+ _controller = controller;
+ _controller.ConfigChanged += controller_ConfigChanged;
+
+ LoadCurrentConfiguration();
+
+ // get all textboxes belong to this form
+ _allTextBoxes = HotKeys.GetChildControls(this.tableLayoutPanel1);
+ if (!_allTextBoxes.Any()) throw new Exception("Cannot get all textboxes");
+ }
+
+ private void controller_ConfigChanged(object sender, EventArgs e)
+ {
+ LoadCurrentConfiguration();
+ }
+
+ private void LoadCurrentConfiguration()
+ {
+ _modifiedConfig = _controller.GetConfigurationCopy().hotkey;
+ LoadConfiguration(_modifiedConfig);
+ }
+
+ private void LoadConfiguration(HotkeyConfig config)
+ {
+ SwitchSystemProxyTextBox.Text = config.SwitchSystemProxy;
+ ChangeToPacTextBox.Text = config.ChangeToPac;
+ ChangeToGlobalTextBox.Text = config.ChangeToGlobal;
+ SwitchAllowLanTextBox.Text = config.SwitchAllowLan;
+ ShowLogsTextBox.Text = config.ShowLogs;
+ ServerMoveUpTextBox.Text = config.ServerMoveUp;
+ ServerMoveDownTextBox.Text = config.ServerMoveDown;
+ }
+
+ private void UpdateTexts()
+ {
+ // I18N stuff
+ SwitchSystemProxyLabel.Text = I18N.GetString("Switch system proxy");
+ ChangeToPacLabel.Text = I18N.GetString("Switch to PAC mode");
+ ChangeToGlobalLabel.Text = I18N.GetString("Switch to Global mode");
+ SwitchAllowLanLabel.Text = I18N.GetString("Switch share over LAN");
+ ShowLogsLabel.Text = I18N.GetString("Show Logs");
+ ServerMoveUpLabel.Text = I18N.GetString("Switch to prev server");
+ ServerMoveDownLabel.Text = I18N.GetString("Switch to next server");
+ btnOK.Text = I18N.GetString("OK");
+ btnCancel.Text = I18N.GetString("Cancel");
+ btnRegisterAll.Text = I18N.GetString("Reg All");
+ this.Text = I18N.GetString("Edit Hotkeys...");
+ }
+
+ ///
+ /// Capture hotkey - Press key
+ ///
+ private void HotkeyDown(object sender, KeyEventArgs e)
+ {
+ _sb.Length = 0;
+ //Combination key only
+ if (e.Modifiers != 0)
+ {
+ // XXX: Hotkey parsing depends on the sequence, more specifically, ModifierKeysConverter.
+ // Windows key is reserved by operating system, we deny this key.
+ if (e.Control)
+ {
+ _sb.Append("Ctrl+");
+ }
+ if (e.Alt)
+ {
+ _sb.Append("Alt+");
+ }
+ if (e.Shift)
+ {
+ _sb.Append("Shift+");
+ }
+
+ Keys keyvalue = (Keys) e.KeyValue;
+ if ((keyvalue >= Keys.PageUp && keyvalue <= Keys.Down) ||
+ (keyvalue >= Keys.A && keyvalue <= Keys.Z) ||
+ (keyvalue >= Keys.F1 && keyvalue <= Keys.F12))
+ {
+ _sb.Append(e.KeyCode);
+ }
+ else if (keyvalue >= Keys.D0 && keyvalue <= Keys.D9)
+ {
+ _sb.Append('D').Append((char) e.KeyValue);
+ }
+ else if (keyvalue >= Keys.NumPad0 && keyvalue <= Keys.NumPad9)
+ {
+ _sb.Append("NumPad").Append((char) (e.KeyValue - 48));
+ }
+ }
+ ((TextBox) sender).Text = _sb.ToString();
+ }
+
+ ///
+ /// Capture hotkey - Release key
+ ///
+ private void HotkeyUp(object sender, KeyEventArgs e)
+ {
+ TextBox tb = sender as TextBox;
+ string content = tb.Text.TrimEnd();
+ if (content.Length >= 1 && content[content.Length - 1] == '+')
+ {
+ tb.Text = "";
+ }
+ }
+
+ private void TextBox_TextChanged(object sender, EventArgs e)
+ {
+ TextBox tb = sender as TextBox;
+
+ if (tb.Text == "")
+ {
+ // unreg
+ UnregHotkey(tb);
+ }
+ }
+
+ private void UnregHotkey(TextBox tb)
+ {
+
+ PrepareForHotkey(tb, out _callBack, out _lb);
+
+ UnregPrevHotkey(_callBack);
+ }
+
+ private void CancelButton_Click(object sender, EventArgs e)
+ {
+ this.Close();
+ }
+
+ private void OKButton_Click(object sender, EventArgs e)
+ {
+ // try to register, notify to change settings if failed
+ foreach (var tb in _allTextBoxes)
+ {
+ if (tb.Text.IsNullOrEmpty())
+ {
+ continue;
+ }
+ if (!TryRegHotkey(tb))
+ {
+ MessageBox.Show(I18N.GetString("Register hotkey failed"));
+ return;
+ }
+ }
+
+ // All check passed, saving
+ SaveConfig();
+ this.Close();
+ }
+
+ private void RegisterAllButton_Click(object sender, EventArgs e)
+ {
+ foreach (var tb in _allTextBoxes)
+ {
+ if (tb.Text.IsNullOrEmpty())
+ {
+ continue;
+ }
+ TryRegHotkey(tb);
+ }
+ }
+
+ private bool TryRegHotkey(TextBox tb)
+ {
+ var hotkey = HotKeys.Str2HotKey(tb.Text);
+ if (hotkey == null)
+ {
+ MessageBox.Show(string.Format(I18N.GetString("Cannot parse hotkey: {0}"), tb.Text));
+ tb.Clear();
+ return false;
+ }
+
+ PrepareForHotkey(tb, out _callBack, out _lb);
+
+ UnregPrevHotkey(_callBack);
+
+ // try to register keys
+ // if already registered by other progs
+ // notify to change
+
+ // use the corresponding label color to indicate
+ // reg result.
+ // Green: not occupied by others and operation succeed
+ // Yellow: already registered by other program and need action: disable by clear the content
+ // or change to another one
+ // Transparent without color: first run or empty config
+
+ bool regResult = HotKeys.Regist(hotkey, _callBack);
+ _lb.BackColor = regResult ? Color.Green : Color.Yellow;
+ return regResult;
+ }
+
+ private static void UnregPrevHotkey(HotKeys.HotKeyCallBackHandler cb)
+ {
+ GlobalHotKey.HotKey prevHotKey;
+ if (HotKeys.IsCallbackExists(cb, out prevHotKey))
+ {
+ // unregister previous one
+ HotKeys.UnRegist(prevHotKey);
+ }
+ }
+
+ private void SaveConfig()
+ {
+ _modifiedConfig.SwitchSystemProxy = SwitchSystemProxyTextBox.Text;
+ _modifiedConfig.ChangeToPac = ChangeToPacTextBox.Text;
+ _modifiedConfig.ChangeToGlobal = ChangeToGlobalTextBox.Text;
+ _modifiedConfig.SwitchAllowLan = SwitchAllowLanTextBox.Text;
+ _modifiedConfig.ShowLogs = ShowLogsTextBox.Text;
+ _modifiedConfig.ServerMoveUp = ServerMoveUpTextBox.Text;
+ _modifiedConfig.ServerMoveDown = ServerMoveDownTextBox.Text;
+ _controller.SaveHotkeyConfig(_modifiedConfig);
+ }
+
+ #region Callbacks
+
+ private void SwitchSystemProxyCallback()
+ {
+ bool enabled = _controller.GetConfigurationCopy().enabled;
+ _controller.ToggleEnable(!enabled);
+ }
+
+ private void ChangeToPacCallback()
+ {
+ bool enabled = _controller.GetConfigurationCopy().enabled;
+ if (enabled == false) return;
+ _controller.ToggleGlobal(false);
+ }
+
+ private void ChangeToGlobalCallback()
+ {
+ bool enabled = _controller.GetConfigurationCopy().enabled;
+ if (enabled == false) return;
+ _controller.ToggleGlobal(true);
+ }
+
+ private void SwitchAllowLanCallback()
+ {
+ var status = _controller.GetConfigurationCopy().shareOverLan;
+ _controller.ToggleShareOverLAN(!status);
+ }
+
+ private void ShowLogsCallback()
+ {
+ // Get the current MenuViewController in this program via reflection
+ FieldInfo fi = Assembly.GetExecutingAssembly().GetType("Shadowsocks.Program")
+ .GetField("_viewController",
+ BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.IgnoreCase);
+ // To retrieve the value of a static field, pass null here
+ var mvc = fi.GetValue(null) as MenuViewController;
+ mvc.ShowLogForm_HotKey();
+ }
+
+ private void ServerMoveUpCallback()
+ {
+ int currIndex;
+ int serverCount;
+ GetCurrServerInfo(out currIndex, out serverCount);
+ if (currIndex - 1 < 0)
+ {
+ // revert to last server
+ currIndex = serverCount - 1;
+ }
+ else
+ {
+ currIndex -= 1;
+ }
+ _controller.SelectServerIndex(currIndex);
+ }
+
+ private void ServerMoveDownCallback()
+ {
+ int currIndex;
+ int serverCount;
+ GetCurrServerInfo(out currIndex, out serverCount);
+ if (currIndex + 1 == serverCount)
+ {
+ // revert to first server
+ currIndex = 0;
+ }
+ else
+ {
+ currIndex += 1;
+ }
+ _controller.SelectServerIndex(currIndex);
+ }
+
+ private void GetCurrServerInfo(out int currIndex, out int serverCount)
+ {
+ var currConfig = _controller.GetCurrentConfiguration();
+ currIndex = currConfig.index;
+ serverCount = currConfig.configs.Count;
+ }
+
+ #endregion
+
+ #region Prepare hotkey
+
+ ///
+ /// Find correct callback and corresponding label
+ ///
+ ///
+ ///
+ ///
+ private void PrepareForHotkey(TextBox tb, out HotKeys.HotKeyCallBackHandler cb, out Label lb)
+ {
+ /*
+ * XXX: The labelName, TextBoxName and callbackName
+ * must follow this rule to make use of reflection
+ *
+ *
+ */
+ if (tb == null)
+ throw new ArgumentNullException(nameof(tb));
+
+ var pos = tb.Name.LastIndexOf("TextBox", StringComparison.OrdinalIgnoreCase);
+ var rawName = tb.Name.Substring(0, pos);
+ var labelName = rawName + "Label";
+ var callbackName = rawName + "Callback";
+
+ var callback = GetDelegateViaMethodName(this.GetType(), callbackName);
+ if (callback == null)
+ {
+ throw new Exception($"{callbackName} not found");
+ }
+ cb = callback as HotKeys.HotKeyCallBackHandler;
+
+ object label = GetFieldViaName(this.GetType(), labelName, this);
+ if (label == null)
+ {
+ throw new Exception($"{labelName} not found");
+ }
+ lb = label as Label;
+ }
+
+ ///
+ ///
+ ///
+ /// from which type
+ /// field name
+ /// pass null if static field
+ ///
+ private static object GetFieldViaName(Type type, string name, object obj)
+ {
+ if (type == null) throw new ArgumentNullException(nameof(type));
+ if (name.IsNullOrEmpty()) throw new ArgumentException(nameof(name));
+ // In general, TextBoxes and Labels are private
+ FieldInfo fi = type.GetField(name,
+ BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Static);
+ return fi == null ? null : fi.GetValue(obj);
+ }
+
+ ///
+ /// Create hotkey callback handler delegate based on callback name
+ ///
+ ///
+ ///
+ ///
+ private Delegate GetDelegateViaMethodName(Type type, string methodname)
+ {
+ if (type == null) throw new ArgumentNullException(nameof(type));
+ if (methodname.IsNullOrEmpty()) throw new ArgumentException(nameof(methodname));
+ //HotkeySettingsForm form = new HotkeySettingsForm(_controller);
+ Type delegateType = Type.GetType("Shadowsocks.Util.HotKeys").GetNestedType("HotKeyCallBackHandler");
+ MethodInfo dynMethod = type.GetMethod(methodname,
+ BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase);
+ return dynMethod == null ? null : Delegate.CreateDelegate(delegateType, this, dynMethod);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/shadowsocks-csharp/View/HotkeySettingsForm.designer.cs b/shadowsocks-csharp/View/HotkeySettingsForm.designer.cs
new file mode 100644
index 00000000..5367fc88
--- /dev/null
+++ b/shadowsocks-csharp/View/HotkeySettingsForm.designer.cs
@@ -0,0 +1,354 @@
+namespace Shadowsocks.View
+{
+ partial class HotkeySettingsForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
+ this.btnOK = new System.Windows.Forms.Button();
+ this.btnCancel = new System.Windows.Forms.Button();
+ this.btnRegisterAll = new System.Windows.Forms.Button();
+ this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
+ this.SwitchSystemProxyLabel = new System.Windows.Forms.Label();
+ this.ChangeToPacLabel = new System.Windows.Forms.Label();
+ this.ChangeToGlobalLabel = new System.Windows.Forms.Label();
+ this.SwitchAllowLanLabel = new System.Windows.Forms.Label();
+ this.ShowLogsLabel = new System.Windows.Forms.Label();
+ this.ServerMoveUpLabel = new System.Windows.Forms.Label();
+ this.ServerMoveDownLabel = new System.Windows.Forms.Label();
+ this.SwitchSystemProxyTextBox = new System.Windows.Forms.TextBox();
+ this.ChangeToPacTextBox = new System.Windows.Forms.TextBox();
+ this.ChangeToGlobalTextBox = new System.Windows.Forms.TextBox();
+ this.SwitchAllowLanTextBox = new System.Windows.Forms.TextBox();
+ this.ShowLogsTextBox = new System.Windows.Forms.TextBox();
+ this.ServerMoveUpTextBox = new System.Windows.Forms.TextBox();
+ this.ServerMoveDownTextBox = new System.Windows.Forms.TextBox();
+ flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
+ flowLayoutPanel1.SuspendLayout();
+ this.tableLayoutPanel1.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // flowLayoutPanel1
+ //
+ this.tableLayoutPanel1.SetColumnSpan(flowLayoutPanel1, 2);
+ flowLayoutPanel1.Controls.Add(this.btnOK);
+ flowLayoutPanel1.Controls.Add(this.btnCancel);
+ flowLayoutPanel1.Controls.Add(this.btnRegisterAll);
+ flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.BottomUp;
+ flowLayoutPanel1.Location = new System.Drawing.Point(0, 227);
+ flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0);
+ flowLayoutPanel1.Name = "flowLayoutPanel1";
+ flowLayoutPanel1.Padding = new System.Windows.Forms.Padding(0, 0, 16, 0);
+ flowLayoutPanel1.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
+ flowLayoutPanel1.Size = new System.Drawing.Size(475, 44);
+ flowLayoutPanel1.TabIndex = 6;
+ //
+ // btnOK
+ //
+ this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
+ this.btnOK.Location = new System.Drawing.Point(381, 10);
+ this.btnOK.Name = "btnOK";
+ this.btnOK.Size = new System.Drawing.Size(75, 31);
+ this.btnOK.TabIndex = 0;
+ this.btnOK.Text = "OK";
+ this.btnOK.UseVisualStyleBackColor = true;
+ this.btnOK.Click += new System.EventHandler(this.OKButton_Click);
+ //
+ // btnCancel
+ //
+ this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.btnCancel.Location = new System.Drawing.Point(300, 10);
+ this.btnCancel.Name = "btnCancel";
+ this.btnCancel.Size = new System.Drawing.Size(75, 31);
+ this.btnCancel.TabIndex = 1;
+ this.btnCancel.Text = "Cancel";
+ this.btnCancel.UseVisualStyleBackColor = true;
+ this.btnCancel.Click += new System.EventHandler(this.CancelButton_Click);
+ //
+ // btnRegisterAll
+ //
+ this.btnRegisterAll.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.btnRegisterAll.Location = new System.Drawing.Point(219, 10);
+ this.btnRegisterAll.Name = "btnRegisterAll";
+ this.btnRegisterAll.Size = new System.Drawing.Size(75, 31);
+ this.btnRegisterAll.TabIndex = 2;
+ this.btnRegisterAll.Text = "Reg All";
+ this.btnRegisterAll.UseVisualStyleBackColor = true;
+ this.btnRegisterAll.Click += new System.EventHandler(this.RegisterAllButton_Click);
+ //
+ // tableLayoutPanel1
+ //
+ this.tableLayoutPanel1.ColumnCount = 2;
+ this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
+ this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
+ this.tableLayoutPanel1.Controls.Add(this.SwitchSystemProxyLabel, 0, 0);
+ this.tableLayoutPanel1.Controls.Add(this.ChangeToPacLabel, 0, 1);
+ this.tableLayoutPanel1.Controls.Add(this.ChangeToGlobalLabel, 0, 2);
+ this.tableLayoutPanel1.Controls.Add(this.SwitchAllowLanLabel, 0, 3);
+ this.tableLayoutPanel1.Controls.Add(this.ShowLogsLabel, 0, 4);
+ this.tableLayoutPanel1.Controls.Add(this.ServerMoveUpLabel, 0, 5);
+ this.tableLayoutPanel1.Controls.Add(this.ServerMoveDownLabel, 0, 6);
+ this.tableLayoutPanel1.Controls.Add(flowLayoutPanel1, 0, 7);
+ this.tableLayoutPanel1.Controls.Add(this.SwitchSystemProxyTextBox, 1, 0);
+ this.tableLayoutPanel1.Controls.Add(this.ChangeToPacTextBox, 1, 1);
+ this.tableLayoutPanel1.Controls.Add(this.ChangeToGlobalTextBox, 1, 2);
+ this.tableLayoutPanel1.Controls.Add(this.SwitchAllowLanTextBox, 1, 3);
+ this.tableLayoutPanel1.Controls.Add(this.ShowLogsTextBox, 1, 4);
+ this.tableLayoutPanel1.Controls.Add(this.ServerMoveUpTextBox, 1, 5);
+ this.tableLayoutPanel1.Controls.Add(this.ServerMoveDownTextBox, 1, 6);
+ this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
+ this.tableLayoutPanel1.Name = "tableLayoutPanel1";
+ this.tableLayoutPanel1.RowCount = 8;
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.77778F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.38889F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
+ this.tableLayoutPanel1.Size = new System.Drawing.Size(475, 271);
+ this.tableLayoutPanel1.TabIndex = 0;
+ //
+ // SwitchSystemProxyLabel
+ //
+ this.SwitchSystemProxyLabel.AutoSize = true;
+ this.SwitchSystemProxyLabel.Dock = System.Windows.Forms.DockStyle.Right;
+ this.SwitchSystemProxyLabel.Location = new System.Drawing.Point(25, 0);
+ this.SwitchSystemProxyLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
+ this.SwitchSystemProxyLabel.Name = "SwitchSystemProxyLabel";
+ this.SwitchSystemProxyLabel.Size = new System.Drawing.Size(147, 32);
+ this.SwitchSystemProxyLabel.TabIndex = 0;
+ this.SwitchSystemProxyLabel.Text = "Enable System Proxy";
+ this.SwitchSystemProxyLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // ChangeToPacLabel
+ //
+ this.ChangeToPacLabel.AutoSize = true;
+ this.ChangeToPacLabel.Dock = System.Windows.Forms.DockStyle.Right;
+ this.ChangeToPacLabel.Location = new System.Drawing.Point(135, 32);
+ this.ChangeToPacLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
+ this.ChangeToPacLabel.Name = "ChangeToPacLabel";
+ this.ChangeToPacLabel.Size = new System.Drawing.Size(37, 32);
+ this.ChangeToPacLabel.TabIndex = 1;
+ this.ChangeToPacLabel.Text = "PAC";
+ this.ChangeToPacLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // ChangeToGlobalLabel
+ //
+ this.ChangeToGlobalLabel.AutoSize = true;
+ this.ChangeToGlobalLabel.Dock = System.Windows.Forms.DockStyle.Right;
+ this.ChangeToGlobalLabel.Location = new System.Drawing.Point(119, 64);
+ this.ChangeToGlobalLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
+ this.ChangeToGlobalLabel.Name = "ChangeToGlobalLabel";
+ this.ChangeToGlobalLabel.Size = new System.Drawing.Size(53, 32);
+ this.ChangeToGlobalLabel.TabIndex = 2;
+ this.ChangeToGlobalLabel.Text = "Global";
+ this.ChangeToGlobalLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // SwitchAllowLanLabel
+ //
+ this.SwitchAllowLanLabel.AutoSize = true;
+ this.SwitchAllowLanLabel.Dock = System.Windows.Forms.DockStyle.Right;
+ this.SwitchAllowLanLabel.Location = new System.Drawing.Point(8, 96);
+ this.SwitchAllowLanLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
+ this.SwitchAllowLanLabel.Name = "SwitchAllowLanLabel";
+ this.SwitchAllowLanLabel.Size = new System.Drawing.Size(164, 32);
+ this.SwitchAllowLanLabel.TabIndex = 3;
+ this.SwitchAllowLanLabel.Text = "Allow Clients from LAN";
+ this.SwitchAllowLanLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // ShowLogsLabel
+ //
+ this.ShowLogsLabel.AutoSize = true;
+ this.ShowLogsLabel.Dock = System.Windows.Forms.DockStyle.Right;
+ this.ShowLogsLabel.Location = new System.Drawing.Point(82, 128);
+ this.ShowLogsLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
+ this.ShowLogsLabel.Name = "ShowLogsLabel";
+ this.ShowLogsLabel.Size = new System.Drawing.Size(90, 32);
+ this.ShowLogsLabel.TabIndex = 4;
+ this.ShowLogsLabel.Text = "Show Logs...";
+ this.ShowLogsLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // ServerMoveUpLabel
+ //
+ this.ServerMoveUpLabel.AutoSize = true;
+ this.ServerMoveUpLabel.Dock = System.Windows.Forms.DockStyle.Right;
+ this.ServerMoveUpLabel.Location = new System.Drawing.Point(103, 160);
+ this.ServerMoveUpLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
+ this.ServerMoveUpLabel.Name = "ServerMoveUpLabel";
+ this.ServerMoveUpLabel.Size = new System.Drawing.Size(69, 34);
+ this.ServerMoveUpLabel.TabIndex = 4;
+ this.ServerMoveUpLabel.Text = "Move up";
+ this.ServerMoveUpLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // ServerMoveDownLabel
+ //
+ this.ServerMoveDownLabel.AutoSize = true;
+ this.ServerMoveDownLabel.Dock = System.Windows.Forms.DockStyle.Right;
+ this.ServerMoveDownLabel.Location = new System.Drawing.Point(81, 194);
+ this.ServerMoveDownLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
+ this.ServerMoveDownLabel.Name = "ServerMoveDownLabel";
+ this.ServerMoveDownLabel.Size = new System.Drawing.Size(91, 33);
+ this.ServerMoveDownLabel.TabIndex = 4;
+ this.ServerMoveDownLabel.Text = "Move Down";
+ this.ServerMoveDownLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // SwitchSystemProxyTextBox
+ //
+ this.SwitchSystemProxyTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.SwitchSystemProxyTextBox.Location = new System.Drawing.Point(183, 3);
+ this.SwitchSystemProxyTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
+ this.SwitchSystemProxyTextBox.Name = "SwitchSystemProxyTextBox";
+ this.SwitchSystemProxyTextBox.ReadOnly = true;
+ this.SwitchSystemProxyTextBox.Size = new System.Drawing.Size(276, 25);
+ this.SwitchSystemProxyTextBox.TabIndex = 7;
+ this.SwitchSystemProxyTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
+ this.SwitchSystemProxyTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
+ this.SwitchSystemProxyTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
+ //
+ // ChangeToPacTextBox
+ //
+ this.ChangeToPacTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.ChangeToPacTextBox.Location = new System.Drawing.Point(183, 35);
+ this.ChangeToPacTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
+ this.ChangeToPacTextBox.Name = "ChangeToPacTextBox";
+ this.ChangeToPacTextBox.ReadOnly = true;
+ this.ChangeToPacTextBox.Size = new System.Drawing.Size(276, 25);
+ this.ChangeToPacTextBox.TabIndex = 8;
+ this.ChangeToPacTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
+ this.ChangeToPacTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
+ this.ChangeToPacTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
+ //
+ // ChangeToGlobalTextBox
+ //
+ this.ChangeToGlobalTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.ChangeToGlobalTextBox.Location = new System.Drawing.Point(183, 67);
+ this.ChangeToGlobalTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
+ this.ChangeToGlobalTextBox.Name = "ChangeToGlobalTextBox";
+ this.ChangeToGlobalTextBox.ReadOnly = true;
+ this.ChangeToGlobalTextBox.Size = new System.Drawing.Size(276, 25);
+ this.ChangeToGlobalTextBox.TabIndex = 9;
+ this.ChangeToGlobalTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
+ this.ChangeToGlobalTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
+ this.ChangeToGlobalTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
+ //
+ // SwitchAllowLanTextBox
+ //
+ this.SwitchAllowLanTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.SwitchAllowLanTextBox.Location = new System.Drawing.Point(183, 99);
+ this.SwitchAllowLanTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
+ this.SwitchAllowLanTextBox.Name = "SwitchAllowLanTextBox";
+ this.SwitchAllowLanTextBox.ReadOnly = true;
+ this.SwitchAllowLanTextBox.Size = new System.Drawing.Size(276, 25);
+ this.SwitchAllowLanTextBox.TabIndex = 10;
+ this.SwitchAllowLanTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
+ this.SwitchAllowLanTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
+ this.SwitchAllowLanTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
+ //
+ // ShowLogsTextBox
+ //
+ this.ShowLogsTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.ShowLogsTextBox.Location = new System.Drawing.Point(183, 131);
+ this.ShowLogsTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
+ this.ShowLogsTextBox.Name = "ShowLogsTextBox";
+ this.ShowLogsTextBox.ReadOnly = true;
+ this.ShowLogsTextBox.Size = new System.Drawing.Size(276, 25);
+ this.ShowLogsTextBox.TabIndex = 11;
+ this.ShowLogsTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
+ this.ShowLogsTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
+ this.ShowLogsTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
+ //
+ // ServerMoveUpTextBox
+ //
+ this.ServerMoveUpTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.ServerMoveUpTextBox.Location = new System.Drawing.Point(183, 163);
+ this.ServerMoveUpTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
+ this.ServerMoveUpTextBox.Name = "ServerMoveUpTextBox";
+ this.ServerMoveUpTextBox.ReadOnly = true;
+ this.ServerMoveUpTextBox.Size = new System.Drawing.Size(276, 25);
+ this.ServerMoveUpTextBox.TabIndex = 12;
+ this.ServerMoveUpTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
+ this.ServerMoveUpTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
+ this.ServerMoveUpTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
+ //
+ // ServerMoveDownTextBox
+ //
+ this.ServerMoveDownTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.ServerMoveDownTextBox.Location = new System.Drawing.Point(183, 197);
+ this.ServerMoveDownTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
+ this.ServerMoveDownTextBox.Name = "ServerMoveDownTextBox";
+ this.ServerMoveDownTextBox.ReadOnly = true;
+ this.ServerMoveDownTextBox.Size = new System.Drawing.Size(276, 25);
+ this.ServerMoveDownTextBox.TabIndex = 13;
+ this.ServerMoveDownTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
+ this.ServerMoveDownTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
+ this.ServerMoveDownTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
+ //
+ // HotkeySettingsForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 19F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(475, 271);
+ this.Controls.Add(this.tableLayoutPanel1);
+ this.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+ this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "HotkeySettingsForm";
+ this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Edit Hotkeys...";
+ flowLayoutPanel1.ResumeLayout(false);
+ this.tableLayoutPanel1.ResumeLayout(false);
+ this.tableLayoutPanel1.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+ private System.Windows.Forms.Label SwitchSystemProxyLabel;
+ private System.Windows.Forms.Label ChangeToPacLabel;
+ private System.Windows.Forms.Label ChangeToGlobalLabel;
+ private System.Windows.Forms.Label SwitchAllowLanLabel;
+ private System.Windows.Forms.Label ShowLogsLabel;
+ private System.Windows.Forms.Label ServerMoveUpLabel;
+ private System.Windows.Forms.Label ServerMoveDownLabel;
+ private System.Windows.Forms.Button btnOK;
+ private System.Windows.Forms.Button btnCancel;
+ private System.Windows.Forms.TextBox ShowLogsTextBox;
+ private System.Windows.Forms.TextBox SwitchAllowLanTextBox;
+ private System.Windows.Forms.TextBox ChangeToGlobalTextBox;
+ private System.Windows.Forms.TextBox ChangeToPacTextBox;
+ private System.Windows.Forms.TextBox SwitchSystemProxyTextBox;
+ private System.Windows.Forms.TextBox ServerMoveUpTextBox;
+ private System.Windows.Forms.TextBox ServerMoveDownTextBox;
+ private System.Windows.Forms.Button btnRegisterAll;
+ private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
+ }
+}
\ No newline at end of file
diff --git a/shadowsocks-csharp/View/HotkeySettingsForm.resx b/shadowsocks-csharp/View/HotkeySettingsForm.resx
new file mode 100644
index 00000000..f5b19983
--- /dev/null
+++ b/shadowsocks-csharp/View/HotkeySettingsForm.resx
@@ -0,0 +1,183 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
\ No newline at end of file
diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs
index a77a961e..c29ff82d 100644
--- a/shadowsocks-csharp/View/LogForm.cs
+++ b/shadowsocks-csharp/View/LogForm.cs
@@ -10,9 +10,22 @@ using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Model;
using Shadowsocks.Util;
+using System.Text;
namespace Shadowsocks.View
{
+ struct TrafficInfo
+ {
+ public long inbound;
+ public long outbound;
+
+ public TrafficInfo(long inbound, long outbound)
+ {
+ this.inbound = inbound;
+ this.outbound = outbound;
+ }
+ }
+
public partial class LogForm : Form
{
long lastOffset;
@@ -23,7 +36,7 @@ namespace Shadowsocks.View
#region chart
long lastMaxSpeed;
- public ShadowsocksController.QueueLast> traffic = new ShadowsocksController.QueueLast>();
+ ShadowsocksController.QueueLast traffic = new ShadowsocksController.QueueLast();
#endregion
public LogForm(ShadowsocksController controller, string filename)
@@ -34,19 +47,13 @@ namespace Shadowsocks.View
Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
- if (config == null)
- {
- config = new LogViewerConfig();
- }
- else
- {
- topMostTrigger = config.topMost;
- wrapTextTrigger = config.wrapText;
- toolbarTrigger = config.toolbarShown;
- LogMessageTextBox.BackColor = config.GetBackgroundColor();
- LogMessageTextBox.ForeColor = config.GetTextColor();
- LogMessageTextBox.Font = config.GetFont();
- }
+
+ topMostTrigger = config.topMost;
+ wrapTextTrigger = config.wrapText;
+ toolbarTrigger = config.toolbarShown;
+ LogMessageTextBox.BackColor = config.GetBackgroundColor();
+ LogMessageTextBox.ForeColor = config.GetTextColor();
+ LogMessageTextBox.Font = config.GetFont();
controller.TrafficChanged += controller_TrafficChanged;
@@ -59,7 +66,7 @@ namespace Shadowsocks.View
List outboundPoints = new List();
TextAnnotation inboundAnnotation = new TextAnnotation();
TextAnnotation outboundAnnotation = new TextAnnotation();
- Tuple bandwidthScale;
+ BandwidthScaleInfo bandwidthScale;
const long minScale = 50;
long maxSpeed = 0;
long lastInbound, lastOutbound;
@@ -70,12 +77,12 @@ namespace Shadowsocks.View
return;
foreach (var trafficPerSecond in traffic)
{
- inboundPoints.Add(trafficPerSecond.Item1);
- outboundPoints.Add(trafficPerSecond.Item2);
- maxSpeed = Math.Max(maxSpeed, Math.Max(trafficPerSecond.Item1, trafficPerSecond.Item2));
+ inboundPoints.Add(trafficPerSecond.inbound);
+ outboundPoints.Add(trafficPerSecond.outbound);
+ maxSpeed = Math.Max(maxSpeed, Math.Max(trafficPerSecond.inbound, trafficPerSecond.outbound));
}
- lastInbound = traffic.Last().Item1;
- lastOutbound = traffic.Last().Item2;
+ lastInbound = traffic.Last().inbound;
+ lastOutbound = traffic.Last().outbound;
}
if (maxSpeed > 0)
@@ -92,15 +99,15 @@ namespace Shadowsocks.View
bandwidthScale = Utils.GetBandwidthScale(maxSpeed);
//rescale the original data points, since it is List, .ForEach does not work
- inboundPoints = inboundPoints.Select(p => p / bandwidthScale.Item3).ToList();
- outboundPoints = outboundPoints.Select(p => p / bandwidthScale.Item3).ToList();
+ inboundPoints = inboundPoints.Select(p => p / bandwidthScale.unit).ToList();
+ outboundPoints = outboundPoints.Select(p => p / bandwidthScale.unit).ToList();
if (trafficChart.IsHandleCreated)
{
trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints);
trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints);
- trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.Item2;
- trafficChart.ChartAreas[0].AxisY.Maximum = bandwidthScale.Item1;
+ trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.unit_name;
+ trafficChart.ChartAreas[0].AxisY.Maximum = bandwidthScale.value;
inboundAnnotation.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last();
inboundAnnotation.Text = Utils.FormatBandwidth(lastInbound);
outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last();
@@ -115,10 +122,10 @@ namespace Shadowsocks.View
{
lock (this)
{
- traffic = new ShadowsocksController.QueueLast>();
+ traffic = new ShadowsocksController.QueueLast();
foreach (var trafficPerSecond in controller.traffic)
{
- traffic.Enqueue(new Tuple(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement));
+ traffic.Enqueue(new TrafficInfo(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement));
}
}
}
@@ -162,9 +169,11 @@ namespace Shadowsocks.View
}
string line = "";
+ StringBuilder appendText = new StringBuilder(1024);
while ((line = reader.ReadLine()) != null)
- LogMessageTextBox.AppendText(line + Environment.NewLine);
+ appendText.Append(line + Environment.NewLine);
+ LogMessageTextBox.AppendText(appendText.ToString());
LogMessageTextBox.ScrollToCaret();
lastOffset = reader.BaseStream.Position;
@@ -182,14 +191,16 @@ namespace Shadowsocks.View
string line = "";
bool changed = false;
+ StringBuilder appendText = new StringBuilder(128);
while ((line = reader.ReadLine()) != null)
{
changed = true;
- LogMessageTextBox.AppendText(line + Environment.NewLine);
+ appendText.Append(line + Environment.NewLine);
}
if (changed)
{
+ LogMessageTextBox.AppendText(appendText.ToString());
LogMessageTextBox.ScrollToCaret();
}
@@ -214,8 +225,7 @@ namespace Shadowsocks.View
timer.Start();
LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
- if (config == null)
- config = new LogViewerConfig();
+
Height = config.height;
Width = config.width;
Top = config.GetBestTop();
@@ -241,8 +251,7 @@ namespace Shadowsocks.View
timer.Stop();
controller.TrafficChanged -= controller_TrafficChanged;
LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
- if (config == null)
- config = new LogViewerConfig();
+
config.topMost = topMostTrigger;
config.wrapText = wrapTextTrigger;
config.toolbarShown = toolbarTrigger;
diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs
index db0aef08..4aa270b1 100644
--- a/shadowsocks-csharp/View/MenuViewController.cs
+++ b/shadowsocks-csharp/View/MenuViewController.cs
@@ -49,11 +49,12 @@ namespace Shadowsocks.View
private MenuItem editOnlinePACItem;
private MenuItem autoCheckUpdatesToggleItem;
private MenuItem proxyItem;
+ private MenuItem hotKeyItem;
private MenuItem VerboseLoggingToggleItem;
private ConfigForm configForm;
private ProxyForm proxyForm;
- private List logForms = new List();
- private bool logFormsVisible = false;
+ private LogForm logForm;
+ private HotkeySettingsForm hotkeySettingsForm;
private string _urlToOpen;
public MenuViewController(ShadowsocksController controller)
@@ -133,6 +134,8 @@ namespace Shadowsocks.View
MessageBox.Show(e.GetException().ToString(), String.Format(I18N.GetString("Shadowsocks Error: {0}"), e.GetException().Message));
}
+ #region Tray Icon
+
private void UpdateTrayIcon()
{
int dpi;
@@ -177,10 +180,10 @@ namespace Shadowsocks.View
}
// we want to show more details but notify icon title is limited to 63 characters
string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" +
- (enabled ?
- I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) :
- String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running
- + "\n" + serverInfo;
+ (enabled ?
+ I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) :
+ String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running
+ + "\n" + serverInfo;
_notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length));
}
@@ -201,7 +204,7 @@ namespace Shadowsocks.View
else if (global)
{
Color flyBlue = Color.FromArgb(25, 125, 191);
- // Muliply with flyBlue
+ // Multiply with flyBlue
int red = color.R * flyBlue.R / 255;
int green = color.G * flyBlue.G / 255;
int blue = color.B * flyBlue.B / 255;
@@ -230,6 +233,10 @@ namespace Shadowsocks.View
return bitmap;
}
+ #endregion
+
+ #region MenuItems and MenuGroups
+
private MenuItem CreateMenuItem(string text, EventHandler click)
{
return new MenuItem(I18N.GetString(text), click);
@@ -271,6 +278,7 @@ namespace Shadowsocks.View
new MenuItem("-"),
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
this.VerboseLoggingToggleItem = CreateMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ),
+ this.hotKeyItem = CreateMenuItem("Edit Hotkeys...", new EventHandler(this.hotKeyItem_Click)),
CreateMenuGroup("Updates...", new MenuItem[] {
CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)),
new MenuItem("-"),
@@ -282,6 +290,8 @@ namespace Shadowsocks.View
});
}
+ #endregion
+
private void controller_ConfigChanged(object sender, EventArgs e)
{
LoadCurrentConfiguration();
@@ -406,7 +416,7 @@ namespace Shadowsocks.View
i++;
}
- // user want a seperator item between strategy and servers menugroup
+ // user wants a seperator item between strategy and servers menugroup
items.Add( i++, new MenuItem("-") );
int strategyCount = i;
@@ -459,31 +469,40 @@ namespace Shadowsocks.View
}
}
- private void ShowLogForms()
+ private void ShowHotKeySettingsForm()
{
- if (logForms.Count == 0)
+ if (hotkeySettingsForm != null)
+ {
+ hotkeySettingsForm.Activate();
+ }
+ else
{
- LogForm f = new LogForm(controller, Logging.LogFilePath);
- f.Show();
- f.Activate();
- f.FormClosed += logForm_FormClosed;
+ hotkeySettingsForm = new HotkeySettingsForm(controller);
+ hotkeySettingsForm.Show();
+ hotkeySettingsForm.Activate();
+ hotkeySettingsForm.FormClosed += hotkeySettingsForm_FormClosed;
+ }
+ }
- logForms.Add(f);
- logFormsVisible = true;
+ private void ShowLogForm()
+ {
+ if (logForm != null)
+ {
+ logForm.Activate();
}
else
{
- logFormsVisible = !logFormsVisible;
- foreach (LogForm f in logForms)
- {
- f.Visible = logFormsVisible;
- }
+ logForm = new LogForm(controller, Logging.LogFilePath);
+ logForm.Show();
+ logForm.Activate();
+ logForm.FormClosed += logForm_FormClosed;
}
}
void logForm_FormClosed(object sender, FormClosedEventArgs e)
{
- logForms.Remove((LogForm)sender);
+ logForm = null;
+ Utils.ReleaseMemory(true);
}
void configForm_FormClosed(object sender, FormClosedEventArgs e)
@@ -504,6 +523,12 @@ namespace Shadowsocks.View
Utils.ReleaseMemory(true);
}
+ void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e)
+ {
+ hotkeySettingsForm = null;
+ Utils.ReleaseMemory(true);
+ }
+
private void Config_Click(object sender, EventArgs e)
{
ShowConfigForm();
@@ -519,11 +544,9 @@ namespace Shadowsocks.View
private void CheckUpdateForFirstRun()
{
Configuration config = controller.GetConfigurationCopy();
- if (!config.isDefault)
- {
- _isStartupChecking = true;
- updateChecker.CheckUpdate(config, 3000);
- }
+ if (config.isDefault) return;
+ _isStartupChecking = true;
+ updateChecker.CheckUpdate(config, 3000);
}
private void ShowFirstTimeBalloon()
@@ -547,7 +570,7 @@ namespace Shadowsocks.View
}
else if (e.Button == MouseButtons.Middle)
{
- ShowLogForms();
+ ShowLogForm();
}
}
@@ -607,15 +630,6 @@ namespace Shadowsocks.View
controller.SelectStrategy((string)item.Tag);
}
- private void ShowLogItem_Click(object sender, EventArgs e)
- {
- LogForm f = new LogForm(controller, Logging.LogFilePath);
- f.Show();
- f.FormClosed += logForm_FormClosed;
-
- logForms.Add(f);
- }
-
private void VerboseLoggingToggleItem_Click( object sender, EventArgs e ) {
VerboseLoggingToggleItem.Checked = ! VerboseLoggingToggleItem.Checked;
controller.ToggleVerboseLogging( VerboseLoggingToggleItem.Checked );
@@ -820,5 +834,20 @@ namespace Shadowsocks.View
{
ShowProxyForm();
}
+
+ private void hotKeyItem_Click(object sender, EventArgs e)
+ {
+ ShowHotKeySettingsForm();
+ }
+
+ private void ShowLogItem_Click(object sender, EventArgs e)
+ {
+ ShowLogForm();
+ }
+
+ public void ShowLogForm_HotKey()
+ {
+ ShowLogForm();
+ }
}
}
diff --git a/shadowsocks-csharp/View/ProxyForm.cs b/shadowsocks-csharp/View/ProxyForm.cs
index 4f2a900d..0f780c88 100644
--- a/shadowsocks-csharp/View/ProxyForm.cs
+++ b/shadowsocks-csharp/View/ProxyForm.cs
@@ -1,116 +1,125 @@
-using System;
-using System.Drawing;
-using System.Windows.Forms;
-using Shadowsocks.Controller;
-using Shadowsocks.Model;
-using Shadowsocks.Properties;
-
-namespace Shadowsocks.View
-{
- public partial class ProxyForm : Form
- {
- private ShadowsocksController controller;
-
- // this is a copy of configuration that we are working on
- private Configuration _modifiedConfiguration;
-
- public ProxyForm(ShadowsocksController controller)
- {
- this.Font = System.Drawing.SystemFonts.MessageBoxFont;
- InitializeComponent();
-
- UpdateTexts();
- this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
-
- this.controller = controller;
- controller.ConfigChanged += controller_ConfigChanged;
-
- UpdateEnabled();
- LoadCurrentConfiguration();
- }
-
- private void UpdateTexts()
- {
- UseProxyCheckBox.Text = I18N.GetString("Use Proxy");
- ProxyAddrLabel.Text = I18N.GetString("Proxy Addr");
- ProxyPortLable.Text = I18N.GetString("Proxy Port");
- OKButton.Text = I18N.GetString("OK");
- MyCancelButton.Text = I18N.GetString("Cancel");
- this.Text = I18N.GetString("Edit Proxy");
- }
-
- private void controller_ConfigChanged(object sender, EventArgs e)
- {
- LoadCurrentConfiguration();
- }
-
- private void LoadCurrentConfiguration()
- {
- _modifiedConfiguration = controller.GetConfigurationCopy();
-
- UseProxyCheckBox.Checked = _modifiedConfiguration.useProxy;
- ProxyServerTextBox.Text = _modifiedConfiguration.proxyServer;
- ProxyPortTextBox.Text = _modifiedConfiguration.proxyPort.ToString();
- }
-
- private void OKButton_Click(object sender, EventArgs e)
- {
- if (UseProxyCheckBox.Checked)
- {
- try
- {
- var proxy = ProxyServerTextBox.Text;
- var port = int.Parse(ProxyPortTextBox.Text);
- Configuration.CheckServer(proxy);
- Configuration.CheckPort(port);
-
- controller.EnableProxy(proxy, port);
- }
- catch (FormatException)
- {
- MessageBox.Show(I18N.GetString("Illegal port number format"));
- return;
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.Message);
- return;
- }
- }
- else
- {
- controller.DisableProxy();
- }
- this.Close();
- }
-
- private void CancelButton_Click(object sender, EventArgs e)
- {
- this.Close();
- }
-
- private void ProxyForm_FormClosed(object sender, FormClosedEventArgs e)
- {
- controller.ConfigChanged -= controller_ConfigChanged;
- }
-
- private void UseProxyCheckBox_CheckedChanged(object sender, EventArgs e)
- {
- UpdateEnabled();
- }
-
- private void UpdateEnabled()
- {
- if (UseProxyCheckBox.Checked)
- {
- ProxyServerTextBox.Enabled = true;
- ProxyPortTextBox.Enabled = true;
- }
- else
- {
- ProxyServerTextBox.Enabled = false;
- ProxyPortTextBox.Enabled = false;
- }
- }
- }
-}
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using Shadowsocks.Controller;
+using Shadowsocks.Model;
+using Shadowsocks.Properties;
+
+namespace Shadowsocks.View
+{
+ public partial class ProxyForm : Form
+ {
+ private ShadowsocksController controller;
+
+ // this is a copy of configuration that we are working on
+ private ProxyConfig _modifiedConfiguration;
+
+ public ProxyForm(ShadowsocksController controller)
+ {
+ this.Font = System.Drawing.SystemFonts.MessageBoxFont;
+ InitializeComponent();
+
+ UpdateTexts();
+ this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
+
+ this.controller = controller;
+ controller.ConfigChanged += controller_ConfigChanged;
+
+ UpdateEnabled();
+ LoadCurrentConfiguration();
+ }
+
+ private void UpdateTexts()
+ {
+ UseProxyCheckBox.Text = I18N.GetString("Use Proxy");
+ ProxyAddrLabel.Text = I18N.GetString("Proxy Addr");
+ ProxyPortLable.Text = I18N.GetString("Proxy Port");
+ OKButton.Text = I18N.GetString("OK");
+ MyCancelButton.Text = I18N.GetString("Cancel");
+ this.Text = I18N.GetString("Edit Proxy");
+ }
+
+ private void controller_ConfigChanged(object sender, EventArgs e)
+ {
+ LoadCurrentConfiguration();
+ }
+
+ private void LoadCurrentConfiguration()
+ {
+ _modifiedConfiguration = controller.GetConfigurationCopy().proxy;
+ UseProxyCheckBox.Checked = _modifiedConfiguration.useProxy;
+ ProxyServerTextBox.Text = _modifiedConfiguration.proxyServer;
+ ProxyPortTextBox.Text = _modifiedConfiguration.proxyPort.ToString();
+ }
+
+ private void OKButton_Click(object sender, EventArgs e)
+ {
+ if (UseProxyCheckBox.Checked)
+ {
+ try
+ {
+ var proxy = ProxyServerTextBox.Text;
+ var port = int.Parse(ProxyPortTextBox.Text);
+ Configuration.CheckServer(proxy);
+ Configuration.CheckPort(port);
+
+ controller.EnableProxy(proxy, port);
+ }
+ catch (FormatException)
+ {
+ MessageBox.Show(I18N.GetString("Illegal port number format"));
+ return;
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.Message);
+ return;
+ }
+ }
+ else
+ {
+ controller.DisableProxy();
+ }
+
+ _modifiedConfiguration.useProxy = UseProxyCheckBox.Checked;
+ _modifiedConfiguration.proxyServer = ProxyServerTextBox.Text;
+ var tmpProxyPort = 0;
+ int.TryParse(ProxyPortTextBox.Text, out tmpProxyPort);
+ _modifiedConfiguration.proxyPort = tmpProxyPort;
+ controller.SaveProxyConfig(_modifiedConfiguration);
+
+ this.Close();
+ }
+
+ private void CancelButton_Click(object sender, EventArgs e)
+ {
+ this.Close();
+ }
+
+ private void ProxyForm_FormClosed(object sender, FormClosedEventArgs e)
+ {
+ controller.ConfigChanged -= controller_ConfigChanged;
+ }
+
+ private void UseProxyCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+ UpdateEnabled();
+ }
+
+ private void UpdateEnabled()
+ {
+ if (UseProxyCheckBox.Checked)
+ {
+ ProxyServerTextBox.Enabled = true;
+ ProxyPortTextBox.Enabled = true;
+ }
+ else
+ {
+ ProxyServerTextBox.Clear();
+ ProxyPortTextBox.Clear();
+ ProxyServerTextBox.Enabled = false;
+ ProxyPortTextBox.Enabled = false;
+ }
+ }
+ }
+}
diff --git a/shadowsocks-csharp/packages.config b/shadowsocks-csharp/packages.config
index ea2a7bce..8b1d7274 100644
--- a/shadowsocks-csharp/packages.config
+++ b/shadowsocks-csharp/packages.config
@@ -3,6 +3,7 @@
+
\ No newline at end of file
diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj
index 46a5c89b..44240bae 100644
--- a/shadowsocks-csharp/shadowsocks-csharp.csproj
+++ b/shadowsocks-csharp/shadowsocks-csharp.csproj
@@ -65,6 +65,10 @@
app.manifest
+
+ 3rd\GlobalHotKey.1.1.0\lib\GlobalHotKey.dll
+ True
+
@@ -139,6 +143,8 @@
+
+
@@ -177,6 +183,7 @@
+
@@ -200,6 +207,12 @@
CalculationControl.cs
+
+ Form
+
+
+ HotkeySettingsForm.cs
+
Form
@@ -240,6 +253,9 @@
CalculationControl.cs
+
+ HotkeySettingsForm.cs
+
LogForm.cs
diff --git a/test/UnitTest.cs b/test/UnitTest.cs
index 6fa4f21d..6364dba2 100755
--- a/test/UnitTest.cs
+++ b/test/UnitTest.cs
@@ -2,6 +2,9 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shadowsocks.Controller;
using Shadowsocks.Encryption;
+using Shadowsocks.Util;
+using GlobalHotKey;
+using System.Windows.Input;
using System.Threading;
using System.Collections.Generic;
@@ -22,6 +25,32 @@ namespace test
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3.2", "1.3.1") > 0);
}
+ [ TestMethod ]
+ public void TestHotKey2Str() {
+ Assert.AreEqual( "Ctrl+A", HotKeys.HotKey2Str( Key.A, ModifierKeys.Control ) );
+ Assert.AreEqual( "Ctrl+Alt+D2", HotKeys.HotKey2Str( Key.D2, (ModifierKeys.Alt | ModifierKeys.Control) ) );
+ Assert.AreEqual("Ctrl+Alt+Shift+NumPad7", HotKeys.HotKey2Str(Key.NumPad7, (ModifierKeys.Alt|ModifierKeys.Control|ModifierKeys.Shift)));
+ Assert.AreEqual( "Ctrl+Alt+Shift+F6", HotKeys.HotKey2Str( Key.F6, (ModifierKeys.Alt|ModifierKeys.Control|ModifierKeys.Shift)));
+ Assert.AreNotEqual("Ctrl+Shift+Alt+F6", HotKeys.HotKey2Str(Key.F6, (ModifierKeys.Alt | ModifierKeys.Control | ModifierKeys.Shift)));
+ }
+
+ [TestMethod]
+ public void TestStr2HotKey()
+ {
+ Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+A").Equals(new HotKey(Key.A, ModifierKeys.Control)));
+ Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+Alt+A").Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Alt))));
+ Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+Shift+A").Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Shift))));
+ Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+Alt+Shift+A").Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Alt| ModifierKeys.Shift))));
+ HotKey testKey0 = HotKeys.Str2HotKey("Ctrl+Alt+Shift+A");
+ Assert.IsTrue(testKey0 != null && testKey0.Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift))));
+ HotKey testKey1 = HotKeys.Str2HotKey("Ctrl+Alt+Shift+F2");
+ Assert.IsTrue(testKey1 != null && testKey1.Equals(new HotKey(Key.F2, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift))));
+ HotKey testKey2 = HotKeys.Str2HotKey("Ctrl+Shift+Alt+D7");
+ Assert.IsTrue(testKey2 != null && testKey2.Equals(new HotKey(Key.D7, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift))));
+ HotKey testKey3 = HotKeys.Str2HotKey("Ctrl+Shift+Alt+NumPad7");
+ Assert.IsTrue(testKey3 != null && testKey3.Equals(new HotKey(Key.NumPad7, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift))));
+ }
+
[TestMethod]
public void TestMD5()
{
diff --git a/test/test.csproj b/test/test.csproj
index d1c3fa2e..2d261cfb 100755
--- a/test/test.csproj
+++ b/test/test.csproj
@@ -35,10 +35,15 @@
+
+ ..\shadowsocks-csharp\3rd\GlobalHotKey.1.1.0\lib\GlobalHotKey.dll
+ True
+
+