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 + +