# Conflicts: # shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs # shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs # shadowsocks-csharp/View/LogForm.Designer.cs # shadowsocks-csharp/View/MenuViewController.cstags/3.0
@@ -1,3 +1,12 @@ | |||||
2.5.6 2015-08-19 | |||||
- Add portable mode. Create shadowsocks_portable_mode.txt to use it | |||||
- Support server reorder | |||||
2.5.5 2015-08-17 | |||||
- Fix crash when enabling Availability Statistics and some servers can not be resolved | |||||
- Allow multiple instances | |||||
- Other fixes | |||||
2.5.4 2015-08-16 | 2.5.4 2015-08-16 | ||||
- Hide Privoxy icon | - Hide Privoxy icon | ||||
@@ -3,34 +3,36 @@ Shadowsocks for Windows | |||||
[![Build Status]][Appveyor] | [![Build Status]][Appveyor] | ||||
[中文说明] | |||||
#### Features | #### Features | ||||
1. System proxy configuration | 1. System proxy configuration | ||||
2. PAC mode and global mode | 2. PAC mode and global mode | ||||
3. GFWList and user rules | |||||
3. [GFWList] and user rules | |||||
4. Supports HTTP proxy | 4. Supports HTTP proxy | ||||
5. Supports server auto switching | 5. Supports server auto switching | ||||
6. Supports UDP relay (see Usage) | 6. Supports UDP relay (see Usage) | ||||
#### Download | #### Download | ||||
Download a [latest release]. | |||||
Download the [latest release]. | |||||
#### Basic | #### Basic | ||||
1. Find Shadowsocks icon in the notification tray | 1. Find Shadowsocks icon in the notification tray | ||||
2. You can add multiple servers in servers menu | 2. You can add multiple servers in servers menu | ||||
3. Select Enable System Proxy menu to enable system proxy. Please disable other | |||||
3. Select `Enable System Proxy` menu to enable system proxy. Please disable other | |||||
proxy addons in your browser, or set them to use system proxy | proxy addons in your browser, or set them to use system proxy | ||||
4. You can also configure your browser proxy manually if you don't want to enable | 4. You can also configure your browser proxy manually if you don't want to enable | ||||
system proxy. Set Socks5 or HTTP proxy to 127.0.0.1:1080. You can change this | system proxy. Set Socks5 or HTTP proxy to 127.0.0.1:1080. You can change this | ||||
port in Server -> Edit Servers | |||||
port in `Servers -> Edit Servers` | |||||
#### PAC | #### PAC | ||||
1. You can change PAC rules by editing the PAC file. When you save the PAC file | 1. You can change PAC rules by editing the PAC file. When you save the PAC file | ||||
with any editor, Shadowsocks will notify browsers about the change automatically | with any editor, Shadowsocks will notify browsers about the change automatically | ||||
2. You can also update PAC file from GFWList (maintained by 3rd party) | |||||
2. You can also update PAC file from [GFWList] (maintained by 3rd party) | |||||
3. You can also use online PAC URL | 3. You can also use online PAC URL | ||||
#### Server Auto Switching | #### Server Auto Switching | ||||
@@ -44,7 +46,25 @@ with any editor, Shadowsocks will notify browsers about the change automatically | |||||
#### UDP | #### UDP | ||||
For UDP, you need to use SocksCap or ProxyCap to force programs you want | For UDP, you need to use SocksCap or ProxyCap to force programs you want | ||||
to proxy to tunnel over Shadowsocks | |||||
to be proxied to tunnel over Shadowsocks | |||||
#### Multiple Instances | |||||
If you want to manage multiple servers using other tools like SwitchyOmega, | |||||
you can start multiple Shadowsocks instances. To avoid configuration conflicts, | |||||
copy Shadowsocks to a new directory and choose a different local port. | |||||
Also, make sure to use `SOCKS5` proxy in SwitchyOmega, since we have only | |||||
one HTTP proxy instance. | |||||
#### Server Configuration | |||||
Please visit [Servers] for more information. | |||||
#### Portable Mode | |||||
If you want to put all temporary files into shadowsocks/temp folder instead of | |||||
system temp folder, create a `shadowsocks_portable_mode.txt` into shadowsocks folder. | |||||
#### Develop | #### Develop | ||||
@@ -58,3 +78,6 @@ GPLv3 | |||||
[Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp | [Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp | ||||
[Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master | [Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master | ||||
[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases | [latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases | ||||
[GFWList]: https://github.com/gfwlist/gfwlist | |||||
[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side | |||||
[中文说明]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Shadowsocks-Windows-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E |
@@ -12,7 +12,7 @@ namespace Shadowsocks.Controller | |||||
static I18N() | static I18N() | ||||
{ | { | ||||
Strings = new Dictionary<string, string>(); | Strings = new Dictionary<string, string>(); | ||||
if (System.Globalization.CultureInfo.CurrentCulture.IetfLanguageTag.ToLowerInvariant().StartsWith("zh")) | if (System.Globalization.CultureInfo.CurrentCulture.IetfLanguageTag.ToLowerInvariant().StartsWith("zh")) | ||||
{ | { | ||||
string[] lines = Regex.Split(Resources.cn, "\r\n|\r|\n"); | string[] lines = Regex.Split(Resources.cn, "\r\n|\r|\n"); | ||||
@@ -1,4 +1,5 @@ | |||||
using System; | |||||
using Shadowsocks.Util; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.IO; | using System.IO; | ||||
using System.Net.Sockets; | using System.Net.Sockets; | ||||
@@ -14,14 +15,14 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
string temppath = Path.GetTempPath(); | |||||
string temppath = Utils.GetTempPath(); | |||||
LogFile = Path.Combine(temppath, "shadowsocks.log"); | LogFile = Path.Combine(temppath, "shadowsocks.log"); | ||||
FileStream fs = new FileStream(LogFile, FileMode.Append); | FileStream fs = new FileStream(LogFile, FileMode.Append); | ||||
StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs); | StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs); | ||||
sw.AutoFlush = true; | sw.AutoFlush = true; | ||||
Console.SetOut(sw); | Console.SetOut(sw); | ||||
Console.SetError(sw); | Console.SetError(sw); | ||||
return true; | return true; | ||||
} | } | ||||
catch (IOException e) | catch (IOException e) | ||||
@@ -71,7 +72,6 @@ namespace Shadowsocks.Controller | |||||
Console.WriteLine(e); | Console.WriteLine(e); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Simply extended System.IO.StreamWriter for adding timestamp workaround | // Simply extended System.IO.StreamWriter for adding timestamp workaround | ||||
@@ -10,13 +10,15 @@ using System.Threading.Tasks; | |||||
using System.Windows.Forms; | using System.Windows.Forms; | ||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using SimpleJson = SimpleJson.SimpleJson; | using SimpleJson = SimpleJson.SimpleJson; | ||||
using Shadowsocks.Util; | |||||
using Timer = System.Threading.Timer; | using Timer = System.Threading.Timer; | ||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
using DataUnit = KeyValuePair<string, string>; | using DataUnit = KeyValuePair<string, string>; | ||||
using DataList = List<KeyValuePair<string, string>>; | using DataList = List<KeyValuePair<string, string>>; | ||||
class AvailabilityStatistics | |||||
internal class AvailabilityStatistics | |||||
{ | { | ||||
private const string StatisticsFilesName = "shadowsocks.availability.csv"; | private const string StatisticsFilesName = "shadowsocks.availability.csv"; | ||||
private const string Delimiter = ","; | private const string Delimiter = ","; | ||||
@@ -33,7 +35,7 @@ namespace Shadowsocks.Controller | |||||
//static constructor to initialize every public static fields before refereced | //static constructor to initialize every public static fields before refereced | ||||
static AvailabilityStatistics() | static AvailabilityStatistics() | ||||
{ | { | ||||
var temppath = Path.GetTempPath(); | |||||
string temppath = Utils.GetTempPath(); | |||||
AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName); | AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName); | ||||
} | } | ||||
@@ -41,10 +43,18 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
_timer?.Dispose(); | |||||
if (!enabled) return true; | |||||
_state = new State(); | |||||
_timer = new Timer(Evaluate, _state, delayBeforeStart, Interval); | |||||
if (enabled) | |||||
{ | |||||
if (_timer?.Change(0, Interval) == null) | |||||
{ | |||||
_state = new State(); | |||||
_timer = new Timer(Evaluate, _state, 0, Interval); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
_timer?.Dispose(); | |||||
} | |||||
return true; | return true; | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
@@ -80,7 +90,7 @@ namespace Shadowsocks.Controller | |||||
string country = obj["country"]; | string country = obj["country"]; | ||||
string city = obj["city"]; | string city = obj["city"]; | ||||
string isp = obj["isp"]; | string isp = obj["isp"]; | ||||
string regionName= obj["regionName"]; | |||||
string regionName = obj["regionName"]; | |||||
if (country == null || city == null || isp == null || regionName == null) return ret; | if (country == null || city == null || isp == null || regionName == null) return ret; | ||||
ret[0] = new DataUnit(State.Geolocation, $"{country} {regionName} {city}"); | ret[0] = new DataUnit(State.Geolocation, $"{country} {regionName} {city}"); | ||||
ret[1] = new DataUnit(State.ISP, isp); | ret[1] = new DataUnit(State.ISP, isp); | ||||
@@ -93,7 +103,8 @@ namespace Shadowsocks.Controller | |||||
if (server.server == "") return null; | if (server.server == "") return null; | ||||
var ping = new Ping(); | var ping = new Ping(); | ||||
var ret = new List<DataList>(); | var ret = new List<DataList>(); | ||||
foreach (var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))) | |||||
foreach ( | |||||
var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))) | |||||
{ | { | ||||
//ICMP echo. we can also set options and special bytes | //ICMP echo. we can also set options and special bytes | ||||
try | try | ||||
@@ -108,7 +119,7 @@ namespace Shadowsocks.Controller | |||||
//new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply | //new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply | ||||
}); | }); | ||||
} | } | ||||
catch (PingException e) | |||||
catch (Exception e) | |||||
{ | { | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
} | } | ||||
@@ -138,11 +149,11 @@ namespace Shadowsocks.Controller | |||||
if (!File.Exists(AvailabilityStatisticsFile)) | if (!File.Exists(AvailabilityStatisticsFile)) | ||||
{ | { | ||||
var headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray()); | var headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray()); | ||||
lines = new[] { headerLine, dataLine }; | |||||
lines = new[] {headerLine, dataLine}; | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
lines = new[] { dataLine }; | |||||
lines = new[] {dataLine}; | |||||
} | } | ||||
File.AppendAllLines(AvailabilityStatisticsFile, lines); | File.AppendAllLines(AvailabilityStatisticsFile, lines); | ||||
} | } | ||||
@@ -77,7 +77,6 @@ namespace Shadowsocks.Controller | |||||
_udpSocket.Bind(localEndPoint); | _udpSocket.Bind(localEndPoint); | ||||
_tcpSocket.Listen(1024); | _tcpSocket.Listen(1024); | ||||
// Start an asynchronous socket to listen for connections. | // Start an asynchronous socket to listen for connections. | ||||
Console.WriteLine("Shadowsocks started"); | Console.WriteLine("Shadowsocks started"); | ||||
_tcpSocket.BeginAccept( | _tcpSocket.BeginAccept( | ||||
@@ -185,7 +184,6 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
private void ReceiveCallback(IAsyncResult ar) | private void ReceiveCallback(IAsyncResult ar) | ||||
{ | { | ||||
object[] state = (object[])ar.AsyncState; | object[] state = (object[])ar.AsyncState; | ||||
@@ -86,7 +86,6 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
public string TouchPACFile() | public string TouchPACFile() | ||||
{ | { | ||||
if (File.Exists(PAC_FILE)) | if (File.Exists(PAC_FILE)) | ||||
@@ -146,7 +145,7 @@ Connection: Close | |||||
", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac; | ", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac; | ||||
byte[] response = System.Text.Encoding.UTF8.GetBytes(text); | byte[] response = System.Text.Encoding.UTF8.GetBytes(text); | ||||
socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket); | socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket); | ||||
Util.Utils.ReleaseMemory(); | |||||
Util.Utils.ReleaseMemory(true); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -9,6 +9,7 @@ using System.Text; | |||||
using System.Net.NetworkInformation; | using System.Net.NetworkInformation; | ||||
using System.Net; | using System.Net; | ||||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
@@ -20,7 +21,7 @@ namespace Shadowsocks.Controller | |||||
static PolipoRunner() | static PolipoRunner() | ||||
{ | { | ||||
temppath = Path.GetTempPath(); | |||||
temppath = Utils.GetTempPath(); | |||||
try | try | ||||
{ | { | ||||
FileManager.UncompressFile(temppath + "/ss_privoxy.exe", Resources.privoxy_exe); | FileManager.UncompressFile(temppath + "/ss_privoxy.exe", Resources.privoxy_exe); | ||||
@@ -65,10 +66,13 @@ namespace Shadowsocks.Controller | |||||
polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1"); | polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1"); | ||||
FileManager.ByteArrayToFile(temppath + "/privoxy.conf", System.Text.Encoding.UTF8.GetBytes(polipoConfig)); | FileManager.ByteArrayToFile(temppath + "/privoxy.conf", System.Text.Encoding.UTF8.GetBytes(polipoConfig)); | ||||
if (!(temppath.EndsWith("\\") || temppath.EndsWith("/"))) { | |||||
temppath = temppath + "\\"; | |||||
} | |||||
_process = new Process(); | _process = new Process(); | ||||
// Configure the process using the StartInfo properties. | // Configure the process using the StartInfo properties. | ||||
_process.StartInfo.FileName = temppath + "/ss_privoxy.exe"; | |||||
_process.StartInfo.Arguments = " \"" + temppath + "/privoxy.conf\""; | |||||
_process.StartInfo.FileName = temppath + "ss_privoxy.exe"; | |||||
_process.StartInfo.Arguments = " \"" + temppath + "privoxy.conf\""; | |||||
_process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; | _process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; | ||||
_process.StartInfo.UseShellExecute = true; | _process.StartInfo.UseShellExecute = true; | ||||
_process.StartInfo.CreateNoWindow = true; | _process.StartInfo.CreateNoWindow = true; | ||||
@@ -144,7 +148,6 @@ namespace Shadowsocks.Controller | |||||
[DllImport("user32.dll")] | [DllImport("user32.dll")] | ||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam); | public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam); | ||||
public void RefreshTrayArea() | public void RefreshTrayArea() | ||||
{ | { | ||||
IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null); | IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null); | ||||
@@ -161,7 +164,6 @@ namespace Shadowsocks.Controller | |||||
RefreshTrayArea(notificationAreaHandle); | RefreshTrayArea(notificationAreaHandle); | ||||
} | } | ||||
private static void RefreshTrayArea(IntPtr windowHandle) | private static void RefreshTrayArea(IntPtr windowHandle) | ||||
{ | { | ||||
const uint wmMousemove = 0x0200; | const uint wmMousemove = 0x0200; | ||||
@@ -103,7 +103,6 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
private void StartPipe(IAsyncResult ar) | private void StartPipe(IAsyncResult ar) | ||||
{ | { | ||||
if (_closed) | if (_closed) | ||||
@@ -14,9 +14,18 @@ namespace Shadowsocks.Controller | |||||
class TCPRelay : Listener.Service | class TCPRelay : Listener.Service | ||||
{ | { | ||||
private ShadowsocksController _controller; | private ShadowsocksController _controller; | ||||
private DateTime _lastSweepTime; | |||||
public ISet<Handler> Handlers | |||||
{ | |||||
get; set; | |||||
} | |||||
public TCPRelay(ShadowsocksController controller) | public TCPRelay(ShadowsocksController controller) | ||||
{ | { | ||||
this._controller = controller; | this._controller = controller; | ||||
this.Handlers = new HashSet<Handler>(); | |||||
this._lastSweepTime = DateTime.Now; | |||||
} | } | ||||
public bool Handle(byte[] firstPacket, int length, Socket socket, object state) | public bool Handle(byte[] firstPacket, int length, Socket socket, object state) | ||||
@@ -33,9 +42,33 @@ namespace Shadowsocks.Controller | |||||
Handler handler = new Handler(); | Handler handler = new Handler(); | ||||
handler.connection = socket; | handler.connection = socket; | ||||
handler.controller = _controller; | handler.controller = _controller; | ||||
handler.relay = this; | |||||
handler.Start(firstPacket, length); | handler.Start(firstPacket, length); | ||||
return true; | |||||
IList<Handler> handlersToClose = new List<Handler>(); | |||||
lock (this.Handlers) | |||||
{ | |||||
this.Handlers.Add(handler); | |||||
Logging.Debug($"connections: {Handlers.Count}"); | |||||
DateTime now = DateTime.Now; | |||||
if (now - _lastSweepTime > TimeSpan.FromSeconds(1)) | |||||
{ | |||||
_lastSweepTime = now; | |||||
foreach (Handler handler1 in this.Handlers) | |||||
{ | |||||
if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) | |||||
{ | |||||
handlersToClose.Add(handler1); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
foreach (Handler handler1 in handlersToClose) | |||||
{ | |||||
Logging.Debug("Closing timed out connection"); | |||||
handler1.Close(); | |||||
} | |||||
return true; | |||||
} | } | ||||
} | } | ||||
@@ -48,6 +81,10 @@ namespace Shadowsocks.Controller | |||||
public Socket remote; | public Socket remote; | ||||
public Socket connection; | public Socket connection; | ||||
public ShadowsocksController controller; | public ShadowsocksController controller; | ||||
public TCPRelay relay; | |||||
public DateTime lastActivity; | |||||
private int retryCount = 0; | private int retryCount = 0; | ||||
private bool connected; | private bool connected; | ||||
@@ -55,7 +92,7 @@ namespace Shadowsocks.Controller | |||||
private byte[] _firstPacket; | private byte[] _firstPacket; | ||||
private int _firstPacketLength; | private int _firstPacketLength; | ||||
// Size of receive buffer. | // Size of receive buffer. | ||||
public const int RecvSize = 16384; | |||||
public const int RecvSize = 8192; | |||||
public const int BufferSize = RecvSize + 32; | public const int BufferSize = RecvSize + 32; | ||||
private int totalRead = 0; | private int totalRead = 0; | ||||
@@ -74,7 +111,7 @@ namespace Shadowsocks.Controller | |||||
private bool connectionShutdown = false; | private bool connectionShutdown = false; | ||||
private bool remoteShutdown = false; | private bool remoteShutdown = false; | ||||
private bool closed = false; | private bool closed = false; | ||||
private object encryptionLock = new object(); | private object encryptionLock = new object(); | ||||
private object decryptionLock = new object(); | private object decryptionLock = new object(); | ||||
@@ -96,6 +133,7 @@ namespace Shadowsocks.Controller | |||||
this._firstPacket = firstPacket; | this._firstPacket = firstPacket; | ||||
this._firstPacketLength = length; | this._firstPacketLength = length; | ||||
this.HandshakeReceive(); | this.HandshakeReceive(); | ||||
this.lastActivity = DateTime.Now; | |||||
} | } | ||||
private void CheckClose() | private void CheckClose() | ||||
@@ -108,6 +146,11 @@ namespace Shadowsocks.Controller | |||||
public void Close() | public void Close() | ||||
{ | { | ||||
lock (relay.Handlers) | |||||
{ | |||||
Logging.Debug($"connections: {relay.Handlers.Count}"); | |||||
relay.Handlers.Remove(this); | |||||
} | |||||
lock (this) | lock (this) | ||||
{ | { | ||||
if (closed) | if (closed) | ||||
@@ -152,7 +195,6 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
private void HandshakeReceive() | private void HandshakeReceive() | ||||
{ | { | ||||
if (closed) | if (closed) | ||||
@@ -222,7 +264,7 @@ namespace Shadowsocks.Controller | |||||
try | try | ||||
{ | { | ||||
int bytesRead = connection.EndReceive(ar); | int bytesRead = connection.EndReceive(ar); | ||||
if (bytesRead >= 3) | if (bytesRead >= 3) | ||||
{ | { | ||||
command = connetionRecvBuffer[1]; | command = connetionRecvBuffer[1]; | ||||
@@ -272,7 +314,6 @@ namespace Shadowsocks.Controller | |||||
private void ReadAll(IAsyncResult ar) | private void ReadAll(IAsyncResult ar) | ||||
{ | { | ||||
if (closed) | if (closed) | ||||
{ | { | ||||
return; | return; | ||||
@@ -347,7 +388,6 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
IPEndPoint remoteEP = new IPEndPoint(ipAddress, server.server_port); | IPEndPoint remoteEP = new IPEndPoint(ipAddress, server.server_port); | ||||
remote = new Socket(ipAddress.AddressFamily, | remote = new Socket(ipAddress.AddressFamily, | ||||
SocketType.Stream, ProtocolType.Tcp); | SocketType.Stream, ProtocolType.Tcp); | ||||
remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | ||||
@@ -485,6 +525,7 @@ namespace Shadowsocks.Controller | |||||
if (bytesRead > 0) | if (bytesRead > 0) | ||||
{ | { | ||||
this.lastActivity = DateTime.Now; | |||||
int bytesToSend; | int bytesToSend; | ||||
lock (decryptionLock) | lock (decryptionLock) | ||||
{ | { | ||||
@@ -548,7 +589,6 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); | remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); | ||||
IStrategy strategy = controller.GetCurrentStrategy(); | IStrategy strategy = controller.GetCurrentStrategy(); | ||||
if (strategy != null) | if (strategy != null) | ||||
{ | { | ||||
@@ -607,5 +647,4 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } |
@@ -108,9 +108,11 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (ObjectDisposedException) | catch (ObjectDisposedException) | ||||
{ | { | ||||
// TODO: handle the ObjectDisposedException | |||||
} | } | ||||
catch (Exception) | catch (Exception) | ||||
{ | { | ||||
// TODO: need more think about handle other Exceptions, or should remove this catch(). | |||||
} | } | ||||
finally | finally | ||||
{ | { | ||||
@@ -124,9 +126,11 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (ObjectDisposedException) | catch (ObjectDisposedException) | ||||
{ | { | ||||
// TODO: handle the ObjectDisposedException | |||||
} | } | ||||
catch (Exception) | catch (Exception) | ||||
{ | { | ||||
// TODO: need more think about handle other Exceptions, or should remove this catch(). | |||||
} | } | ||||
finally | finally | ||||
{ | { | ||||
@@ -134,6 +138,8 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
} | } | ||||
// cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054 | // cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054 | ||||
class LRUCache<K, V> where V : UDPRelay.UDPHandler | class LRUCache<K, V> where V : UDPRelay.UDPHandler | ||||
{ | { | ||||
@@ -196,5 +202,4 @@ namespace Shadowsocks.Controller | |||||
public K key; | public K key; | ||||
public V value; | public V value; | ||||
} | } | ||||
} | } |
@@ -12,13 +12,13 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
public class UpdateChecker | public class UpdateChecker | ||||
{ | { | ||||
private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-csharp/releases"; | |||||
private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-windows/releases"; | |||||
public string LatestVersionNumber; | public string LatestVersionNumber; | ||||
public string LatestVersionURL; | public string LatestVersionURL; | ||||
public event EventHandler NewVersionFound; | public event EventHandler NewVersionFound; | ||||
public const string Version = "2.5.4"; | |||||
public const string Version = "2.5.6"; | |||||
public void CheckUpdate(Configuration config) | public void CheckUpdate(Configuration config) | ||||
{ | { | ||||
@@ -53,7 +53,6 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
return CompareVersion(ParseVersionFromURL(x), ParseVersionFromURL(y)); | return CompareVersion(ParseVersionFromURL(x), ParseVersionFromURL(y)); | ||||
} | } | ||||
} | } | ||||
private static string ParseVersionFromURL(string url) | private static string ParseVersionFromURL(string url) | ||||
@@ -39,7 +39,7 @@ namespace Shadowsocks.Controller | |||||
public event EventHandler EnableStatusChanged; | public event EventHandler EnableStatusChanged; | ||||
public event EventHandler EnableGlobalChanged; | public event EventHandler EnableGlobalChanged; | ||||
public event EventHandler ShareOverLANStatusChanged; | public event EventHandler ShareOverLANStatusChanged; | ||||
// when user clicked Edit PAC, and PAC file has already created | // when user clicked Edit PAC, and PAC file has already created | ||||
public event EventHandler<PathEventArgs> PACFileReadyToOpen; | public event EventHandler<PathEventArgs> PACFileReadyToOpen; | ||||
public event EventHandler<PathEventArgs> UserRuleFileReadyToOpen; | public event EventHandler<PathEventArgs> UserRuleFileReadyToOpen; | ||||
@@ -365,17 +365,15 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
UpdateSystemProxy(); | UpdateSystemProxy(); | ||||
Util.Utils.ReleaseMemory(); | |||||
Util.Utils.ReleaseMemory(true); | |||||
} | } | ||||
protected void SaveConfig(Configuration newConfig) | protected void SaveConfig(Configuration newConfig) | ||||
{ | { | ||||
Configuration.Save(newConfig); | Configuration.Save(newConfig); | ||||
Reload(); | Reload(); | ||||
} | } | ||||
private void UpdateSystemProxy() | private void UpdateSystemProxy() | ||||
{ | { | ||||
if (_config.enabled) | if (_config.enabled) | ||||
@@ -422,7 +420,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
while (true) | while (true) | ||||
{ | { | ||||
Util.Utils.ReleaseMemory(); | |||||
Util.Utils.ReleaseMemory(false); | |||||
Thread.Sleep(30 * 1000); | Thread.Sleep(30 * 1000); | ||||
} | } | ||||
} | } | ||||
@@ -14,17 +14,17 @@ namespace Shadowsocks.Controller.Strategy | |||||
/* | /* | ||||
* IStrategy | * IStrategy | ||||
* | |||||
* | |||||
* Subclasses must be thread-safe | * Subclasses must be thread-safe | ||||
*/ | */ | ||||
public interface IStrategy | public interface IStrategy | ||||
{ | { | ||||
string Name { get; } | string Name { get; } | ||||
string ID { get; } | string ID { get; } | ||||
/* | /* | ||||
* Called when servers need to be reloaded, i.e. new configuration saved | |||||
* Called when servers need to be reloaded, i.e. new configuration saved | |||||
*/ | */ | ||||
void ReloadServers(); | void ReloadServers(); | ||||
@@ -3,6 +3,9 @@ using System.Collections.Generic; | |||||
using System.IO; | using System.IO; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Net; | using System.Net; | ||||
using System.Text; | |||||
using Shadowsocks.Model; | |||||
using System.IO; | |||||
using System.Net.NetworkInformation; | using System.Net.NetworkInformation; | ||||
using System.Threading; | using System.Threading; | ||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
@@ -18,15 +21,6 @@ namespace Shadowsocks.Controller.Strategy | |||||
private const int CachedInterval = 30*60*1000; //choose a new server every 30 minutes | private const int CachedInterval = 30*60*1000; //choose a new server every 30 minutes | ||||
private const int RetryInterval = 2*60*1000; //choose a new server every 30 minutes | private const int RetryInterval = 2*60*1000; //choose a new server every 30 minutes | ||||
public class StatisticsData | |||||
{ | |||||
public int SuccessTimes; | |||||
public int TimedOutTimes; | |||||
public int AverageResponse; | |||||
public int MinResponse; | |||||
public int MaxResponse; | |||||
} | |||||
public StatisticsStrategy(ShadowsocksController controller) | public StatisticsStrategy(ShadowsocksController controller) | ||||
{ | { | ||||
_controller = controller; | _controller = controller; | ||||
@@ -99,6 +93,15 @@ namespace Shadowsocks.Controller.Strategy | |||||
return (double)data.SuccessTimes / (data.SuccessTimes + data.TimedOutTimes); //simply choose min package loss | return (double)data.SuccessTimes / (data.SuccessTimes + data.TimedOutTimes); //simply choose min package loss | ||||
} | } | ||||
private class StatisticsData | |||||
{ | |||||
public int SuccessTimes; | |||||
public int TimedOutTimes; | |||||
public int AverageResponse; | |||||
public int MinResponse; | |||||
public int MaxResponse; | |||||
} | |||||
private void ChooseNewServer(List<Server> servers) | private void ChooseNewServer(List<Server> servers) | ||||
{ | { | ||||
if (_statistics == null || servers.Count == 0) | if (_statistics == null || servers.Count == 0) | ||||
@@ -118,7 +121,7 @@ namespace Shadowsocks.Controller.Strategy | |||||
).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2); | ).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2); | ||||
if (!_currentServer.Equals(bestResult.server)) //output when enabled | if (!_currentServer.Equals(bestResult.server)) //output when enabled | ||||
{ | |||||
{ | |||||
LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by package loss:{1 - bestResult.score}"); | LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by package loss:{1 - bestResult.score}"); | ||||
} | } | ||||
_currentServer = bestResult.server; | _currentServer = bestResult.server; | ||||
@@ -137,7 +140,7 @@ namespace Shadowsocks.Controller.Strategy | |||||
} | } | ||||
} | } | ||||
public string ID => "com.shadowsocks.strategy.statistics"; | |||||
public string ID => "com.shadowsocks.strategy.scbs"; | |||||
public string Name => I18N.GetString("Choose By Total Package Loss"); | public string Name => I18N.GetString("Choose By Total Package Loss"); | ||||
@@ -148,6 +151,9 @@ namespace Shadowsocks.Controller.Strategy | |||||
{ | { | ||||
ChooseNewServer(_controller.GetCurrentConfiguration().configs); | ChooseNewServer(_controller.GetCurrentConfiguration().configs); | ||||
} | } | ||||
if (oldServer != _currentServer) | |||||
{ | |||||
} | |||||
return _currentServer; //current server cached for CachedInterval | return _currentServer; //current server cached for CachedInterval | ||||
} | } | ||||
@@ -176,5 +182,6 @@ namespace Shadowsocks.Controller.Strategy | |||||
{ | { | ||||
//TODO: combine this part of data with ICMP statics | //TODO: combine this part of data with ICMP statics | ||||
} | } | ||||
} | } | ||||
} | } |
@@ -56,18 +56,14 @@ namespace Shadowsocks.Controller | |||||
pacUrl = "http://127.0.0.1:" + config.localPort.ToString() + "/pac?t=" + GetTimestamp(DateTime.Now); | pacUrl = "http://127.0.0.1:" + config.localPort.ToString() + "/pac?t=" + GetTimestamp(DateTime.Now); | ||||
registry.SetValue("ProxyEnable", 0); | registry.SetValue("ProxyEnable", 0); | ||||
var readProxyServer = registry.GetValue("ProxyServer"); | var readProxyServer = registry.GetValue("ProxyServer"); | ||||
if (readProxyServer != null && readProxyServer.Equals("127.0.0.1:" + config.localPort.ToString())) | |||||
registry.SetValue("ProxyServer", ""); | |||||
registry.SetValue("ProxyServer", ""); | |||||
registry.SetValue("AutoConfigURL", pacUrl); | registry.SetValue("AutoConfigURL", pacUrl); | ||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
registry.SetValue("ProxyEnable", 0); | registry.SetValue("ProxyEnable", 0); | ||||
if (global) | |||||
{ | |||||
registry.SetValue("ProxyServer", ""); | |||||
} | |||||
registry.SetValue("ProxyServer", ""); | |||||
registry.SetValue("AutoConfigURL", ""); | registry.SetValue("AutoConfigURL", ""); | ||||
} | } | ||||
//Set AutoDetectProxy Off | //Set AutoDetectProxy Off | ||||
@@ -19,6 +19,7 @@ Update Local PAC from GFWList=从 GFWList 更新本地 PAC | |||||
Edit User Rule for GFWList...=编辑 GFWList 的用户规则... | Edit User Rule for GFWList...=编辑 GFWList 的用户规则... | ||||
Show QRCode...=显示二维码... | Show QRCode...=显示二维码... | ||||
Scan QRCode from Screen...=扫描屏幕上的二维码... | Scan QRCode from Screen...=扫描屏幕上的二维码... | ||||
Availability Statistics=统计可用性 | |||||
Show Logs...=显示日志... | Show Logs...=显示日志... | ||||
About...=关于... | About...=关于... | ||||
Quit=退出 | Quit=退出 | ||||
@@ -41,6 +42,19 @@ Remarks=备注 | |||||
OK=确定 | OK=确定 | ||||
Cancel=取消 | Cancel=取消 | ||||
New server=未配置的服务器 | New server=未配置的服务器 | ||||
Move &Up=上移(&U) | |||||
Move D&own=下移(&O) | |||||
# Log Form | |||||
&File=文件(&F) | |||||
&Open Location=在资源管理器中打开(&O) | |||||
E&xit=退出(&X) | |||||
&Clean logs=清空(&C) | |||||
&Font=字体(&F) | |||||
&Wrap text=自动换行(&W) | |||||
&Top most=置顶(&T) | |||||
Log Viewer=日志查看器 | |||||
# QRCode Form | # QRCode Form | ||||
@@ -72,7 +86,10 @@ Failed to update PAC file =更新 PAC 文件失败 | |||||
PAC updated=更新 PAC 成功 | PAC updated=更新 PAC 成功 | ||||
No updates found. Please report to GFWList if you have problems with it.=未发现更新。如有问题请提交给 GFWList。 | No updates found. Please report to GFWList if you have problems with it.=未发现更新。如有问题请提交给 GFWList。 | ||||
No QRCode found. Try to zoom in or move it to the center of the screen.=未发现二维码,尝试把它放大或移动到靠近屏幕中间的位置 | No QRCode found. Try to zoom in or move it to the center of the screen.=未发现二维码,尝试把它放大或移动到靠近屏幕中间的位置 | ||||
Shadowsocks is already running.=Shadowsocks 已经在运行。 | |||||
Find Shadowsocks icon in your notify tray.=请在任务栏里寻找 Shadowsocks 图标。 | |||||
If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同时启动多个,可以另外复制一份到别的目录。 | |||||
Failed to decode QRCode=无法解析二维码 | Failed to decode QRCode=无法解析二维码 | ||||
Failed to update registry=无法修改注册表 | Failed to update registry=无法修改注册表 | ||||
System Proxy On: =系统代理已启用: | System Proxy On: =系统代理已启用: | ||||
Running: Port {0}=正在运行:端口 {0} | |||||
Running: Port {0}=正在运行:端口 {0} |
@@ -26,7 +26,6 @@ namespace Shadowsocks.Encryption | |||||
protected int keyLen; | protected int keyLen; | ||||
protected int ivLen; | protected int ivLen; | ||||
public IVEncryptor(string method, string password) | public IVEncryptor(string method, string password) | ||||
: base(method, password) | : base(method, password) | ||||
{ | { | ||||
@@ -1,5 +1,6 @@ | |||||
using Shadowsocks.Controller; | using Shadowsocks.Controller; | ||||
using Shadowsocks.Properties; | using Shadowsocks.Properties; | ||||
using Shadowsocks.Util; | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.IO; | using System.IO; | ||||
@@ -18,7 +19,7 @@ namespace Shadowsocks.Encryption | |||||
static PolarSSL() | static PolarSSL() | ||||
{ | { | ||||
string tempPath = Path.GetTempPath(); | |||||
string tempPath = Utils.GetTempPath(); | |||||
string dllPath = tempPath + "/libsscrypto.dll"; | string dllPath = tempPath + "/libsscrypto.dll"; | ||||
try | try | ||||
{ | { | ||||
@@ -49,7 +50,6 @@ namespace Shadowsocks.Encryption | |||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | ||||
public extern static int aes_crypt_cfb128(IntPtr ctx, int mode, int length, ref int iv_off, byte[] iv, byte[] input, byte[] output); | public extern static int aes_crypt_cfb128(IntPtr ctx, int mode, int length, ref int iv_off, byte[] iv, byte[] input, byte[] output); | ||||
public const int ARC4_CTX_SIZE = 264; | public const int ARC4_CTX_SIZE = 264; | ||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | ||||
@@ -1,5 +1,6 @@ | |||||
using Shadowsocks.Controller; | using Shadowsocks.Controller; | ||||
using Shadowsocks.Properties; | using Shadowsocks.Properties; | ||||
using Shadowsocks.Util; | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.IO; | using System.IO; | ||||
@@ -14,7 +15,7 @@ namespace Shadowsocks.Encryption | |||||
static Sodium() | static Sodium() | ||||
{ | { | ||||
string tempPath = Path.GetTempPath(); | |||||
string tempPath = Utils.GetTempPath(); | |||||
string dllPath = tempPath + "/libsscrypto.dll"; | string dllPath = tempPath + "/libsscrypto.dll"; | ||||
try | try | ||||
{ | { | ||||
@@ -1,6 +1,7 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | using System.Text; | ||||
using System.Threading; | |||||
namespace Shadowsocks.Encryption | namespace Shadowsocks.Encryption | ||||
{ | { | ||||
@@ -12,19 +13,17 @@ namespace Shadowsocks.Encryption | |||||
const int SODIUM_BLOCK_SIZE = 64; | const int SODIUM_BLOCK_SIZE = 64; | ||||
static byte[] sodiumBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE]; | |||||
protected int _encryptBytesRemaining; | protected int _encryptBytesRemaining; | ||||
protected int _decryptBytesRemaining; | protected int _decryptBytesRemaining; | ||||
protected ulong _encryptIC; | protected ulong _encryptIC; | ||||
protected ulong _decryptIC; | protected ulong _decryptIC; | ||||
protected byte[] _encryptBuf; | |||||
protected byte[] _decryptBuf; | |||||
public SodiumEncryptor(string method, string password) | public SodiumEncryptor(string method, string password) | ||||
: base(method, password) | : base(method, password) | ||||
{ | { | ||||
InitKey(method, password); | InitKey(method, password); | ||||
_encryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE]; | |||||
_decryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE]; | |||||
} | } | ||||
private static Dictionary<string, int[]> _ciphers = new Dictionary<string, int[]> { | private static Dictionary<string, int[]> _ciphers = new Dictionary<string, int[]> { | ||||
@@ -47,48 +46,51 @@ namespace Shadowsocks.Encryption | |||||
// TODO write a unidirection cipher so we don't have to if if if | // TODO write a unidirection cipher so we don't have to if if if | ||||
int bytesRemaining; | int bytesRemaining; | ||||
ulong ic; | ulong ic; | ||||
byte[] sodiumBuf; | |||||
byte[] iv; | byte[] iv; | ||||
if (isCipher) | |||||
{ | |||||
bytesRemaining = _encryptBytesRemaining; | |||||
ic = _encryptIC; | |||||
sodiumBuf = _encryptBuf; | |||||
iv = _encryptIV; | |||||
} | |||||
else | |||||
{ | |||||
bytesRemaining = _decryptBytesRemaining; | |||||
ic = _decryptIC; | |||||
sodiumBuf = _decryptBuf; | |||||
iv = _decryptIV; | |||||
} | |||||
int padding = bytesRemaining; | |||||
Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length); | |||||
switch (_cipher) | |||||
// I'm tired. just add a big lock | |||||
// let's optimize for RAM instead of CPU | |||||
lock(sodiumBuf) | |||||
{ | { | ||||
case CIPHER_SALSA20: | |||||
Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); | |||||
break; | |||||
case CIPHER_CHACHA20: | |||||
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); | |||||
break; | |||||
} | |||||
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length); | |||||
padding += length; | |||||
ic += (ulong)padding / SODIUM_BLOCK_SIZE; | |||||
bytesRemaining = padding % SODIUM_BLOCK_SIZE; | |||||
if (isCipher) | |||||
{ | |||||
bytesRemaining = _encryptBytesRemaining; | |||||
ic = _encryptIC; | |||||
iv = _encryptIV; | |||||
} | |||||
else | |||||
{ | |||||
bytesRemaining = _decryptBytesRemaining; | |||||
ic = _decryptIC; | |||||
iv = _decryptIV; | |||||
} | |||||
int padding = bytesRemaining; | |||||
Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length); | |||||
if (isCipher) | |||||
{ | |||||
_encryptBytesRemaining = bytesRemaining; | |||||
_encryptIC = ic; | |||||
} | |||||
else | |||||
{ | |||||
_decryptBytesRemaining = bytesRemaining; | |||||
_decryptIC = ic; | |||||
switch (_cipher) | |||||
{ | |||||
case CIPHER_SALSA20: | |||||
Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); | |||||
break; | |||||
case CIPHER_CHACHA20: | |||||
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); | |||||
break; | |||||
} | |||||
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length); | |||||
padding += length; | |||||
ic += (ulong)padding / SODIUM_BLOCK_SIZE; | |||||
bytesRemaining = padding % SODIUM_BLOCK_SIZE; | |||||
if (isCipher) | |||||
{ | |||||
_encryptBytesRemaining = bytesRemaining; | |||||
_encryptIC = ic; | |||||
} | |||||
else | |||||
{ | |||||
_decryptBytesRemaining = bytesRemaining; | |||||
_decryptIC = ic; | |||||
} | |||||
} | } | ||||
} | } | ||||
@@ -41,7 +41,6 @@ namespace Shadowsocks.Encryption | |||||
outlength = length; | outlength = length; | ||||
} | } | ||||
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | ||||
{ | { | ||||
byte[] result = new byte[length]; | byte[] result = new byte[length]; | ||||
@@ -100,7 +99,6 @@ namespace Shadowsocks.Encryption | |||||
return sorted; | return sorted; | ||||
} | } | ||||
public override void Dispose() | public override void Dispose() | ||||
{ | { | ||||
} | } | ||||
@@ -18,8 +18,8 @@ namespace Shadowsocks | |||||
[STAThread] | [STAThread] | ||||
static void Main() | static void Main() | ||||
{ | { | ||||
Util.Utils.ReleaseMemory(); | |||||
using (Mutex mutex = new Mutex(false, "Global\\" + "71981632-A427-497F-AB91-241CD227EC1F")) | |||||
Util.Utils.ReleaseMemory(true); | |||||
using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode())) | |||||
{ | { | ||||
Application.EnableVisualStyles(); | Application.EnableVisualStyles(); | ||||
Application.SetCompatibleTextRenderingDefault(false); | Application.SetCompatibleTextRenderingDefault(false); | ||||
@@ -31,7 +31,9 @@ namespace Shadowsocks | |||||
{ | { | ||||
Process oldProcess = oldProcesses[0]; | Process oldProcess = oldProcesses[0]; | ||||
} | } | ||||
MessageBox.Show("Shadowsocks is already running.\n\nFind Shadowsocks icon in your notify tray."); | |||||
MessageBox.Show(I18N.GetString("Find Shadowsocks icon in your notify tray.") + "\n" + | |||||
I18N.GetString("If you want to start multiple Shadowsocks, make a copy in another directory."), | |||||
I18N.GetString("Shadowsocks is already running.")); | |||||
return; | return; | ||||
} | } | ||||
Directory.SetCurrentDirectory(Application.StartupPath); | Directory.SetCurrentDirectory(Application.StartupPath); | ||||
@@ -5,12 +5,31 @@ using System.IO; | |||||
using System.IO.Compression; | using System.IO.Compression; | ||||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||
using System.Text; | using System.Text; | ||||
using System.Windows.Forms; | |||||
namespace Shadowsocks.Util | namespace Shadowsocks.Util | ||||
{ | { | ||||
public class Utils | public class Utils | ||||
{ | { | ||||
public static void ReleaseMemory() | |||||
// return path to store temporary files | |||||
public static string GetTempPath() | |||||
{ | |||||
if (File.Exists(Application.StartupPath + "\\shadowsocks_portable_mode.txt")) | |||||
{ | |||||
try | |||||
{ | |||||
Directory.CreateDirectory(Application.StartupPath + "\\temp"); | |||||
} catch (Exception e) | |||||
{ | |||||
Console.WriteLine(e); | |||||
} | |||||
// don't use "/", it will fail when we call explorer /select xxx/temp\xxx.log | |||||
return Application.StartupPath + "\\temp"; | |||||
} | |||||
return Path.GetTempPath(); | |||||
} | |||||
public static void ReleaseMemory(bool removePages) | |||||
{ | { | ||||
// release any unused pages | // release any unused pages | ||||
// making the numbers look good in task manager | // making the numbers look good in task manager | ||||
@@ -20,8 +39,29 @@ namespace Shadowsocks.Util | |||||
// which is part of user experience | // which is part of user experience | ||||
GC.Collect(GC.MaxGeneration); | GC.Collect(GC.MaxGeneration); | ||||
GC.WaitForPendingFinalizers(); | GC.WaitForPendingFinalizers(); | ||||
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, | |||||
(UIntPtr)0xFFFFFFFF, (UIntPtr)0xFFFFFFFF); | |||||
if (removePages) | |||||
{ | |||||
// as some users have pointed out | |||||
// removing pages from working set will cause some IO | |||||
// which lowered user experience for another group of users | |||||
// | |||||
// so we do 2 more things here to satisfy them: | |||||
// 1. only remove pages once when configuration is changed | |||||
// 2. add more comments here to tell users that calling | |||||
// this function will not be more frequent than | |||||
// IM apps writing chat logs, or web browsers writing cache files | |||||
// if they're so concerned about their disk, they should | |||||
// uninstall all IM apps and web browsers | |||||
// | |||||
// please open an issue if you're worried about anything else in your computer | |||||
// no matter it's GPU performance, monitor contrast, audio fidelity | |||||
// or anything else in the task manager | |||||
// we'll do as much as we can to help you | |||||
// | |||||
// just kidding | |||||
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, | |||||
(UIntPtr)0xFFFFFFFF, (UIntPtr)0xFFFFFFFF); | |||||
} | |||||
} | } | ||||
public static string UnGzip(byte[] buf) | public static string UnGzip(byte[] buf) | ||||
@@ -47,6 +47,9 @@ | |||||
this.ServerGroupBox = new System.Windows.Forms.GroupBox(); | this.ServerGroupBox = new System.Windows.Forms.GroupBox(); | ||||
this.ServersListBox = new System.Windows.Forms.ListBox(); | this.ServersListBox = new System.Windows.Forms.ListBox(); | ||||
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); | this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); | ||||
this.tableLayoutPanel6 = new System.Windows.Forms.TableLayoutPanel(); | |||||
this.MoveDownButton = new System.Windows.Forms.Button(); | |||||
this.MoveUpButton = new System.Windows.Forms.Button(); | |||||
this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel(); | this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel(); | ||||
this.ProxyPortTextBox = new System.Windows.Forms.TextBox(); | this.ProxyPortTextBox = new System.Windows.Forms.TextBox(); | ||||
this.ProxyPortLabel = new System.Windows.Forms.Label(); | this.ProxyPortLabel = new System.Windows.Forms.Label(); | ||||
@@ -55,6 +58,7 @@ | |||||
this.tableLayoutPanel1.SuspendLayout(); | this.tableLayoutPanel1.SuspendLayout(); | ||||
this.ServerGroupBox.SuspendLayout(); | this.ServerGroupBox.SuspendLayout(); | ||||
this.tableLayoutPanel2.SuspendLayout(); | this.tableLayoutPanel2.SuspendLayout(); | ||||
this.tableLayoutPanel6.SuspendLayout(); | |||||
this.tableLayoutPanel5.SuspendLayout(); | this.tableLayoutPanel5.SuspendLayout(); | ||||
this.tableLayoutPanel3.SuspendLayout(); | this.tableLayoutPanel3.SuspendLayout(); | ||||
this.tableLayoutPanel4.SuspendLayout(); | this.tableLayoutPanel4.SuspendLayout(); | ||||
@@ -184,7 +188,7 @@ | |||||
// | // | ||||
// EncryptionSelect | // EncryptionSelect | ||||
// | // | ||||
this.EncryptionSelect.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | |||||
this.EncryptionSelect.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | |||||
| System.Windows.Forms.AnchorStyles.Right))); | | System.Windows.Forms.AnchorStyles.Right))); | ||||
this.EncryptionSelect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; | this.EncryptionSelect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; | ||||
this.EncryptionSelect.FormattingEnabled = true; | this.EncryptionSelect.FormattingEnabled = true; | ||||
@@ -281,6 +285,7 @@ | |||||
// | // | ||||
this.ServersListBox.FormattingEnabled = true; | this.ServersListBox.FormattingEnabled = true; | ||||
this.ServersListBox.IntegralHeight = false; | this.ServersListBox.IntegralHeight = false; | ||||
this.ServersListBox.ItemHeight = 12; | |||||
this.ServersListBox.Location = new System.Drawing.Point(0, 0); | this.ServersListBox.Location = new System.Drawing.Point(0, 0); | ||||
this.ServersListBox.Margin = new System.Windows.Forms.Padding(0); | this.ServersListBox.Margin = new System.Windows.Forms.Padding(0); | ||||
this.ServersListBox.Name = "ServersListBox"; | this.ServersListBox.Name = "ServersListBox"; | ||||
@@ -295,6 +300,7 @@ | |||||
this.tableLayoutPanel2.ColumnCount = 2; | this.tableLayoutPanel2.ColumnCount = 2; | ||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); | this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); | ||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); | this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); | ||||
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel6, 0, 2); | |||||
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel5, 1, 1); | this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel5, 1, 1); | ||||
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 1, 2); | this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 1, 2); | ||||
this.tableLayoutPanel2.Controls.Add(this.ServersListBox, 0, 0); | this.tableLayoutPanel2.Controls.Add(this.ServersListBox, 0, 0); | ||||
@@ -310,6 +316,48 @@ | |||||
this.tableLayoutPanel2.Size = new System.Drawing.Size(427, 238); | this.tableLayoutPanel2.Size = new System.Drawing.Size(427, 238); | ||||
this.tableLayoutPanel2.TabIndex = 7; | this.tableLayoutPanel2.TabIndex = 7; | ||||
// | // | ||||
// tableLayoutPanel6 | |||||
// | |||||
this.tableLayoutPanel6.AutoSize = true; | |||||
this.tableLayoutPanel6.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; | |||||
this.tableLayoutPanel6.ColumnCount = 2; | |||||
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); | |||||
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); | |||||
this.tableLayoutPanel6.Controls.Add(this.MoveDownButton, 1, 0); | |||||
this.tableLayoutPanel6.Controls.Add(this.MoveUpButton, 0, 0); | |||||
this.tableLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Top; | |||||
this.tableLayoutPanel6.Location = new System.Drawing.Point(0, 211); | |||||
this.tableLayoutPanel6.Margin = new System.Windows.Forms.Padding(0); | |||||
this.tableLayoutPanel6.Name = "tableLayoutPanel6"; | |||||
this.tableLayoutPanel6.RowCount = 1; | |||||
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle()); | |||||
this.tableLayoutPanel6.Size = new System.Drawing.Size(166, 32); | |||||
this.tableLayoutPanel6.TabIndex = 10; | |||||
// | |||||
// MoveDownButton | |||||
// | |||||
this.MoveDownButton.Dock = System.Windows.Forms.DockStyle.Right; | |||||
this.MoveDownButton.Location = new System.Drawing.Point(86, 6); | |||||
this.MoveDownButton.Margin = new System.Windows.Forms.Padding(3, 6, 0, 3); | |||||
this.MoveDownButton.Name = "MoveDownButton"; | |||||
this.MoveDownButton.Size = new System.Drawing.Size(80, 23); | |||||
this.MoveDownButton.TabIndex = 7; | |||||
this.MoveDownButton.Text = "Move D&own"; | |||||
this.MoveDownButton.UseVisualStyleBackColor = true; | |||||
this.MoveDownButton.Click += new System.EventHandler(this.MoveDownButton_Click); | |||||
// | |||||
// MoveUpButton | |||||
// | |||||
this.MoveUpButton.Dock = System.Windows.Forms.DockStyle.Left; | |||||
this.MoveUpButton.Location = new System.Drawing.Point(0, 6); | |||||
this.MoveUpButton.Margin = new System.Windows.Forms.Padding(0, 6, 3, 3); | |||||
this.MoveUpButton.Name = "MoveUpButton"; | |||||
this.MoveUpButton.Size = new System.Drawing.Size(80, 23); | |||||
this.MoveUpButton.TabIndex = 6; | |||||
this.MoveUpButton.Text = "Move &Up"; | |||||
this.MoveUpButton.UseVisualStyleBackColor = true; | |||||
this.MoveUpButton.Click += new System.EventHandler(this.MoveUpButton_Click); | |||||
// | |||||
// tableLayoutPanel5 | // tableLayoutPanel5 | ||||
// | // | ||||
this.tableLayoutPanel5.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | this.tableLayoutPanel5.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | ||||
@@ -418,6 +466,7 @@ | |||||
this.ServerGroupBox.PerformLayout(); | this.ServerGroupBox.PerformLayout(); | ||||
this.tableLayoutPanel2.ResumeLayout(false); | this.tableLayoutPanel2.ResumeLayout(false); | ||||
this.tableLayoutPanel2.PerformLayout(); | this.tableLayoutPanel2.PerformLayout(); | ||||
this.tableLayoutPanel6.ResumeLayout(false); | |||||
this.tableLayoutPanel5.ResumeLayout(false); | this.tableLayoutPanel5.ResumeLayout(false); | ||||
this.tableLayoutPanel5.PerformLayout(); | this.tableLayoutPanel5.PerformLayout(); | ||||
this.tableLayoutPanel3.ResumeLayout(false); | this.tableLayoutPanel3.ResumeLayout(false); | ||||
@@ -453,6 +502,9 @@ | |||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5; | private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5; | ||||
private System.Windows.Forms.TextBox ProxyPortTextBox; | private System.Windows.Forms.TextBox ProxyPortTextBox; | ||||
private System.Windows.Forms.Label ProxyPortLabel; | private System.Windows.Forms.Label ProxyPortLabel; | ||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6; | |||||
private System.Windows.Forms.Button MoveDownButton; | |||||
private System.Windows.Forms.Button MoveUpButton; | |||||
} | } | ||||
} | } | ||||
@@ -18,7 +18,7 @@ namespace Shadowsocks.View | |||||
// this is a copy of configuration that we are working on | // this is a copy of configuration that we are working on | ||||
private Configuration _modifiedConfiguration; | private Configuration _modifiedConfiguration; | ||||
private int _oldSelectedIndex = -1; | |||||
private int _lastSelectedIndex = -1; | |||||
public ConfigForm(ShadowsocksController controller) | public ConfigForm(ShadowsocksController controller) | ||||
{ | { | ||||
@@ -51,6 +51,8 @@ namespace Shadowsocks.View | |||||
ServerGroupBox.Text = I18N.GetString("Server"); | ServerGroupBox.Text = I18N.GetString("Server"); | ||||
OKButton.Text = I18N.GetString("OK"); | OKButton.Text = I18N.GetString("OK"); | ||||
MyCancelButton.Text = I18N.GetString("Cancel"); | MyCancelButton.Text = I18N.GetString("Cancel"); | ||||
MoveUpButton.Text = I18N.GetString("Move &Up"); | |||||
MoveDownButton.Text = I18N.GetString("Move D&own"); | |||||
this.Text = I18N.GetString("Edit Servers"); | this.Text = I18N.GetString("Edit Servers"); | ||||
} | } | ||||
@@ -58,7 +60,7 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
LoadCurrentConfiguration(); | LoadCurrentConfiguration(); | ||||
} | } | ||||
private void ShowWindow() | private void ShowWindow() | ||||
{ | { | ||||
this.Opacity = 1; | this.Opacity = 1; | ||||
@@ -70,7 +72,7 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
if (_oldSelectedIndex == -1 || _oldSelectedIndex >= _modifiedConfiguration.configs.Count) | |||||
if (_lastSelectedIndex == -1 || _lastSelectedIndex >= _modifiedConfiguration.configs.Count) | |||||
{ | { | ||||
return true; | return true; | ||||
} | } | ||||
@@ -85,9 +87,9 @@ namespace Shadowsocks.View | |||||
int localPort = int.Parse(ProxyPortTextBox.Text); | int localPort = int.Parse(ProxyPortTextBox.Text); | ||||
Configuration.CheckServer(server); | Configuration.CheckServer(server); | ||||
Configuration.CheckLocalPort(localPort); | Configuration.CheckLocalPort(localPort); | ||||
_modifiedConfiguration.configs[_oldSelectedIndex] = server; | |||||
_modifiedConfiguration.configs[_lastSelectedIndex] = server; | |||||
_modifiedConfiguration.localPort = localPort; | _modifiedConfiguration.localPort = localPort; | ||||
return true; | return true; | ||||
} | } | ||||
catch (FormatException) | catch (FormatException) | ||||
@@ -113,12 +115,6 @@ namespace Shadowsocks.View | |||||
ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString(); | ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString(); | ||||
EncryptionSelect.Text = server.method ?? "aes-256-cfb"; | EncryptionSelect.Text = server.method ?? "aes-256-cfb"; | ||||
RemarksTextBox.Text = server.remarks; | RemarksTextBox.Text = server.remarks; | ||||
ServerGroupBox.Visible = true; | |||||
//IPTextBox.Focus(); | |||||
} | |||||
else | |||||
{ | |||||
ServerGroupBox.Visible = false; | |||||
} | } | ||||
} | } | ||||
@@ -135,12 +131,13 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
_modifiedConfiguration = controller.GetConfigurationCopy(); | _modifiedConfiguration = controller.GetConfigurationCopy(); | ||||
LoadConfiguration(_modifiedConfiguration); | LoadConfiguration(_modifiedConfiguration); | ||||
_oldSelectedIndex = _modifiedConfiguration.index; | |||||
if (_oldSelectedIndex < 0) | |||||
_lastSelectedIndex = _modifiedConfiguration.index; | |||||
if (_lastSelectedIndex < 0) | |||||
{ | { | ||||
_oldSelectedIndex = 0; | |||||
_lastSelectedIndex = 0; | |||||
} | } | ||||
ServersListBox.SelectedIndex = _oldSelectedIndex; | |||||
ServersListBox.SelectedIndex = _lastSelectedIndex; | |||||
UpdateMoveUpAndDownButton(); | |||||
LoadSelectedServer(); | LoadSelectedServer(); | ||||
} | } | ||||
@@ -151,7 +148,11 @@ namespace Shadowsocks.View | |||||
private void ServersListBox_SelectedIndexChanged(object sender, EventArgs e) | private void ServersListBox_SelectedIndexChanged(object sender, EventArgs e) | ||||
{ | { | ||||
if (_oldSelectedIndex == ServersListBox.SelectedIndex) | |||||
if (!ServersListBox.CanSelect) | |||||
{ | |||||
return; | |||||
} | |||||
if (_lastSelectedIndex == ServersListBox.SelectedIndex) | |||||
{ | { | ||||
// we are moving back to oldSelectedIndex or doing a force move | // we are moving back to oldSelectedIndex or doing a force move | ||||
return; | return; | ||||
@@ -159,11 +160,13 @@ namespace Shadowsocks.View | |||||
if (!SaveOldSelectedServer()) | if (!SaveOldSelectedServer()) | ||||
{ | { | ||||
// why this won't cause stack overflow? | // why this won't cause stack overflow? | ||||
ServersListBox.SelectedIndex = _oldSelectedIndex; | |||||
ServersListBox.SelectedIndex = _lastSelectedIndex; | |||||
return; | return; | ||||
} | } | ||||
ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].FriendlyName(); | |||||
UpdateMoveUpAndDownButton(); | |||||
LoadSelectedServer(); | LoadSelectedServer(); | ||||
_oldSelectedIndex = ServersListBox.SelectedIndex; | |||||
_lastSelectedIndex = ServersListBox.SelectedIndex; | |||||
} | } | ||||
private void AddButton_Click(object sender, EventArgs e) | private void AddButton_Click(object sender, EventArgs e) | ||||
@@ -176,29 +179,30 @@ namespace Shadowsocks.View | |||||
_modifiedConfiguration.configs.Add(server); | _modifiedConfiguration.configs.Add(server); | ||||
LoadConfiguration(_modifiedConfiguration); | LoadConfiguration(_modifiedConfiguration); | ||||
ServersListBox.SelectedIndex = _modifiedConfiguration.configs.Count - 1; | ServersListBox.SelectedIndex = _modifiedConfiguration.configs.Count - 1; | ||||
_oldSelectedIndex = ServersListBox.SelectedIndex; | |||||
_lastSelectedIndex = ServersListBox.SelectedIndex; | |||||
} | } | ||||
private void DeleteButton_Click(object sender, EventArgs e) | private void DeleteButton_Click(object sender, EventArgs e) | ||||
{ | { | ||||
_oldSelectedIndex = ServersListBox.SelectedIndex; | |||||
if (_oldSelectedIndex >= 0 && _oldSelectedIndex < _modifiedConfiguration.configs.Count) | |||||
_lastSelectedIndex = ServersListBox.SelectedIndex; | |||||
if (_lastSelectedIndex >= 0 && _lastSelectedIndex < _modifiedConfiguration.configs.Count) | |||||
{ | { | ||||
_modifiedConfiguration.configs.RemoveAt(_oldSelectedIndex); | |||||
_modifiedConfiguration.configs.RemoveAt(_lastSelectedIndex); | |||||
} | } | ||||
if (_oldSelectedIndex >= _modifiedConfiguration.configs.Count) | |||||
if (_lastSelectedIndex >= _modifiedConfiguration.configs.Count) | |||||
{ | { | ||||
// can be -1 | // can be -1 | ||||
_oldSelectedIndex = _modifiedConfiguration.configs.Count - 1; | |||||
_lastSelectedIndex = _modifiedConfiguration.configs.Count - 1; | |||||
} | } | ||||
ServersListBox.SelectedIndex = _oldSelectedIndex; | |||||
ServersListBox.SelectedIndex = _lastSelectedIndex; | |||||
LoadConfiguration(_modifiedConfiguration); | LoadConfiguration(_modifiedConfiguration); | ||||
ServersListBox.SelectedIndex = _oldSelectedIndex; | |||||
ServersListBox.SelectedIndex = _lastSelectedIndex; | |||||
LoadSelectedServer(); | LoadSelectedServer(); | ||||
} | } | ||||
private void OKButton_Click(object sender, EventArgs e) | private void OKButton_Click(object sender, EventArgs e) | ||||
{ | { | ||||
Server server = controller.GetCurrentServer(); | |||||
if (!SaveOldSelectedServer()) | if (!SaveOldSelectedServer()) | ||||
{ | { | ||||
return; | return; | ||||
@@ -209,6 +213,7 @@ namespace Shadowsocks.View | |||||
return; | return; | ||||
} | } | ||||
controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort); | controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort); | ||||
controller.SelectServerIndex(_modifiedConfiguration.configs.IndexOf(server)); | |||||
this.Close(); | this.Close(); | ||||
} | } | ||||
@@ -227,5 +232,70 @@ namespace Shadowsocks.View | |||||
controller.ConfigChanged -= controller_ConfigChanged; | controller.ConfigChanged -= controller_ConfigChanged; | ||||
} | } | ||||
private void MoveConfigItem(int step) | |||||
{ | |||||
int index = ServersListBox.SelectedIndex; | |||||
Server server = _modifiedConfiguration.configs[index]; | |||||
object item = ServersListBox.SelectedItem; | |||||
_modifiedConfiguration.configs.Remove(server); | |||||
_modifiedConfiguration.configs.Insert(index + step, server); | |||||
_modifiedConfiguration.index += step; | |||||
ServersListBox.BeginUpdate(); | |||||
ServersListBox.Enabled = false; | |||||
_lastSelectedIndex = index + step; | |||||
ServersListBox.Items.Remove(item); | |||||
ServersListBox.Items.Insert(index + step, item); | |||||
ServersListBox.Enabled = true; | |||||
ServersListBox.SelectedIndex = index + step; | |||||
ServersListBox.EndUpdate(); | |||||
UpdateMoveUpAndDownButton(); | |||||
} | |||||
private void UpdateMoveUpAndDownButton() | |||||
{ | |||||
if (ServersListBox.SelectedIndex == 0) | |||||
{ | |||||
MoveUpButton.Enabled = false; | |||||
} | |||||
else | |||||
{ | |||||
MoveUpButton.Enabled = true; | |||||
} | |||||
if (ServersListBox.SelectedIndex == ServersListBox.Items.Count - 1) | |||||
{ | |||||
MoveDownButton.Enabled = false; | |||||
} | |||||
else | |||||
{ | |||||
MoveDownButton.Enabled = true; | |||||
} | |||||
} | |||||
private void MoveUpButton_Click(object sender, EventArgs e) | |||||
{ | |||||
if (!SaveOldSelectedServer()) | |||||
{ | |||||
return; | |||||
} | |||||
if (ServersListBox.SelectedIndex > 0) | |||||
{ | |||||
MoveConfigItem(-1); // -1 means move backward | |||||
} | |||||
} | |||||
private void MoveDownButton_Click(object sender, EventArgs e) | |||||
{ | |||||
if (!SaveOldSelectedServer()) | |||||
{ | |||||
return; | |||||
} | |||||
if (ServersListBox.SelectedIndex < ServersListBox.Items.Count - 1) | |||||
{ | |||||
MoveConfigItem(+1); // +1 means move forward | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -112,9 +112,9 @@ | |||||
<value>2.0</value> | <value>2.0</value> | ||||
</resheader> | </resheader> | ||||
<resheader name="reader"> | <resheader name="reader"> | ||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
</resheader> | </resheader> | ||||
<resheader name="writer"> | <resheader name="writer"> | ||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
</resheader> | </resheader> | ||||
</root> | </root> |
@@ -29,69 +29,143 @@ | |||||
private void InitializeComponent() | private void InitializeComponent() | ||||
{ | { | ||||
this.components = new System.ComponentModel.Container(); | this.components = new System.ComponentModel.Container(); | ||||
this.textBox1 = new System.Windows.Forms.TextBox(); | |||||
this.LogMessageTextBox = new System.Windows.Forms.TextBox(); | |||||
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); | this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); | ||||
this.mainMenu1 = new System.Windows.Forms.MainMenu(this.components); | this.mainMenu1 = new System.Windows.Forms.MainMenu(this.components); | ||||
this.menuItem1 = new System.Windows.Forms.MenuItem(); | |||||
this.menuItem2 = new System.Windows.Forms.MenuItem(); | |||||
this.menuItem4 = new System.Windows.Forms.MenuItem(); | |||||
this.FileMenuItem = new System.Windows.Forms.MenuItem(); | |||||
this.OpenLocationMenuItem = new System.Windows.Forms.MenuItem(); | |||||
this.ExitMenuItem = new System.Windows.Forms.MenuItem(); | |||||
this.panel1 = new System.Windows.Forms.Panel(); | |||||
this.ChangeFontButton = new System.Windows.Forms.Button(); | |||||
this.CleanLogsButton = new System.Windows.Forms.Button(); | |||||
this.WrapTextCheckBox = new System.Windows.Forms.CheckBox(); | |||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); | |||||
this.TopMostCheckBox = new System.Windows.Forms.CheckBox(); | |||||
this.panel1.SuspendLayout(); | |||||
this.tableLayoutPanel1.SuspendLayout(); | |||||
this.SuspendLayout(); | this.SuspendLayout(); | ||||
// | // | ||||
// textBox1 | |||||
// | |||||
this.textBox1.BackColor = System.Drawing.Color.Black; | |||||
this.textBox1.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.textBox1.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||||
this.textBox1.ForeColor = System.Drawing.Color.White; | |||||
this.textBox1.Location = new System.Drawing.Point(0, 0); | |||||
this.textBox1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); | |||||
this.textBox1.MaxLength = 2147483647; | |||||
this.textBox1.Multiline = true; | |||||
this.textBox1.Name = "textBox1"; | |||||
this.textBox1.ReadOnly = true; | |||||
this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Both; | |||||
this.textBox1.Size = new System.Drawing.Size(820, 529); | |||||
this.textBox1.TabIndex = 0; | |||||
this.textBox1.WordWrap = false; | |||||
// LogMessageTextBox | |||||
// | |||||
this.LogMessageTextBox.BackColor = System.Drawing.Color.Black; | |||||
this.LogMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.LogMessageTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||||
this.LogMessageTextBox.ForeColor = System.Drawing.Color.White; | |||||
this.LogMessageTextBox.Location = new System.Drawing.Point(3, 43); | |||||
this.LogMessageTextBox.MaxLength = 2147483647; | |||||
this.LogMessageTextBox.Multiline = true; | |||||
this.LogMessageTextBox.Name = "LogMessageTextBox"; | |||||
this.LogMessageTextBox.ReadOnly = true; | |||||
this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; | |||||
this.LogMessageTextBox.Size = new System.Drawing.Size(541, 307); | |||||
this.LogMessageTextBox.TabIndex = 0; | |||||
this.LogMessageTextBox.WordWrap = false; | |||||
// | // | ||||
// contextMenuStrip1 | // contextMenuStrip1 | ||||
// | // | ||||
this.contextMenuStrip1.ImageScalingSize = new System.Drawing.Size(24, 24); | |||||
this.contextMenuStrip1.Name = "contextMenuStrip1"; | this.contextMenuStrip1.Name = "contextMenuStrip1"; | ||||
this.contextMenuStrip1.Size = new System.Drawing.Size(74, 4); | |||||
this.contextMenuStrip1.Size = new System.Drawing.Size(61, 4); | |||||
// | // | ||||
// mainMenu1 | // mainMenu1 | ||||
// | // | ||||
this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { | this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { | ||||
this.menuItem1}); | |||||
this.FileMenuItem}); | |||||
// | |||||
// FileMenuItem | |||||
// | |||||
this.FileMenuItem.Index = 0; | |||||
this.FileMenuItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { | |||||
this.OpenLocationMenuItem, | |||||
this.ExitMenuItem}); | |||||
this.FileMenuItem.Text = "&File"; | |||||
// | |||||
// OpenLocationMenuItem | |||||
// | |||||
this.OpenLocationMenuItem.Index = 0; | |||||
this.OpenLocationMenuItem.Text = "&Open Location"; | |||||
this.OpenLocationMenuItem.Click += new System.EventHandler(this.OpenLocationMenuItem_Click); | |||||
// | |||||
// ExitMenuItem | |||||
// | |||||
this.ExitMenuItem.Index = 1; | |||||
this.ExitMenuItem.Text = "E&xit"; | |||||
this.ExitMenuItem.Click += new System.EventHandler(this.ExitMenuItem_Click); | |||||
// | |||||
// panel1 | |||||
// | |||||
this.panel1.Controls.Add(this.TopMostCheckBox); | |||||
this.panel1.Controls.Add(this.ChangeFontButton); | |||||
this.panel1.Controls.Add(this.CleanLogsButton); | |||||
this.panel1.Controls.Add(this.WrapTextCheckBox); | |||||
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.panel1.Location = new System.Drawing.Point(3, 3); | |||||
this.panel1.Name = "panel1"; | |||||
this.panel1.Size = new System.Drawing.Size(541, 34); | |||||
this.panel1.TabIndex = 1; | |||||
// | |||||
// ChangeFontButton | |||||
// | |||||
this.ChangeFontButton.Location = new System.Drawing.Point(107, 4); | |||||
this.ChangeFontButton.Name = "ChangeFontButton"; | |||||
this.ChangeFontButton.Size = new System.Drawing.Size(75, 23); | |||||
this.ChangeFontButton.TabIndex = 2; | |||||
this.ChangeFontButton.Text = "&Font"; | |||||
this.ChangeFontButton.UseVisualStyleBackColor = true; | |||||
this.ChangeFontButton.Click += new System.EventHandler(this.ChangeFontButton_Click); | |||||
// | |||||
// CleanLogsButton | |||||
// | |||||
this.CleanLogsButton.Location = new System.Drawing.Point(9, 4); | |||||
this.CleanLogsButton.Name = "CleanLogsButton"; | |||||
this.CleanLogsButton.Size = new System.Drawing.Size(75, 23); | |||||
this.CleanLogsButton.TabIndex = 1; | |||||
this.CleanLogsButton.Text = "&Clean logs"; | |||||
this.CleanLogsButton.UseVisualStyleBackColor = true; | |||||
this.CleanLogsButton.Click += new System.EventHandler(this.CleanLogsButton_Click); | |||||
// | // | ||||
// menuItem1 | |||||
// WrapTextCheckBox | |||||
// | // | ||||
this.menuItem1.Index = 0; | |||||
this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { | |||||
this.menuItem2, | |||||
this.menuItem4}); | |||||
this.menuItem1.Text = "&File"; | |||||
this.WrapTextCheckBox.AutoSize = true; | |||||
this.WrapTextCheckBox.Location = new System.Drawing.Point(209, 9); | |||||
this.WrapTextCheckBox.Name = "WrapTextCheckBox"; | |||||
this.WrapTextCheckBox.Size = new System.Drawing.Size(78, 16); | |||||
this.WrapTextCheckBox.TabIndex = 0; | |||||
this.WrapTextCheckBox.Text = "&Wrap text"; | |||||
this.WrapTextCheckBox.UseVisualStyleBackColor = true; | |||||
this.WrapTextCheckBox.CheckedChanged += new System.EventHandler(this.WrapTextCheckBox_CheckedChanged); | |||||
// | // | ||||
// menuItem2 | |||||
// tableLayoutPanel1 | |||||
// | // | ||||
this.menuItem2.Index = 0; | |||||
this.menuItem2.Text = "&Open Location"; | |||||
this.menuItem2.Click += new System.EventHandler(this.menuItem2_Click); | |||||
this.tableLayoutPanel1.ColumnCount = 1; | |||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); | |||||
this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); | |||||
this.tableLayoutPanel1.Controls.Add(this.LogMessageTextBox, 0, 1); | |||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); | |||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1"; | |||||
this.tableLayoutPanel1.RowCount = 2; | |||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); | |||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | |||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(547, 353); | |||||
this.tableLayoutPanel1.TabIndex = 2; | |||||
// | // | ||||
// menuItem4 | |||||
// TopMostCheckBox | |||||
// | // | ||||
this.menuItem4.Index = 1; | |||||
this.menuItem4.Text = "E&xit"; | |||||
this.menuItem4.Click += new System.EventHandler(this.menuItem4_Click); | |||||
this.TopMostCheckBox.AutoSize = true; | |||||
this.TopMostCheckBox.Location = new System.Drawing.Point(311, 9); | |||||
this.TopMostCheckBox.Name = "TopMostCheckBox"; | |||||
this.TopMostCheckBox.Size = new System.Drawing.Size(72, 16); | |||||
this.TopMostCheckBox.TabIndex = 3; | |||||
this.TopMostCheckBox.Text = "&Top most"; | |||||
this.TopMostCheckBox.UseVisualStyleBackColor = true; | |||||
this.TopMostCheckBox.CheckedChanged += new System.EventHandler(this.TopMostCheckBox_CheckedChanged); | |||||
// | // | ||||
// LogForm | // LogForm | ||||
// | // | ||||
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F); | |||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); | |||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | ||||
this.ClientSize = new System.Drawing.Size(820, 529); | |||||
this.Controls.Add(this.textBox1); | |||||
this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); | |||||
this.ClientSize = new System.Drawing.Size(547, 353); | |||||
this.Controls.Add(this.tableLayoutPanel1); | |||||
this.Menu = this.mainMenu1; | this.Menu = this.mainMenu1; | ||||
this.Name = "LogForm"; | this.Name = "LogForm"; | ||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; | ||||
@@ -99,18 +173,27 @@ | |||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.LogForm_FormClosing); | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.LogForm_FormClosing); | ||||
this.Load += new System.EventHandler(this.LogForm_Load); | this.Load += new System.EventHandler(this.LogForm_Load); | ||||
this.Shown += new System.EventHandler(this.LogForm_Shown); | this.Shown += new System.EventHandler(this.LogForm_Shown); | ||||
this.panel1.ResumeLayout(false); | |||||
this.panel1.PerformLayout(); | |||||
this.tableLayoutPanel1.ResumeLayout(false); | |||||
this.tableLayoutPanel1.PerformLayout(); | |||||
this.ResumeLayout(false); | this.ResumeLayout(false); | ||||
this.PerformLayout(); | |||||
} | } | ||||
#endregion | #endregion | ||||
private System.Windows.Forms.TextBox textBox1; | |||||
private System.Windows.Forms.TextBox LogMessageTextBox; | |||||
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; | private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; | ||||
private System.Windows.Forms.MainMenu mainMenu1; | private System.Windows.Forms.MainMenu mainMenu1; | ||||
private System.Windows.Forms.MenuItem menuItem1; | |||||
private System.Windows.Forms.MenuItem menuItem2; | |||||
private System.Windows.Forms.MenuItem menuItem4; | |||||
private System.Windows.Forms.MenuItem FileMenuItem; | |||||
private System.Windows.Forms.MenuItem OpenLocationMenuItem; | |||||
private System.Windows.Forms.MenuItem ExitMenuItem; | |||||
private System.Windows.Forms.Panel panel1; | |||||
private System.Windows.Forms.CheckBox WrapTextCheckBox; | |||||
private System.Windows.Forms.Button CleanLogsButton; | |||||
private System.Windows.Forms.Button ChangeFontButton; | |||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; | |||||
private System.Windows.Forms.CheckBox TopMostCheckBox; | |||||
} | } | ||||
} | } |
@@ -1,4 +1,5 @@ | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Properties; | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.ComponentModel; | using System.ComponentModel; | ||||
@@ -23,6 +24,20 @@ namespace Shadowsocks.View | |||||
this.filename = filename; | this.filename = filename; | ||||
InitializeComponent(); | InitializeComponent(); | ||||
this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | ||||
UpdateTexts(); | |||||
} | |||||
private void UpdateTexts() | |||||
{ | |||||
FileMenuItem.Text = I18N.GetString("&File"); | |||||
OpenLocationMenuItem.Text = I18N.GetString("&Open Location"); | |||||
ExitMenuItem.Text = I18N.GetString("E&xit"); | |||||
CleanLogsButton.Text = I18N.GetString("&Clean logs"); | |||||
ChangeFontButton.Text = I18N.GetString("&Font"); | |||||
WrapTextCheckBox.Text = I18N.GetString("&Wrap text"); | |||||
TopMostCheckBox.Text = I18N.GetString("&Top most"); | |||||
this.Text = I18N.GetString("Log Viewer"); | |||||
} | } | ||||
private void Timer_Tick(object sender, EventArgs e) | private void Timer_Tick(object sender, EventArgs e) | ||||
@@ -43,9 +58,9 @@ namespace Shadowsocks.View | |||||
string line = ""; | string line = ""; | ||||
while ((line = reader.ReadLine()) != null) | while ((line = reader.ReadLine()) != null) | ||||
textBox1.AppendText(line + "\r\n"); | |||||
LogMessageTextBox.AppendText(line + "\r\n"); | |||||
textBox1.ScrollToCaret(); | |||||
LogMessageTextBox.ScrollToCaret(); | |||||
lastOffset = reader.BaseStream.Position; | lastOffset = reader.BaseStream.Position; | ||||
} | } | ||||
@@ -63,12 +78,12 @@ namespace Shadowsocks.View | |||||
while ((line = reader.ReadLine()) != null) | while ((line = reader.ReadLine()) != null) | ||||
{ | { | ||||
changed = true; | changed = true; | ||||
textBox1.AppendText(line + "\r\n"); | |||||
LogMessageTextBox.AppendText(line + "\r\n"); | |||||
} | } | ||||
if (changed) | if (changed) | ||||
{ | { | ||||
textBox1.ScrollToCaret(); | |||||
LogMessageTextBox.ScrollToCaret(); | |||||
} | } | ||||
lastOffset = reader.BaseStream.Position; | lastOffset = reader.BaseStream.Position; | ||||
@@ -89,25 +104,47 @@ namespace Shadowsocks.View | |||||
timer.Stop(); | timer.Stop(); | ||||
} | } | ||||
private void menuItem2_Click(object sender, EventArgs e) | |||||
private void OpenLocationMenuItem_Click(object sender, EventArgs e) | |||||
{ | { | ||||
string argument = @"/select, " + filename; | |||||
string argument = "/select, \"" + filename + "\""; | |||||
Console.WriteLine(argument); | |||||
System.Diagnostics.Process.Start("explorer.exe", argument); | System.Diagnostics.Process.Start("explorer.exe", argument); | ||||
} | } | ||||
private void menuItem3_Click(object sender, EventArgs e) | |||||
private void ExitMenuItem_Click(object sender, EventArgs e) | |||||
{ | { | ||||
this.Close(); | |||||
} | |||||
private void LogForm_Shown(object sender, EventArgs e) | |||||
{ | |||||
LogMessageTextBox.ScrollToCaret(); | |||||
} | } | ||||
private void menuItem4_Click(object sender, EventArgs e) | |||||
private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e) | |||||
{ | { | ||||
this.Close(); | |||||
LogMessageTextBox.WordWrap = WrapTextCheckBox.Checked; | |||||
LogMessageTextBox.ScrollToCaret(); | |||||
} | } | ||||
private void LogForm_Shown(object sender, EventArgs e) | |||||
private void CleanLogsButton_Click(object sender, EventArgs e) | |||||
{ | |||||
LogMessageTextBox.Clear(); | |||||
} | |||||
private void ChangeFontButton_Click(object sender, EventArgs e) | |||||
{ | |||||
FontDialog fd = new FontDialog(); | |||||
fd.Font = LogMessageTextBox.Font; | |||||
if (fd.ShowDialog() == DialogResult.OK) | |||||
{ | |||||
LogMessageTextBox.Font = fd.Font; | |||||
} | |||||
} | |||||
private void TopMostCheckBox_CheckedChanged(object sender, EventArgs e) | |||||
{ | { | ||||
textBox1.ScrollToCaret(); | |||||
this.TopMost = TopMostCheckBox.Checked; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -18,7 +18,7 @@ namespace Shadowsocks.View | |||||
// yes this is just a menu view controller | // yes this is just a menu view controller | ||||
// when config form is closed, it moves away from RAM | // when config form is closed, it moves away from RAM | ||||
// and it should just do anything related to the config form | // and it should just do anything related to the config form | ||||
private ShadowsocksController controller; | private ShadowsocksController controller; | ||||
private UpdateChecker updateChecker; | private UpdateChecker updateChecker; | ||||
@@ -29,6 +29,7 @@ namespace Shadowsocks.View | |||||
private MenuItem enableItem; | private MenuItem enableItem; | ||||
private MenuItem modeItem; | private MenuItem modeItem; | ||||
private MenuItem AutoStartupItem; | private MenuItem AutoStartupItem; | ||||
private MenuItem AvailabilityStatistics; | |||||
private MenuItem ShareOverLANItem; | private MenuItem ShareOverLANItem; | ||||
private MenuItem SeperatorItem; | private MenuItem SeperatorItem; | ||||
private MenuItem ConfigItem; | private MenuItem ConfigItem; | ||||
@@ -164,7 +165,6 @@ namespace Shadowsocks.View | |||||
this.SeperatorItem = new MenuItem("-"), | this.SeperatorItem = new MenuItem("-"), | ||||
this.ConfigItem = CreateMenuItem("Edit Servers...", new EventHandler(this.Config_Click)), | this.ConfigItem = CreateMenuItem("Edit Servers...", new EventHandler(this.Config_Click)), | ||||
CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)), | CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)), | ||||
CreateMenuItem("Statistics Stategy Options", new EventHandler(StatisticsStrategyOptionsItem_Click)), | |||||
CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click)) | CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click)) | ||||
}), | }), | ||||
CreateMenuGroup("PAC ", new MenuItem[] { | CreateMenuGroup("PAC ", new MenuItem[] { | ||||
@@ -178,6 +178,7 @@ namespace Shadowsocks.View | |||||
}), | }), | ||||
new MenuItem("-"), | new MenuItem("-"), | ||||
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), | this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), | ||||
this.AvailabilityStatistics = CreateMenuItem("Availability Statistics", new EventHandler(this.AvailabilityStatisticsItem_Click)), | |||||
this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)), | this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)), | ||||
new MenuItem("-"), | new MenuItem("-"), | ||||
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), | CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), | ||||
@@ -261,6 +262,7 @@ namespace Shadowsocks.View | |||||
PACModeItem.Checked = !config.global; | PACModeItem.Checked = !config.global; | ||||
ShareOverLANItem.Checked = config.shareOverLan; | ShareOverLANItem.Checked = config.shareOverLan; | ||||
AutoStartupItem.Checked = AutoStartup.Check(); | AutoStartupItem.Checked = AutoStartup.Check(); | ||||
AvailabilityStatistics.Checked = config.availabilityStatistics; | |||||
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | ||||
localPACItem.Checked = !onlinePACItem.Checked; | localPACItem.Checked = !onlinePACItem.Checked; | ||||
UpdatePACItemsEnabledStatus(); | UpdatePACItemsEnabledStatus(); | ||||
@@ -320,7 +322,7 @@ namespace Shadowsocks.View | |||||
void configForm_FormClosed(object sender, FormClosedEventArgs e) | void configForm_FormClosed(object sender, FormClosedEventArgs e) | ||||
{ | { | ||||
configForm = null; | configForm = null; | ||||
Util.Utils.ReleaseMemory(); | |||||
Util.Utils.ReleaseMemory(true); | |||||
ShowFirstTimeBalloon(); | ShowFirstTimeBalloon(); | ||||
} | } | ||||
@@ -424,14 +426,6 @@ namespace Shadowsocks.View | |||||
qrCodeForm.Show(); | qrCodeForm.Show(); | ||||
} | } | ||||
private void StatisticsStrategyOptionsItem_Click(object sender, EventArgs e) | |||||
{ | |||||
//TODO: Load options | |||||
var statisticsStrategyOptionsForm = new StatisticsStrategyConfigurationForm(controller); | |||||
statisticsStrategyOptionsForm.Show(); | |||||
//TODO: Save options | |||||
} | |||||
private void ScanQRCodeItem_Click(object sender, EventArgs e) | private void ScanQRCodeItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
foreach (Screen screen in Screen.AllScreens) | foreach (Screen screen in Screen.AllScreens) | ||||
@@ -526,12 +520,17 @@ namespace Shadowsocks.View | |||||
Process.Start(_urlToOpen); | Process.Start(_urlToOpen); | ||||
} | } | ||||
private void AutoStartupItem_Click(object sender, EventArgs e) { | |||||
AutoStartupItem.Checked = !AutoStartupItem.Checked; | |||||
if (!AutoStartup.Set(AutoStartupItem.Checked)) { | |||||
MessageBox.Show(I18N.GetString("Failed to update registry")); | |||||
} | |||||
} | |||||
private void AutoStartupItem_Click(object sender, EventArgs e) { | |||||
AutoStartupItem.Checked = !AutoStartupItem.Checked; | |||||
if (!AutoStartup.Set(AutoStartupItem.Checked)) { | |||||
MessageBox.Show(I18N.GetString("Failed to update registry")); | |||||
} | |||||
} | |||||
private void AvailabilityStatisticsItem_Click(object sender, EventArgs e) { | |||||
AvailabilityStatistics.Checked = !AvailabilityStatistics.Checked; | |||||
controller.ToggleAvailabilityStatistics(AvailabilityStatistics.Checked); | |||||
} | |||||
private void LocalPACItem_Click(object sender, EventArgs e) | private void LocalPACItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
@@ -242,7 +242,6 @@ namespace Shadowsocks.View | |||||
SetBitmap(bitmap, 255); | SetBitmap(bitmap, 255); | ||||
} | } | ||||
/// <para>Changes the current bitmap with a custom opacity level. Here is where all happens!</para> | /// <para>Changes the current bitmap with a custom opacity level. Here is where all happens!</para> | ||||
public void SetBitmap(Bitmap bitmap, byte opacity) | public void SetBitmap(Bitmap bitmap, byte opacity) | ||||
{ | { | ||||
@@ -288,7 +287,6 @@ namespace Shadowsocks.View | |||||
} | } | ||||
} | } | ||||
protected override CreateParams CreateParams | protected override CreateParams CreateParams | ||||
{ | { | ||||
get | get | ||||