@@ -1,6 +1,6 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<configuration> | |||||
<config> | |||||
<add key="repositoryPath" value="shadowsocks-csharp\3rd" /> | |||||
</config> | |||||
</configuration> | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<configuration> | |||||
<config> | |||||
<add key="repositoryPath" value="shadowsocks-csharp\3rd" /> | |||||
</config> | |||||
</configuration> |
@@ -35,10 +35,7 @@ namespace Shadowsocks.Controller | |||||
|| (length < 2 || firstPacket[0] != 5)) | || (length < 2 || firstPacket[0] != 5)) | ||||
return false; | return false; | ||||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | 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<TCPHandler> handlersToClose = new List<TCPHandler>(); | IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | ||||
lock (Handlers) | 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 RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth | ||||
public static readonly int BufferSize = RecvSize + RecvReserveSize + 32; | 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 _startConnectTime; | ||||
private DateTime _startReceivingTime; | private DateTime _startReceivingTime; | ||||
private DateTime _startSendingTime; | 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._config = config; | ||||
this._tcprelay = tcprelay; | |||||
this._connection = socket; | |||||
} | } | ||||
public void CreateRemote() | 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 == "") | if (server == null || server.server == "") | ||||
throw new ArgumentException("No server configured"); | throw new ArgumentException("No server configured"); | ||||
lock (_encryptionLock) | lock (_encryptionLock) | ||||
{ | { | ||||
lock (_decryptionLock) | 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) | public void Start(byte[] firstPacket, int length) | ||||
@@ -210,18 +206,19 @@ namespace Shadowsocks.Controller | |||||
public void Close() | public void Close() | ||||
{ | { | ||||
lock (tcprelay.Handlers) | |||||
lock (this) | |||||
{ | { | ||||
tcprelay.Handlers.Remove(this); | |||||
} | |||||
lock (this) { | |||||
if (_closed) return; | if (_closed) return; | ||||
_closed = true; | _closed = true; | ||||
} | } | ||||
lock (_tcprelay.Handlers) | |||||
{ | |||||
_tcprelay.Handlers.Remove(this); | |||||
} | |||||
try | try | ||||
{ | { | ||||
connection?.Shutdown(SocketShutdown.Both); | |||||
connection?.Close(); | |||||
_connection?.Shutdown(SocketShutdown.Both); | |||||
_connection?.Close(); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -241,7 +238,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
lock (_decryptionLock) | lock (_decryptionLock) | ||||
{ | { | ||||
encryptor?.Dispose(); | |||||
_encryptor?.Dispose(); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -261,7 +258,7 @@ namespace Shadowsocks.Controller | |||||
response = new byte[] { 0, 91 }; | response = new byte[] { 0, 91 }; | ||||
Logging.Error("socks 5 protocol error"); | 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 | else | ||||
Close(); | Close(); | ||||
@@ -278,7 +275,7 @@ namespace Shadowsocks.Controller | |||||
if (_closed) return; | if (_closed) return; | ||||
try | try | ||||
{ | { | ||||
connection.EndSend(ar); | |||||
_connection.EndSend(ar); | |||||
// +-----+-----+-------+------+----------+----------+ | // +-----+-----+-------+------+----------+----------+ | ||||
// | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | | // | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | | ||||
@@ -287,7 +284,7 @@ namespace Shadowsocks.Controller | |||||
// +-----+-----+-------+------+----------+----------+ | // +-----+-----+-------+------+----------+----------+ | ||||
// Skip first 3 bytes | // Skip first 3 bytes | ||||
// TODO validate | // 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) | catch (Exception e) | ||||
{ | { | ||||
@@ -301,14 +298,14 @@ namespace Shadowsocks.Controller | |||||
if (_closed) return; | if (_closed) return; | ||||
try | try | ||||
{ | { | ||||
int bytesRead = connection.EndReceive(ar); | |||||
int bytesRead = _connection.EndReceive(ar); | |||||
if (bytesRead >= 3) | if (bytesRead >= 3) | ||||
{ | { | ||||
_command = _connetionRecvBuffer[1]; | _command = _connetionRecvBuffer[1]; | ||||
if (_command == 1) | if (_command == 1) | ||||
{ | { | ||||
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; | 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) | else if (_command == 3) | ||||
HandleUDPAssociate(); | HandleUDPAssociate(); | ||||
@@ -328,7 +325,7 @@ namespace Shadowsocks.Controller | |||||
private void HandleUDPAssociate() | private void HandleUDPAssociate() | ||||
{ | { | ||||
IPEndPoint endPoint = (IPEndPoint)connection.LocalEndPoint; | |||||
IPEndPoint endPoint = (IPEndPoint)_connection.LocalEndPoint; | |||||
byte[] address = endPoint.Address.GetAddressBytes(); | byte[] address = endPoint.Address.GetAddressBytes(); | ||||
int port = endPoint.Port; | int port = endPoint.Port; | ||||
byte[] response = new byte[4 + address.Length + 2]; | byte[] response = new byte[4 + address.Length + 2]; | ||||
@@ -345,7 +342,7 @@ namespace Shadowsocks.Controller | |||||
address.CopyTo(response, 4); | address.CopyTo(response, 4); | ||||
response[response.Length - 1] = (byte)(port & 0xFF); | response[response.Length - 1] = (byte)(port & 0xFF); | ||||
response[response.Length - 2] = (byte)((port >> 8) & 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) | private void ReadAll(IAsyncResult ar) | ||||
@@ -355,15 +352,15 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
if (ar.AsyncState != null) | 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 | else | ||||
{ | { | ||||
int bytesRead = connection.EndReceive(ar); | |||||
int bytesRead = _connection.EndReceive(ar); | |||||
if (bytesRead > 0) | 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 | else | ||||
Close(); | Close(); | ||||
@@ -380,7 +377,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
connection?.EndSend(ar); | |||||
_connection?.EndSend(ar); | |||||
StartConnect(); | StartConnect(); | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
@@ -420,10 +417,10 @@ namespace Shadowsocks.Controller | |||||
// Setting up proxy | // Setting up proxy | ||||
IProxy remote; | IProxy remote; | ||||
EndPoint proxyEP; | EndPoint proxyEP; | ||||
if (_config.useProxy) | |||||
if (_config.proxy.useProxy) | |||||
{ | { | ||||
remote = new Socks5Proxy(); | remote = new Socks5Proxy(); | ||||
proxyEP = SocketUtil.GetEndPoint(_config.proxyServer, _config.proxyPort); | |||||
proxyEP = SocketUtil.GetEndPoint(_config.proxy.proxyServer, _config.proxy.proxyPort); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -440,8 +437,8 @@ namespace Shadowsocks.Controller | |||||
proxyTimer.Enabled = true; | proxyTimer.Enabled = true; | ||||
proxyTimer.Session = session; | 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; | _proxyConnected = false; | ||||
@@ -542,7 +539,7 @@ namespace Shadowsocks.Controller | |||||
var session = timer.Session; | var session = timer.Session; | ||||
Server server = timer.Server; | Server server = timer.Server; | ||||
IStrategy strategy = controller.GetCurrentStrategy(); | |||||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||||
strategy?.SetFailure(server); | strategy?.SetFailure(server); | ||||
Logging.Info($"{server.FriendlyName()} timed out"); | Logging.Info($"{server.FriendlyName()} timed out"); | ||||
session.Remote.Close(); | session.Remote.Close(); | ||||
@@ -568,7 +565,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
var session = (AsyncSession<ServerTimer>) ar.AsyncState; | var session = (AsyncSession<ServerTimer>) ar.AsyncState; | ||||
ServerTimer timer = session.State; | ServerTimer timer = session.State; | ||||
server = timer.Server; | |||||
_server = timer.Server; | |||||
timer.Elapsed -= destConnectTimer_Elapsed; | timer.Elapsed -= destConnectTimer_Elapsed; | ||||
timer.Enabled = false; | timer.Enabled = false; | ||||
timer.Dispose(); | timer.Dispose(); | ||||
@@ -581,13 +578,13 @@ namespace Shadowsocks.Controller | |||||
if (_config.isVerboseLogging) | 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; | 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); | StartPipe(session); | ||||
} | } | ||||
@@ -596,10 +593,10 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | 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); | Logging.LogUsefulException(e); | ||||
RetryConnect(); | RetryConnect(); | ||||
@@ -613,7 +610,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
_startReceivingTime = DateTime.Now; | _startReceivingTime = DateTime.Now; | ||||
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session); | 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<bool>(session, true) /* to tell the callback this is the first time reading packet, and we haven't found the header yet. */); | new AsyncSession<bool>(session, true) /* to tell the callback this is the first time reading packet, and we haven't found the header yet. */); | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
@@ -631,7 +628,7 @@ namespace Shadowsocks.Controller | |||||
var session = (AsyncSession) ar.AsyncState; | var session = (AsyncSession) ar.AsyncState; | ||||
int bytesRead = session.Remote.EndReceive(ar); | int bytesRead = session.Remote.EndReceive(ar); | ||||
_totalRead += bytesRead; | _totalRead += bytesRead; | ||||
_tcprelay.UpdateInboundCounter(server, bytesRead); | |||||
_tcprelay.UpdateInboundCounter(_server, bytesRead); | |||||
if (bytesRead > 0) | if (bytesRead > 0) | ||||
{ | { | ||||
lastActivity = DateTime.Now; | lastActivity = DateTime.Now; | ||||
@@ -639,15 +636,15 @@ namespace Shadowsocks.Controller | |||||
lock (_decryptionLock) | lock (_decryptionLock) | ||||
{ | { | ||||
if (_closed) return; | 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 | else | ||||
{ | { | ||||
connection.Shutdown(SocketShutdown.Send); | |||||
_connection.Shutdown(SocketShutdown.Send); | |||||
_connectionShutdown = true; | _connectionShutdown = true; | ||||
CheckClose(); | CheckClose(); | ||||
} | } | ||||
@@ -664,8 +661,8 @@ namespace Shadowsocks.Controller | |||||
if (_closed) return; | if (_closed) return; | ||||
try | try | ||||
{ | { | ||||
if(connection == null) return; | |||||
int bytesRead = connection.EndReceive(ar); | |||||
if(_connection == null) return; | |||||
int bytesRead = _connection.EndReceive(ar); | |||||
_totalWrite += bytesRead; | _totalWrite += bytesRead; | ||||
var session = (AsyncSession<bool>) ar.AsyncState; | var session = (AsyncSession<bool>) ar.AsyncState; | ||||
@@ -713,14 +710,13 @@ namespace Shadowsocks.Controller | |||||
lock (_encryptionLock) | lock (_encryptionLock) | ||||
{ | { | ||||
if (_closed) return; | 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; | _startSendingTime = DateTime.Now; | ||||
_bytesToSend = bytesToSend; | |||||
remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session); | 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 | else | ||||
{ | { | ||||
@@ -743,7 +739,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
var session = (AsyncSession)ar.AsyncState; | var session = (AsyncSession)ar.AsyncState; | ||||
session.Remote.EndSend(ar); | 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) | catch (Exception e) | ||||
{ | { | ||||
@@ -758,7 +754,7 @@ namespace Shadowsocks.Controller | |||||
try | try | ||||
{ | { | ||||
var session = (AsyncSession)ar.AsyncState; | var session = (AsyncSession)ar.AsyncState; | ||||
connection?.EndSend(ar); | |||||
_connection?.EndSend(ar); | |||||
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session); | session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session); | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
@@ -217,15 +217,15 @@ namespace Shadowsocks.Controller | |||||
public void DisableProxy() | public void DisableProxy() | ||||
{ | { | ||||
_config.useProxy = false; | |||||
_config.proxy.useProxy = false; | |||||
SaveConfig(_config); | SaveConfig(_config); | ||||
} | } | ||||
public void EnableProxy(string proxy, int port) | 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); | SaveConfig(_config); | ||||
} | } | ||||
@@ -348,12 +348,40 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
_config.autoCheckUpdate = enabled; | _config.autoCheckUpdate = enabled; | ||||
Configuration.Save(_config); | Configuration.Save(_config); | ||||
if (ConfigChanged != null) | |||||
{ | |||||
ConfigChanged(this, new EventArgs()); | |||||
} | |||||
} | } | ||||
public void SaveLogViewerConfig(LogViewerConfig newConfig) | public void SaveLogViewerConfig(LogViewerConfig newConfig) | ||||
{ | { | ||||
_config.logViewer = newConfig; | _config.logViewer = newConfig; | ||||
Configuration.Save(_config); | 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) | public void UpdateLatency(Server server, TimeSpan latency) | ||||
@@ -547,6 +575,8 @@ namespace Shadowsocks.Controller | |||||
File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8); | File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8); | ||||
} | } | ||||
#region Memory Management | |||||
private void StartReleasingMemory() | private void StartReleasingMemory() | ||||
{ | { | ||||
_ramThread = new Thread(new ThreadStart(ReleaseMemory)); | _ramThread = new Thread(new ThreadStart(ReleaseMemory)); | ||||
@@ -563,6 +593,10 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
#endregion | |||||
#region Traffic Statistics | |||||
private void StartTrafficStatistics(int queueMaxSize) | private void StartTrafficStatistics(int queueMaxSize) | ||||
{ | { | ||||
traffic = new QueueLast<TrafficPerSecond>(); | traffic = new QueueLast<TrafficPerSecond>(); | ||||
@@ -597,5 +631,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
#endregion | |||||
} | } | ||||
} | } |
@@ -27,6 +27,7 @@ Verbose Logging=详细记录日志 | |||||
Updates...=更新... | Updates...=更新... | ||||
Check for Updates...=检查更新 | Check for Updates...=检查更新 | ||||
Check for Updates at Startup=启动时检查更新 | Check for Updates at Startup=启动时检查更新 | ||||
Edit Hotkeys...=编辑快捷键... | |||||
About...=关于... | About...=关于... | ||||
Quit=退出 | Quit=退出 | ||||
Edit Servers=编辑服务器 | Edit Servers=编辑服务器 | ||||
@@ -84,6 +85,17 @@ Edit Online PAC URL=编辑在线 PAC 网址 | |||||
Edit Online PAC URL...=编辑在线 PAC 网址... | Edit Online PAC URL...=编辑在线 PAC 网址... | ||||
Please input 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 | # Messages | ||||
Shadowsocks Error: {0}=Shadowsocks 错误: {0} | 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。 | Unsupported operating system, use Windows Vista at least.=不支持的操作系统版本,最低需求为Windows Vista。 | ||||
Proxy request failed=代理请求失败 | Proxy request failed=代理请求失败 | ||||
Proxy handshake failed=代理握手失败 | Proxy handshake failed=代理握手失败 | ||||
Register hotkey failed=注册热键失败 | |||||
Cannot parse hotkey: {0}=解析热键失败: {0} |
@@ -27,6 +27,7 @@ Verbose Logging=詳細記錄日誌 | |||||
Updates...=更新... | Updates...=更新... | ||||
Check for Updates...=檢查更新 | Check for Updates...=檢查更新 | ||||
Check for Updates at Startup=啟動時檢查更新 | Check for Updates at Startup=啟動時檢查更新 | ||||
Edit Hotkeys...=編輯熱鍵... | |||||
About...=關於... | About...=關於... | ||||
Quit=退出 | Quit=退出 | ||||
Edit Servers=編輯伺服器 | Edit Servers=編輯伺服器 | ||||
@@ -84,12 +85,23 @@ Edit Online PAC URL=編輯在線 PAC 網址 | |||||
Edit Online PAC URL...=編輯在線 PAC 網址... | Edit Online PAC URL...=編輯在線 PAC 網址... | ||||
Please input 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 | # Messages | ||||
Shadowsocks Error: {0}=Shadowsocks 錯誤: {0} | Shadowsocks Error: {0}=Shadowsocks 錯誤: {0} | ||||
Port already in use=連接埠號碼已被占用 | Port already in use=連接埠號碼已被占用 | ||||
Illegal port number format=非法連接埠號碼格式 | Illegal port number format=非法連接埠號碼格式 | ||||
Please add at least one server=請新增至少壹個伺服器 | |||||
Please add at least one server=請新增至少一個伺服器 | |||||
Server IP can not be blank=伺服器 IP 不能為空 | Server IP can not be blank=伺服器 IP 不能為空 | ||||
Password can not be blank=密碼不能為空 | Password can not be blank=密碼不能為空 | ||||
Port out of range=連接埠號碼超出範圍 | 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 碼,嘗試把它放大或移動到靠近屏幕中間的位置 | No QRCode found. Try to zoom in or move it to the center of the screen.=未發現 QR 碼,嘗試把它放大或移動到靠近屏幕中間的位置 | ||||
Shadowsocks is already running.=Shadowsocks 已經在運行。 | Shadowsocks is already running.=Shadowsocks 已經在運行。 | ||||
Find Shadowsocks icon in your notify tray.=請在工作列裏尋找 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 decode QRCode=無法解析 QR 碼 | ||||
Failed to update registry=無法修改登錄檔 | Failed to update registry=無法修改登錄檔 | ||||
System Proxy On: =系統代理已啟用: | 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。 | Unsupported operating system, use Windows Vista at least.=不支持的作業系統版本,最低需求為Windows Vista。 | ||||
Proxy request failed=代理請求失敗 | Proxy request failed=代理請求失敗 | ||||
Proxy handshake failed=代理握手失敗 | Proxy handshake failed=代理握手失敗 | ||||
Register hotkey failed=註冊熱鍵失敗 | |||||
Cannot parse hotkey: {0}=解析熱鍵失敗: {0} |
@@ -2,6 +2,22 @@ | |||||
namespace Shadowsocks.Encryption | 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 | public abstract class EncryptorBase | ||||
: IEncryptor | : IEncryptor | ||||
{ | { | ||||
@@ -16,8 +16,7 @@ namespace Shadowsocks.Encryption | |||||
protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE]; | protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE]; | ||||
protected Dictionary<string, Dictionary<string, int[]>> ciphers; | |||||
protected Dictionary<string, int[]> ciphersDetail; | |||||
protected Dictionary<string, EncryptorInfo> ciphers; | |||||
private static readonly ConcurrentDictionary<string, byte[]> CachedKeys = new ConcurrentDictionary<string, byte[]>(); | private static readonly ConcurrentDictionary<string, byte[]> CachedKeys = new ConcurrentDictionary<string, byte[]>(); | ||||
protected byte[] _encryptIV; | protected byte[] _encryptIV; | ||||
@@ -28,7 +27,7 @@ namespace Shadowsocks.Encryption | |||||
protected int _cipher; | protected int _cipher; | ||||
// cipher name in MbedTLS, useless when using LibSodium | // cipher name in MbedTLS, useless when using LibSodium | ||||
protected string _cipherMbedName; | protected string _cipherMbedName; | ||||
protected int[] _cipherInfo; | |||||
protected EncryptorInfo _cipherInfo; | |||||
protected byte[] _key; | protected byte[] _key; | ||||
protected int keyLen; | protected int keyLen; | ||||
protected int ivLen; | protected int ivLen; | ||||
@@ -39,7 +38,7 @@ namespace Shadowsocks.Encryption | |||||
InitKey(method, password); | InitKey(method, password); | ||||
} | } | ||||
protected abstract Dictionary<string, Dictionary<string, int[]>> getCiphers(); | |||||
protected abstract Dictionary<string, EncryptorInfo> getCiphers(); | |||||
private void InitKey(string method, string password) | private void InitKey(string method, string password) | ||||
{ | { | ||||
@@ -47,16 +46,15 @@ namespace Shadowsocks.Encryption | |||||
_method = method; | _method = method; | ||||
string k = method + ":" + password; | string k = method + ":" + password; | ||||
ciphers = getCiphers(); | 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) | if (_cipher == 0) | ||||
{ | { | ||||
throw new Exception("method not found"); | 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) => | _key = CachedKeys.GetOrAdd(k, (nk) => | ||||
{ | { | ||||
byte[] passbuf = Encoding.UTF8.GetBytes(password); | byte[] passbuf = Encoding.UTF8.GetBytes(password); | ||||
@@ -20,18 +20,18 @@ namespace Shadowsocks.Encryption | |||||
{ | { | ||||
} | } | ||||
private static Dictionary<string, Dictionary<string, int[]>> _ciphers = new Dictionary<string, Dictionary<string, int[]>> { | |||||
{ "aes-128-cfb", new Dictionary<string, int[]> { { "AES-128-CFB128", new int[] { 16, 16, CIPHER_AES } } } }, | |||||
{ "aes-192-cfb", new Dictionary<string, int[]> { { "AES-192-CFB128", new int[] { 24, 16, CIPHER_AES } } } }, | |||||
{ "aes-256-cfb", new Dictionary<string, int[]> { { "AES-256-CFB128", new int[] { 32, 16, CIPHER_AES } } } }, | |||||
{ "aes-128-ctr", new Dictionary<string, int[]> { { "AES-128-CTR", new int[] { 16, 16, CIPHER_AES } } } }, | |||||
{ "aes-192-ctr", new Dictionary<string, int[]> { { "AES-192-CTR", new int[] { 24, 16, CIPHER_AES } } } }, | |||||
{ "aes-256-ctr", new Dictionary<string, int[]> { { "AES-256-CTR", new int[] { 32, 16, CIPHER_AES } } } }, | |||||
{ "bf-cfb", new Dictionary<string, int[]> { { "BLOWFISH-CFB64", new int[] { 16, 8, CIPHER_BLOWFISH } } } }, | |||||
{ "camellia-128-cfb", new Dictionary<string, int[]> { { "CAMELLIA-128-CFB128", new int[] { 16, 16, CIPHER_CAMELLIA } } } }, | |||||
{ "camellia-192-cfb", new Dictionary<string, int[]> { { "CAMELLIA-192-CFB128", new int[] { 24, 16, CIPHER_CAMELLIA } } } }, | |||||
{ "camellia-256-cfb", new Dictionary<string, int[]> { { "CAMELLIA-256-CFB128", new int[] { 32, 16, CIPHER_CAMELLIA } } } }, | |||||
{ "rc4-md5", new Dictionary<string, int[]> { { "ARC4-128", new int[] { 16, 16, CIPHER_RC4 } } } } | |||||
private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> { | |||||
{ "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<string> SupportedCiphers() | public static List<string> SupportedCiphers() | ||||
@@ -39,7 +39,7 @@ namespace Shadowsocks.Encryption | |||||
return new List<string>(_ciphers.Keys); | return new List<string>(_ciphers.Keys); | ||||
} | } | ||||
protected override Dictionary<string, Dictionary<string, int[]>> getCiphers() | |||||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||||
{ | { | ||||
return _ciphers; | return _ciphers; | ||||
} | } | ||||
@@ -24,13 +24,13 @@ namespace Shadowsocks.Encryption | |||||
{ | { | ||||
} | } | ||||
private static Dictionary<string, Dictionary<string, int[]>> _ciphers = new Dictionary<string, Dictionary<string, int[]>> { | |||||
{ "salsa20", new Dictionary<string, int[]> { { "salsa20", new int[] { 32, 8, CIPHER_SALSA20 } } } }, | |||||
{ "chacha20", new Dictionary<string, int[]> { { "chacha20", new int[] { 32, 8, CIPHER_CHACHA20 } } } }, | |||||
{ "chacha20-ietf", new Dictionary<string, int[]> { { "chacha20-ietf", new int[] { 32, 12, CIPHER_CHACHA20_IETF } } } } | |||||
private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> { | |||||
{ "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<string, Dictionary<string, int[]>> getCiphers() | |||||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||||
{ | { | ||||
return _ciphers; | return _ciphers; | ||||
} | } | ||||
@@ -26,9 +26,8 @@ namespace Shadowsocks.Model | |||||
public bool autoCheckUpdate; | public bool autoCheckUpdate; | ||||
public bool isVerboseLogging; | public bool isVerboseLogging; | ||||
public LogViewerConfig logViewer; | 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"; | private static string CONFIG_FILE = "gui-config.json"; | ||||
@@ -58,6 +57,12 @@ namespace Shadowsocks.Model | |||||
config.localPort = 1080; | config.localPort = 1080; | ||||
if (config.index == -1 && config.strategy == null) | if (config.index == -1 && config.strategy == null) | ||||
config.index = 0; | 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; | return config; | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
@@ -98,7 +103,7 @@ namespace Shadowsocks.Model | |||||
} | } | ||||
catch (IOException e) | catch (IOException e) | ||||
{ | { | ||||
Console.Error.WriteLine(e); | |||||
Logging.LogUsefulException(e); | |||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,33 @@ | |||||
using System; | |||||
namespace Shadowsocks.Model | |||||
{ | |||||
/* | |||||
* Format: | |||||
* <modifiers-combination>+<key> | |||||
* | |||||
*/ | |||||
[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 = ""; | |||||
} | |||||
} | |||||
} |
@@ -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; | |||||
} | |||||
} | |||||
} |
@@ -13,7 +13,9 @@ namespace Shadowsocks | |||||
{ | { | ||||
static class Program | static class Program | ||||
{ | { | ||||
static ShadowsocksController controller; | |||||
private static ShadowsocksController _controller; | |||||
// XXX: Don't change this name | |||||
private static MenuViewController _viewController; | |||||
/// <summary> | /// <summary> | ||||
/// 应用程序的主入口点。 | /// 应用程序的主入口点。 | ||||
@@ -37,6 +39,7 @@ namespace Shadowsocks | |||||
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; | SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; | ||||
Application.EnableVisualStyles(); | Application.EnableVisualStyles(); | ||||
Application.SetCompatibleTextRenderingDefault(false); | Application.SetCompatibleTextRenderingDefault(false); | ||||
Application.ApplicationExit += (sender, args) => HotKeys.Destroy(); | |||||
if (!mutex.WaitOne(0, false)) | if (!mutex.WaitOne(0, false)) | ||||
{ | { | ||||
@@ -61,9 +64,10 @@ namespace Shadowsocks | |||||
#else | #else | ||||
Logging.OpenLogFile(); | Logging.OpenLogFile(); | ||||
#endif | #endif | ||||
controller = new ShadowsocksController(); | |||||
MenuViewController viewController = new MenuViewController(controller); | |||||
controller.Start(); | |||||
_controller = new ShadowsocksController(); | |||||
_viewController = new MenuViewController(_controller); | |||||
HotKeys.Init(); | |||||
_controller.Start(); | |||||
Application.Run(); | Application.Run(); | ||||
} | } | ||||
} | } | ||||
@@ -88,10 +92,10 @@ namespace Shadowsocks | |||||
{ | { | ||||
case PowerModes.Resume: | case PowerModes.Resume: | ||||
Logging.Info("os wake up"); | Logging.Info("os wake up"); | ||||
controller?.Start(); | |||||
_controller?.Start(); | |||||
break; | break; | ||||
case PowerModes.Suspend: | case PowerModes.Suspend: | ||||
controller?.Stop(); | |||||
_controller?.Stop(); | |||||
Logging.Info("os suspend"); | Logging.Info("os suspend"); | ||||
break; | break; | ||||
} | } | ||||
@@ -99,10 +103,10 @@ namespace Shadowsocks | |||||
private static void Application_ApplicationExit(object sender, EventArgs e) | private static void Application_ApplicationExit(object sender, EventArgs e) | ||||
{ | { | ||||
if (controller != null) | |||||
if (_controller != null) | |||||
{ | { | ||||
controller.Stop(); | |||||
controller = null; | |||||
_controller.Stop(); | |||||
_controller = null; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -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<HotKey, HotKeyCallBackHandler> _keymap = new Dictionary<HotKey, HotKeyCallBackHandler>(); | |||||
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<TControl> GetChildControls<TControl>(this Control control) where TControl : Control | |||||
{ | |||||
var children = control.Controls.Count > 0 ? control.Controls.OfType<TControl>() : Enumerable.Empty<TControl>(); | |||||
return children.SelectMany(c => GetChildControls<TControl>(c)).Concat(children); | |||||
} | |||||
} | |||||
} |
@@ -1,7 +1,9 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using System.IO; | using System.IO; | ||||
using System.IO.Compression; | using System.IO.Compression; | ||||
using System.Linq; | |||||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||
using System.Windows.Forms; | using System.Windows.Forms; | ||||
using Microsoft.Win32; | using Microsoft.Win32; | ||||
@@ -9,6 +11,20 @@ using Shadowsocks.Controller; | |||||
namespace Shadowsocks.Util | 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 | public class Utils | ||||
{ | { | ||||
private static bool? _portableMode; | private static bool? _portableMode; | ||||
@@ -114,7 +130,7 @@ namespace Shadowsocks.Util | |||||
public static string FormatBandwidth(long n) | public static string FormatBandwidth(long n) | ||||
{ | { | ||||
var result = GetBandwidthScale(n); | var result = GetBandwidthScale(n); | ||||
return $"{result.Item1:0.##}{result.Item2}"; | |||||
return $"{result.value:0.##}{result.unit_name}"; | |||||
} | } | ||||
public static string FormatBytes(long bytes) | public static string FormatBytes(long bytes) | ||||
@@ -127,32 +143,32 @@ namespace Shadowsocks.Util | |||||
const long E = P * 1024L; | const long E = P * 1024L; | ||||
if (bytes >= P * 990) | if (bytes >= P * 990) | ||||
return (bytes / (double)E).ToString("F5") + "EB"; | |||||
return (bytes / (double)E).ToString("F5") + "EiB"; | |||||
if (bytes >= T * 990) | if (bytes >= T * 990) | ||||
return (bytes / (double)P).ToString("F5") + "PB"; | |||||
return (bytes / (double)P).ToString("F5") + "PiB"; | |||||
if (bytes >= G * 990) | if (bytes >= G * 990) | ||||
return (bytes / (double)T).ToString("F5") + "TB"; | |||||
return (bytes / (double)T).ToString("F5") + "TiB"; | |||||
if (bytes >= M * 990) | if (bytes >= M * 990) | ||||
{ | { | ||||
return (bytes / (double)G).ToString("F4") + "GB"; | |||||
return (bytes / (double)G).ToString("F4") + "GiB"; | |||||
} | } | ||||
if (bytes >= M * 100) | if (bytes >= M * 100) | ||||
{ | { | ||||
return (bytes / (double)M).ToString("F1") + "MB"; | |||||
return (bytes / (double)M).ToString("F1") + "MiB"; | |||||
} | } | ||||
if (bytes >= M * 10) | if (bytes >= M * 10) | ||||
{ | { | ||||
return (bytes / (double)M).ToString("F2") + "MB"; | |||||
return (bytes / (double)M).ToString("F2") + "MiB"; | |||||
} | } | ||||
if (bytes >= K * 990) | if (bytes >= K * 990) | ||||
{ | { | ||||
return (bytes / (double)M).ToString("F3") + "MB"; | |||||
return (bytes / (double)M).ToString("F3") + "MiB"; | |||||
} | } | ||||
if (bytes > K * 2) | 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"; | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
@@ -160,11 +176,9 @@ namespace Shadowsocks.Util | |||||
/// </summary> | /// </summary> | ||||
/// <param name="n">Raw bandwidth</param> | /// <param name="n">Raw bandwidth</param> | ||||
/// <returns> | /// <returns> | ||||
/// Item1: float, bandwidth with suitable scale (eg. 56) | |||||
/// Item2: string, scale unit name (eg. KiB) | |||||
/// Item3: long, scale unit (eg. 1024) | |||||
/// The BandwidthScaleInfo struct | |||||
/// </returns> | /// </returns> | ||||
public static Tuple<float, string, long> GetBandwidthScale(long n) | |||||
public static BandwidthScaleInfo GetBandwidthScale(long n) | |||||
{ | { | ||||
long scale = 1; | long scale = 1; | ||||
float f = n; | float f = n; | ||||
@@ -193,7 +207,7 @@ namespace Shadowsocks.Util | |||||
scale <<= 10; | scale <<= 10; | ||||
unit = "TiB"; | unit = "TiB"; | ||||
} | } | ||||
return new Tuple<float, string, long>(f, unit, scale); | |||||
return new BandwidthScaleInfo(f, unit, scale); | |||||
} | } | ||||
public static RegistryKey OpenUserRegKey( string name, bool writable ) { | public static RegistryKey OpenUserRegKey( string name, bool writable ) { | ||||
@@ -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<TextBox> _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<TextBox>(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..."); | |||||
} | |||||
/// <summary> | |||||
/// Capture hotkey - Press key | |||||
/// </summary> | |||||
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(); | |||||
} | |||||
/// <summary> | |||||
/// Capture hotkey - Release key | |||||
/// </summary> | |||||
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 | |||||
/// <summary> | |||||
/// Find correct callback and corresponding label | |||||
/// </summary> | |||||
/// <param name="tb"></param> | |||||
/// <param name="cb"></param> | |||||
/// <param name="lb"></param> | |||||
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 | |||||
* | |||||
* <BaseName><Control-Type-Name> | |||||
*/ | |||||
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; | |||||
} | |||||
/// <summary> | |||||
/// | |||||
/// </summary> | |||||
/// <param name="type">from which type</param> | |||||
/// <param name="name">field name</param> | |||||
/// <param name="obj">pass null if static field</param> | |||||
/// <returns></returns> | |||||
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); | |||||
} | |||||
/// <summary> | |||||
/// Create hotkey callback handler delegate based on callback name | |||||
/// </summary> | |||||
/// <param name="type"></param> | |||||
/// <param name="methodname"></param> | |||||
/// <returns></returns> | |||||
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 | |||||
} | |||||
} |
@@ -0,0 +1,354 @@ | |||||
namespace Shadowsocks.View | |||||
{ | |||||
partial class HotkeySettingsForm | |||||
{ | |||||
/// <summary> | |||||
/// Required designer variable. | |||||
/// </summary> | |||||
private System.ComponentModel.IContainer components = null; | |||||
/// <summary> | |||||
/// Clean up any resources being used. | |||||
/// </summary> | |||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> | |||||
protected override void Dispose(bool disposing) | |||||
{ | |||||
if (disposing && (components != null)) | |||||
{ | |||||
components.Dispose(); | |||||
} | |||||
base.Dispose(disposing); | |||||
} | |||||
#region Windows Form Designer generated code | |||||
/// <summary> | |||||
/// Required method for Designer support - do not modify | |||||
/// the contents of this method with the code editor. | |||||
/// </summary> | |||||
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; | |||||
} | |||||
} |
@@ -0,0 +1,183 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<root> | |||||
<!-- | |||||
Microsoft ResX Schema | |||||
Version 2.0 | |||||
The primary goals of this format is to allow a simple XML format | |||||
that is mostly human readable. The generation and parsing of the | |||||
various data types are done through the TypeConverter classes | |||||
associated with the data types. | |||||
Example: | |||||
... ado.net/XML headers & schema ... | |||||
<resheader name="resmimetype">text/microsoft-resx</resheader> | |||||
<resheader name="version">2.0</resheader> | |||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> | |||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> | |||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> | |||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> | |||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> | |||||
<value>[base64 mime encoded serialized .NET Framework object]</value> | |||||
</data> | |||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> | |||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> | |||||
<comment>This is a comment</comment> | |||||
</data> | |||||
There are any number of "resheader" rows that contain simple | |||||
name/value pairs. | |||||
Each data row contains a name, and value. The row also contains a | |||||
type or mimetype. Type corresponds to a .NET class that support | |||||
text/value conversion through the TypeConverter architecture. | |||||
Classes that don't support this are serialized and stored with the | |||||
mimetype set. | |||||
The mimetype is used for serialized objects, and tells the | |||||
ResXResourceReader how to depersist the object. This is currently not | |||||
extensible. For a given mimetype the value must be set accordingly: | |||||
Note - application/x-microsoft.net.object.binary.base64 is the format | |||||
that the ResXResourceWriter will generate, however the reader can | |||||
read any of the formats listed below. | |||||
mimetype: application/x-microsoft.net.object.binary.base64 | |||||
value : The object must be serialized with | |||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter | |||||
: and then encoded with base64 encoding. | |||||
mimetype: application/x-microsoft.net.object.soap.base64 | |||||
value : The object must be serialized with | |||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter | |||||
: and then encoded with base64 encoding. | |||||
mimetype: application/x-microsoft.net.object.bytearray.base64 | |||||
value : The object must be serialized into a byte array | |||||
: using a System.ComponentModel.TypeConverter | |||||
: and then encoded with base64 encoding. | |||||
--> | |||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> | |||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> | |||||
<xsd:element name="root" msdata:IsDataSet="true"> | |||||
<xsd:complexType> | |||||
<xsd:choice maxOccurs="unbounded"> | |||||
<xsd:element name="metadata"> | |||||
<xsd:complexType> | |||||
<xsd:sequence> | |||||
<xsd:element name="value" type="xsd:string" minOccurs="0" /> | |||||
</xsd:sequence> | |||||
<xsd:attribute name="name" use="required" type="xsd:string" /> | |||||
<xsd:attribute name="type" type="xsd:string" /> | |||||
<xsd:attribute name="mimetype" type="xsd:string" /> | |||||
<xsd:attribute ref="xml:space" /> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
<xsd:element name="assembly"> | |||||
<xsd:complexType> | |||||
<xsd:attribute name="alias" type="xsd:string" /> | |||||
<xsd:attribute name="name" type="xsd:string" /> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
<xsd:element name="data"> | |||||
<xsd:complexType> | |||||
<xsd:sequence> | |||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> | |||||
</xsd:sequence> | |||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> | |||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> | |||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> | |||||
<xsd:attribute ref="xml:space" /> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
<xsd:element name="resheader"> | |||||
<xsd:complexType> | |||||
<xsd:sequence> | |||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||||
</xsd:sequence> | |||||
<xsd:attribute name="name" type="xsd:string" use="required" /> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
</xsd:choice> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
</xsd:schema> | |||||
<resheader name="resmimetype"> | |||||
<value>text/microsoft-resx</value> | |||||
</resheader> | |||||
<resheader name="version"> | |||||
<value>2.0</value> | |||||
</resheader> | |||||
<resheader name="reader"> | |||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
</resheader> | |||||
<resheader name="writer"> | |||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
</resheader> | |||||
<metadata name="flowLayoutPanel1.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>False</value> | |||||
</metadata> | |||||
<metadata name="flowLayoutPanel1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="tableLayoutPanel1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="SwitchSystemProxyLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="ChangeToPacLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="ChangeToGlobalLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="SwitchAllowLanLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="ShowLogsLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="ServerMoveUpLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="ServerMoveDownLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="SwitchSystemProxyTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="ChangeToPacTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="ChangeToGlobalTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="SwitchAllowLanTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="ShowLogsTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="ServerMoveUpTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="ServerMoveDownTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="btnOK.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="btnCancel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="btnRegisterAll.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="$this.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
</root> |
@@ -10,9 +10,22 @@ using Shadowsocks.Controller; | |||||
using Shadowsocks.Properties; | using Shadowsocks.Properties; | ||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using Shadowsocks.Util; | using Shadowsocks.Util; | ||||
using System.Text; | |||||
namespace Shadowsocks.View | 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 | public partial class LogForm : Form | ||||
{ | { | ||||
long lastOffset; | long lastOffset; | ||||
@@ -23,7 +36,7 @@ namespace Shadowsocks.View | |||||
#region chart | #region chart | ||||
long lastMaxSpeed; | long lastMaxSpeed; | ||||
public ShadowsocksController.QueueLast<Tuple<long, long>> traffic = new ShadowsocksController.QueueLast<Tuple<long, long>>(); | |||||
ShadowsocksController.QueueLast<TrafficInfo> traffic = new ShadowsocksController.QueueLast<TrafficInfo>(); | |||||
#endregion | #endregion | ||||
public LogForm(ShadowsocksController controller, string filename) | public LogForm(ShadowsocksController controller, string filename) | ||||
@@ -34,19 +47,13 @@ namespace Shadowsocks.View | |||||
Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | ||||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | 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; | controller.TrafficChanged += controller_TrafficChanged; | ||||
@@ -59,7 +66,7 @@ namespace Shadowsocks.View | |||||
List<float> outboundPoints = new List<float>(); | List<float> outboundPoints = new List<float>(); | ||||
TextAnnotation inboundAnnotation = new TextAnnotation(); | TextAnnotation inboundAnnotation = new TextAnnotation(); | ||||
TextAnnotation outboundAnnotation = new TextAnnotation(); | TextAnnotation outboundAnnotation = new TextAnnotation(); | ||||
Tuple<float, string, long> bandwidthScale; | |||||
BandwidthScaleInfo bandwidthScale; | |||||
const long minScale = 50; | const long minScale = 50; | ||||
long maxSpeed = 0; | long maxSpeed = 0; | ||||
long lastInbound, lastOutbound; | long lastInbound, lastOutbound; | ||||
@@ -70,12 +77,12 @@ namespace Shadowsocks.View | |||||
return; | return; | ||||
foreach (var trafficPerSecond in traffic) | 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) | if (maxSpeed > 0) | ||||
@@ -92,15 +99,15 @@ namespace Shadowsocks.View | |||||
bandwidthScale = Utils.GetBandwidthScale(maxSpeed); | bandwidthScale = Utils.GetBandwidthScale(maxSpeed); | ||||
//rescale the original data points, since it is List<float>, .ForEach does not work | //rescale the original data points, since it is List<float>, .ForEach does not work | ||||
inboundPoints = inboundPoints.Select(p => p / bandwidthScale.Item3).ToList(); | |||||
outboundPoints = outboundPoints.Select(p => p / bandwidthScale.Item3).ToList(); | |||||
inboundPoints = inboundPoints.Select(p => p / bandwidthScale.unit).ToList(); | |||||
outboundPoints = outboundPoints.Select(p => p / bandwidthScale.unit).ToList(); | |||||
if (trafficChart.IsHandleCreated) | if (trafficChart.IsHandleCreated) | ||||
{ | { | ||||
trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints); | trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints); | ||||
trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints); | 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.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last(); | ||||
inboundAnnotation.Text = Utils.FormatBandwidth(lastInbound); | inboundAnnotation.Text = Utils.FormatBandwidth(lastInbound); | ||||
outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last(); | outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last(); | ||||
@@ -115,10 +122,10 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
lock (this) | lock (this) | ||||
{ | { | ||||
traffic = new ShadowsocksController.QueueLast<Tuple<long, long>>(); | |||||
traffic = new ShadowsocksController.QueueLast<TrafficInfo>(); | |||||
foreach (var trafficPerSecond in controller.traffic) | foreach (var trafficPerSecond in controller.traffic) | ||||
{ | { | ||||
traffic.Enqueue(new Tuple<long, long>(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement)); | |||||
traffic.Enqueue(new TrafficInfo(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement)); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -162,9 +169,11 @@ namespace Shadowsocks.View | |||||
} | } | ||||
string line = ""; | string line = ""; | ||||
StringBuilder appendText = new StringBuilder(1024); | |||||
while ((line = reader.ReadLine()) != null) | while ((line = reader.ReadLine()) != null) | ||||
LogMessageTextBox.AppendText(line + Environment.NewLine); | |||||
appendText.Append(line + Environment.NewLine); | |||||
LogMessageTextBox.AppendText(appendText.ToString()); | |||||
LogMessageTextBox.ScrollToCaret(); | LogMessageTextBox.ScrollToCaret(); | ||||
lastOffset = reader.BaseStream.Position; | lastOffset = reader.BaseStream.Position; | ||||
@@ -182,14 +191,16 @@ namespace Shadowsocks.View | |||||
string line = ""; | string line = ""; | ||||
bool changed = false; | bool changed = false; | ||||
StringBuilder appendText = new StringBuilder(128); | |||||
while ((line = reader.ReadLine()) != null) | while ((line = reader.ReadLine()) != null) | ||||
{ | { | ||||
changed = true; | changed = true; | ||||
LogMessageTextBox.AppendText(line + Environment.NewLine); | |||||
appendText.Append(line + Environment.NewLine); | |||||
} | } | ||||
if (changed) | if (changed) | ||||
{ | { | ||||
LogMessageTextBox.AppendText(appendText.ToString()); | |||||
LogMessageTextBox.ScrollToCaret(); | LogMessageTextBox.ScrollToCaret(); | ||||
} | } | ||||
@@ -214,8 +225,7 @@ namespace Shadowsocks.View | |||||
timer.Start(); | timer.Start(); | ||||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | ||||
if (config == null) | |||||
config = new LogViewerConfig(); | |||||
Height = config.height; | Height = config.height; | ||||
Width = config.width; | Width = config.width; | ||||
Top = config.GetBestTop(); | Top = config.GetBestTop(); | ||||
@@ -241,8 +251,7 @@ namespace Shadowsocks.View | |||||
timer.Stop(); | timer.Stop(); | ||||
controller.TrafficChanged -= controller_TrafficChanged; | controller.TrafficChanged -= controller_TrafficChanged; | ||||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | ||||
if (config == null) | |||||
config = new LogViewerConfig(); | |||||
config.topMost = topMostTrigger; | config.topMost = topMostTrigger; | ||||
config.wrapText = wrapTextTrigger; | config.wrapText = wrapTextTrigger; | ||||
config.toolbarShown = toolbarTrigger; | config.toolbarShown = toolbarTrigger; | ||||
@@ -49,11 +49,12 @@ namespace Shadowsocks.View | |||||
private MenuItem editOnlinePACItem; | private MenuItem editOnlinePACItem; | ||||
private MenuItem autoCheckUpdatesToggleItem; | private MenuItem autoCheckUpdatesToggleItem; | ||||
private MenuItem proxyItem; | private MenuItem proxyItem; | ||||
private MenuItem hotKeyItem; | |||||
private MenuItem VerboseLoggingToggleItem; | private MenuItem VerboseLoggingToggleItem; | ||||
private ConfigForm configForm; | private ConfigForm configForm; | ||||
private ProxyForm proxyForm; | private ProxyForm proxyForm; | ||||
private List<LogForm> logForms = new List<LogForm>(); | |||||
private bool logFormsVisible = false; | |||||
private LogForm logForm; | |||||
private HotkeySettingsForm hotkeySettingsForm; | |||||
private string _urlToOpen; | private string _urlToOpen; | ||||
public MenuViewController(ShadowsocksController controller) | 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)); | MessageBox.Show(e.GetException().ToString(), String.Format(I18N.GetString("Shadowsocks Error: {0}"), e.GetException().Message)); | ||||
} | } | ||||
#region Tray Icon | |||||
private void UpdateTrayIcon() | private void UpdateTrayIcon() | ||||
{ | { | ||||
int dpi; | int dpi; | ||||
@@ -177,10 +180,10 @@ namespace Shadowsocks.View | |||||
} | } | ||||
// we want to show more details but notify icon title is limited to 63 characters | // we want to show more details but notify icon title is limited to 63 characters | ||||
string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" + | 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)); | _notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length)); | ||||
} | } | ||||
@@ -201,7 +204,7 @@ namespace Shadowsocks.View | |||||
else if (global) | else if (global) | ||||
{ | { | ||||
Color flyBlue = Color.FromArgb(25, 125, 191); | Color flyBlue = Color.FromArgb(25, 125, 191); | ||||
// Muliply with flyBlue | |||||
// Multiply with flyBlue | |||||
int red = color.R * flyBlue.R / 255; | int red = color.R * flyBlue.R / 255; | ||||
int green = color.G * flyBlue.G / 255; | int green = color.G * flyBlue.G / 255; | ||||
int blue = color.B * flyBlue.B / 255; | int blue = color.B * flyBlue.B / 255; | ||||
@@ -230,6 +233,10 @@ namespace Shadowsocks.View | |||||
return bitmap; | return bitmap; | ||||
} | } | ||||
#endregion | |||||
#region MenuItems and MenuGroups | |||||
private MenuItem CreateMenuItem(string text, EventHandler click) | private MenuItem CreateMenuItem(string text, EventHandler click) | ||||
{ | { | ||||
return new MenuItem(I18N.GetString(text), click); | return new MenuItem(I18N.GetString(text), click); | ||||
@@ -271,6 +278,7 @@ namespace Shadowsocks.View | |||||
new MenuItem("-"), | new MenuItem("-"), | ||||
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), | CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), | ||||
this.VerboseLoggingToggleItem = CreateMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_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[] { | CreateMenuGroup("Updates...", new MenuItem[] { | ||||
CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)), | CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)), | ||||
new MenuItem("-"), | new MenuItem("-"), | ||||
@@ -282,6 +290,8 @@ namespace Shadowsocks.View | |||||
}); | }); | ||||
} | } | ||||
#endregion | |||||
private void controller_ConfigChanged(object sender, EventArgs e) | private void controller_ConfigChanged(object sender, EventArgs e) | ||||
{ | { | ||||
LoadCurrentConfiguration(); | LoadCurrentConfiguration(); | ||||
@@ -406,7 +416,7 @@ namespace Shadowsocks.View | |||||
i++; | 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("-") ); | items.Add( i++, new MenuItem("-") ); | ||||
int strategyCount = i; | 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 | 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) | void logForm_FormClosed(object sender, FormClosedEventArgs e) | ||||
{ | { | ||||
logForms.Remove((LogForm)sender); | |||||
logForm = null; | |||||
Utils.ReleaseMemory(true); | |||||
} | } | ||||
void configForm_FormClosed(object sender, FormClosedEventArgs e) | void configForm_FormClosed(object sender, FormClosedEventArgs e) | ||||
@@ -504,6 +523,12 @@ namespace Shadowsocks.View | |||||
Utils.ReleaseMemory(true); | Utils.ReleaseMemory(true); | ||||
} | } | ||||
void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e) | |||||
{ | |||||
hotkeySettingsForm = null; | |||||
Utils.ReleaseMemory(true); | |||||
} | |||||
private void Config_Click(object sender, EventArgs e) | private void Config_Click(object sender, EventArgs e) | ||||
{ | { | ||||
ShowConfigForm(); | ShowConfigForm(); | ||||
@@ -519,11 +544,9 @@ namespace Shadowsocks.View | |||||
private void CheckUpdateForFirstRun() | private void CheckUpdateForFirstRun() | ||||
{ | { | ||||
Configuration config = controller.GetConfigurationCopy(); | 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() | private void ShowFirstTimeBalloon() | ||||
@@ -547,7 +570,7 @@ namespace Shadowsocks.View | |||||
} | } | ||||
else if (e.Button == MouseButtons.Middle) | else if (e.Button == MouseButtons.Middle) | ||||
{ | { | ||||
ShowLogForms(); | |||||
ShowLogForm(); | |||||
} | } | ||||
} | } | ||||
@@ -607,15 +630,6 @@ namespace Shadowsocks.View | |||||
controller.SelectStrategy((string)item.Tag); | 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 ) { | private void VerboseLoggingToggleItem_Click( object sender, EventArgs e ) { | ||||
VerboseLoggingToggleItem.Checked = ! VerboseLoggingToggleItem.Checked; | VerboseLoggingToggleItem.Checked = ! VerboseLoggingToggleItem.Checked; | ||||
controller.ToggleVerboseLogging( VerboseLoggingToggleItem.Checked ); | controller.ToggleVerboseLogging( VerboseLoggingToggleItem.Checked ); | ||||
@@ -820,5 +834,20 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
ShowProxyForm(); | ShowProxyForm(); | ||||
} | } | ||||
private void hotKeyItem_Click(object sender, EventArgs e) | |||||
{ | |||||
ShowHotKeySettingsForm(); | |||||
} | |||||
private void ShowLogItem_Click(object sender, EventArgs e) | |||||
{ | |||||
ShowLogForm(); | |||||
} | |||||
public void ShowLogForm_HotKey() | |||||
{ | |||||
ShowLogForm(); | |||||
} | |||||
} | } | ||||
} | } |
@@ -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; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -3,6 +3,7 @@ | |||||
<package id="Caseless.Fody" version="1.4.2" targetFramework="net40-client" developmentDependency="true" /> | <package id="Caseless.Fody" version="1.4.2" targetFramework="net40-client" developmentDependency="true" /> | ||||
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net40-client" developmentDependency="true" /> | <package id="Costura.Fody" version="1.3.3.0" targetFramework="net40-client" developmentDependency="true" /> | ||||
<package id="Fody" version="1.29.4" targetFramework="net40-client" developmentDependency="true" /> | <package id="Fody" version="1.29.4" targetFramework="net40-client" developmentDependency="true" /> | ||||
<package id="GlobalHotKey" version="1.1.0" targetFramework="net40-client" /> | |||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40-client" /> | <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40-client" /> | ||||
<package id="StringEx.CS" version="0.3.1" targetFramework="net40-client" developmentDependency="true" /> | <package id="StringEx.CS" version="0.3.1" targetFramework="net40-client" developmentDependency="true" /> | ||||
</packages> | </packages> |
@@ -65,6 +65,10 @@ | |||||
<ApplicationManifest>app.manifest</ApplicationManifest> | <ApplicationManifest>app.manifest</ApplicationManifest> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<Reference Include="GlobalHotKey, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL"> | |||||
<HintPath>3rd\GlobalHotKey.1.1.0\lib\GlobalHotKey.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="Microsoft.CSharp" /> | <Reference Include="Microsoft.CSharp" /> | ||||
<Reference Include="Microsoft.VisualBasic" /> | <Reference Include="Microsoft.VisualBasic" /> | ||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> | <Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> | ||||
@@ -139,6 +143,8 @@ | |||||
<Compile Include="3rd\zxing\ResultPoint.cs" /> | <Compile Include="3rd\zxing\ResultPoint.cs" /> | ||||
<Compile Include="3rd\zxing\ResultPointCallback.cs" /> | <Compile Include="3rd\zxing\ResultPointCallback.cs" /> | ||||
<Compile Include="3rd\zxing\WriterException.cs" /> | <Compile Include="3rd\zxing\WriterException.cs" /> | ||||
<Compile Include="Model\HotKeyConfig.cs" /> | |||||
<Compile Include="Model\ProxyConfig.cs" /> | |||||
<Compile Include="Proxy\DirectConnect.cs" /> | <Compile Include="Proxy\DirectConnect.cs" /> | ||||
<Compile Include="Proxy\IProxy.cs" /> | <Compile Include="Proxy\IProxy.cs" /> | ||||
<Compile Include="Controller\Service\AvailabilityStatistics.cs" /> | <Compile Include="Controller\Service\AvailabilityStatistics.cs" /> | ||||
@@ -177,6 +183,7 @@ | |||||
<Compile Include="Controller\Strategy\IStrategy.cs" /> | <Compile Include="Controller\Strategy\IStrategy.cs" /> | ||||
<Compile Include="Proxy\Socks5Proxy.cs" /> | <Compile Include="Proxy\Socks5Proxy.cs" /> | ||||
<Compile Include="StringEx.cs" /> | <Compile Include="StringEx.cs" /> | ||||
<Compile Include="Util\Hotkeys.cs" /> | |||||
<Compile Include="Util\ProcessManagement\Job.cs" /> | <Compile Include="Util\ProcessManagement\Job.cs" /> | ||||
<Compile Include="Util\ProcessManagement\ThreadUtil.cs" /> | <Compile Include="Util\ProcessManagement\ThreadUtil.cs" /> | ||||
<Compile Include="Util\Sockets\SocketUtil.cs" /> | <Compile Include="Util\Sockets\SocketUtil.cs" /> | ||||
@@ -200,6 +207,12 @@ | |||||
<Compile Include="View\CalculationControl.Designer.cs"> | <Compile Include="View\CalculationControl.Designer.cs"> | ||||
<DependentUpon>CalculationControl.cs</DependentUpon> | <DependentUpon>CalculationControl.cs</DependentUpon> | ||||
</Compile> | </Compile> | ||||
<Compile Include="View\HotkeySettingsForm.cs"> | |||||
<SubType>Form</SubType> | |||||
</Compile> | |||||
<Compile Include="View\HotkeySettingsForm.designer.cs"> | |||||
<DependentUpon>HotkeySettingsForm.cs</DependentUpon> | |||||
</Compile> | |||||
<Compile Include="View\LogForm.cs"> | <Compile Include="View\LogForm.cs"> | ||||
<SubType>Form</SubType> | <SubType>Form</SubType> | ||||
</Compile> | </Compile> | ||||
@@ -240,6 +253,9 @@ | |||||
<EmbeddedResource Include="View\CalculationControl.resx"> | <EmbeddedResource Include="View\CalculationControl.resx"> | ||||
<DependentUpon>CalculationControl.cs</DependentUpon> | <DependentUpon>CalculationControl.cs</DependentUpon> | ||||
</EmbeddedResource> | </EmbeddedResource> | ||||
<EmbeddedResource Include="View\HotkeySettingsForm.resx"> | |||||
<DependentUpon>HotkeySettingsForm.cs</DependentUpon> | |||||
</EmbeddedResource> | |||||
<EmbeddedResource Include="View\LogForm.resx"> | <EmbeddedResource Include="View\LogForm.resx"> | ||||
<DependentUpon>LogForm.cs</DependentUpon> | <DependentUpon>LogForm.cs</DependentUpon> | ||||
</EmbeddedResource> | </EmbeddedResource> | ||||
@@ -2,6 +2,9 @@ | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||
using Shadowsocks.Controller; | using Shadowsocks.Controller; | ||||
using Shadowsocks.Encryption; | using Shadowsocks.Encryption; | ||||
using Shadowsocks.Util; | |||||
using GlobalHotKey; | |||||
using System.Windows.Input; | |||||
using System.Threading; | using System.Threading; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
@@ -22,6 +25,32 @@ namespace test | |||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3.2", "1.3.1") > 0); | 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] | [TestMethod] | ||||
public void TestMD5() | public void TestMD5() | ||||
{ | { | ||||
@@ -35,10 +35,15 @@ | |||||
<StartupObject /> | <StartupObject /> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<Reference Include="GlobalHotKey, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL"> | |||||
<HintPath>..\shadowsocks-csharp\3rd\GlobalHotKey.1.1.0\lib\GlobalHotKey.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="System" /> | <Reference Include="System" /> | ||||
<Reference Include="System.Net" /> | <Reference Include="System.Net" /> | ||||
<Reference Include="System.Net.Http" /> | <Reference Include="System.Net.Http" /> | ||||
<Reference Include="System.Net.Http.WebRequest" /> | <Reference Include="System.Net.Http.WebRequest" /> | ||||
<Reference Include="WindowsBase" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<Choose> | <Choose> | ||||
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"> | <When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"> | ||||