diff --git a/MelonVPNClient/MainWindow.cs b/MelonVPNClient/MainWindow.cs
index ebe8f8e..744face 100644
--- a/MelonVPNClient/MainWindow.cs
+++ b/MelonVPNClient/MainWindow.cs
@@ -5,6 +5,7 @@ using System.Reflection;
using System.Threading;
using Gtk;
using MelonVPNCore;
+using Notify;
public partial class MainWindow : Window
{
@@ -16,7 +17,8 @@ public partial class MainWindow : Window
private TextView clientsListText;
private Thread startupCheckThread;
private ThreadWrapper wrapper;
- private StatusIcon statusIcon;
+ private StatusIcon trayIcon;
+ private bool ConnectedToVPN;
public static readonly string MelonIconImg = "MiniMelonVPNIcon.png";
public static readonly string MelonOnlineImg = "MiniMelonVPNOnline.png";
@@ -31,7 +33,9 @@ public partial class MainWindow : Window
SetPosition(WindowPosition.CenterAlways);
Resizable = false;
- TypeHint = Gdk.WindowTypeHint.Normal;
+ TypeHint = Gdk.WindowTypeHint.Dialog;
+
+ ShowAll();
Present();
statusLabel = new Label
@@ -105,170 +109,231 @@ public partial class MainWindow : Window
};
startupCheckThread.Start();
- statusIcon = new StatusIcon
+ trayIcon = new StatusIcon
{
- Visible = true
+ Visible = true,
+ TooltipText = "Melon VPN"
};
+ trayIcon.Activate += OnActivateTrayIcon;
+ trayIcon.PopupMenu += OnTrayIconPopup;
}
- void UpdateIcon(string file)
+ private void OnActivateTrayIcon(object sender, EventArgs args)
{
- DirectoryInfo ExeLocation = Directory.GetParent(Assembly.GetEntryAssembly().Location);
- string IconPath = System.IO.Path.Combine(ExeLocation.FullName, file);
- Console.WriteLine("Trying to update icon to: " + IconPath);
- if (File.Exists(IconPath))
- {
- Icon = new Gdk.Pixbuf(IconPath);
- if (statusIcon != null) statusIcon.Icon = new Gdk.Pixbuf(IconPath);
- }
+ Visible = !Visible;
}
- string GenerateTable(string[][] a)
+ private void OnTrayIconPopup(object o, PopupMenuArgs args)
{
- if (a.Length == 0) return "";
- int maxlength = a[0][0].Length;
- foreach (string b in a[0])
- {
- if (b.Length > maxlength) maxlength = b.Length;
- }
- string[] c = new string[a.Length];
- for (int i = 0; i < a.Length; i++) c[i] = a[i][0].PadRight(maxlength, ' ') + a[i][1];
- return string.Join("\n", c);
+ Menu popupMenu = new Menu();
+
+ ImageMenuItem menuItemToggle = new ImageMenuItem(ConnectedToVPN ? "Disconnect" : "Connect");
+ Image toggleimg = new Image(ConnectedToVPN ? Stock.MediaPause : Stock.MediaPlay);
+ menuItemToggle.Image = toggleimg;
+ popupMenu.Add(menuItemToggle);
+ menuItemToggle.Activated += OnToggleMenuItem;
+
+ ImageMenuItem menuItemQuit = new ImageMenuItem("Quit");
+ Image appimg = new Image(Stock.Quit, IconSize.Menu);
+ menuItemQuit.Image = appimg;
+ popupMenu.Add(menuItemQuit);
+ menuItemQuit.Activated += delegate { QuitApp(); };
+
+ popupMenu.ShowAll();
+ popupMenu.Popup();
}
- string GetVpnInternalIp()
+ void OnToggleMenuItem(object sender, EventArgs args)
+ {
+ if (ConnectedToVPN) OnStopClicked(this, new EventArgs());
+ else OnStartClicked(this, new EventArgs());
+ }
+
+ void UpdateIcon(string file)
+ {
+ DirectoryInfo ExeLocation = Directory.GetParent(Assembly.GetEntryAssembly().Location);
+ string IconPath = System.IO.Path.Combine(ExeLocation.FullName, file);
+ Console.WriteLine("Trying to update icon to: " + IconPath);
+ if (File.Exists(IconPath))
+ {
+ Icon = new Gdk.Pixbuf(IconPath);
+ if (trayIcon != null) trayIcon.Icon = new Gdk.Pixbuf(IconPath);
+ }
+ }
+
+ string GenerateTable(string[][] a)
+ {
+ if (a.Length == 0) return "";
+ int maxlength = a[0][0].Length;
+ foreach (string b in a[0])
+ {
+ if (b.Length > maxlength) maxlength = b.Length;
+ }
+ string[] c = new string[a.Length];
+ for (int i = 0; i < a.Length; i++) c[i] = a[i][0].PadRight(maxlength, ' ') + a[i][1];
+ return string.Join("\n", c);
+ }
+
+ string GetVpnInternalIp()
+ {
+ if (NetworkInterface.GetIsNetworkAvailable())
+ {
+ foreach (NetworkInterface f in NetworkInterface.GetAllNetworkInterfaces())
+ {
+ IPInterfaceProperties p = f.GetIPProperties();
+ IPAddressInformationCollection addressesColl = p.AnycastAddresses;
+ foreach (IPAddressInformation ip in addressesColl)
+ {
+ if (ip.Address.ToString().StartsWith("10.137.248.", StringComparison.CurrentCulture))
+ {
+ return ip.Address.ToString();
+ }
+ }
+ }
+ }
+ return "";
+ }
+
+ void StartupCheckMethod()
+ {
+ ClientResponseState s = Client.SendDataMessage(DataMessage.Status);
+ Console.WriteLine(s);
+ Application.Invoke(delegate
+ {
+ UpdateStatus(s);
+ });
+ }
+
+ void Refresh()
+ {
+ ClientResponseState s = Client.SendDataMessage(DataMessage.Status);
+ switch (s)
+ {
+ case ClientResponseState.Error:
+ case ClientResponseState.ServerError:
+ UpdateStatus(s);
+ MessageDialog md = new MessageDialog(this,
+ DialogFlags.DestroyWithParent, MessageType.Error,
+ ButtonsType.Ok, "Error stopping VPN. Is the daemon running?");
+ md.Run();
+ md.Destroy();
+ break;
+ case ClientResponseState.Online:
+ case ClientResponseState.Offline:
+ UpdateStatus(s);
+ break;
+ }
+ }
+
+ void UpdateStatus(ClientResponseState s)
{
- if (NetworkInterface.GetIsNetworkAvailable())
- {
- foreach (NetworkInterface f in NetworkInterface.GetAllNetworkInterfaces())
- {
- IPInterfaceProperties p = f.GetIPProperties();
- IPAddressInformationCollection addressesColl = p.AnycastAddresses;
- foreach (IPAddressInformation ip in addressesColl)
- {
- if (ip.Address.ToString().StartsWith("10.137.248.", StringComparison.CurrentCulture))
- {
- return ip.Address.ToString();
- }
- }
+ bool oldState = ConnectedToVPN;
+ switch (s)
+ {
+ case ClientResponseState.Error:
+ statusLabel.Text = "Client Error";
+ ConnectedToVPN = false;
+ break;
+ case ClientResponseState.ServerError:
+ statusLabel.Text = "Server Error";
+ ConnectedToVPN = false;
+ break;
+ case ClientResponseState.Blank:
+ statusLabel.Text = "No reply";
+ ConnectedToVPN = false;
+ break;
+ case ClientResponseState.Online:
+ statusLabel.Text = "Online - " + GetVpnInternalIp();
+ ConnectedToVPN = true;
+ break;
+ case ClientResponseState.Offline:
+ statusLabel.Text = "Offline";
+ ConnectedToVPN = false;
+ break;
+ }
+ if (ConnectedToVPN) UpdateIcon(MelonOnlineImg);
+ else UpdateIcon(MelonIconImg);
+ NotifyStateUpdate(oldState,ConnectedToVPN);
+ }
+
+ void NotifyStateUpdate(bool old, bool current)
+ {
+ if(old!=current)
+ {
+ if(current==true)
+ {
+ Notification pop = new Notification("Melon VPN", "Connected to the VPN");
+ pop.Show();
+ }
+ else
+ {
+ Notification pop = new Notification("Melon VPN", "Disconnected from the VPN");
+ pop.Show();
}
- }
- return "";
- }
-
- void StartupCheckMethod()
+ }
+ }
+
+ protected void OnDeleteEvent(object sender, DeleteEventArgs a)
{
- ClientResponseState s = Client.SendDataMessage(DataMessage.Status);
- Console.WriteLine(s);
- Application.Invoke(delegate
- {
- UpdateStatus(s);
- });
- }
-
- void Refresh()
- {
- ClientResponseState s = Client.SendDataMessage(DataMessage.Status);
- switch (s)
- {
- case ClientResponseState.Error:
- case ClientResponseState.ServerError:
- UpdateStatus(s);
- MessageDialog md = new MessageDialog(this,
- DialogFlags.DestroyWithParent, MessageType.Error,
- ButtonsType.Ok, "Error stopping VPN. Is the daemon running?");
- md.Run();
- md.Destroy();
- break;
- case ClientResponseState.Online:
- case ClientResponseState.Offline:
- UpdateStatus(s);
- break;
- }
- }
-
- void UpdateStatus(ClientResponseState s)
- {
- switch (s)
- {
- case ClientResponseState.Error:
- statusLabel.Text = "Client Error";
- UpdateIcon(MelonIconImg);
- break;
- case ClientResponseState.ServerError:
- statusLabel.Text = "Server Error";
- UpdateIcon(MelonIconImg);
- break;
- case ClientResponseState.Blank:
- statusLabel.Text = "No reply";
- UpdateIcon(MelonIconImg);
- break;
- case ClientResponseState.Online:
- statusLabel.Text = "Online - " + GetVpnInternalIp();
- UpdateIcon(MelonOnlineImg);
- break;
- case ClientResponseState.Offline:
- statusLabel.Text = "Offline";
- UpdateIcon(MelonIconImg);
- break;
- }
- }
-
- protected void OnDeleteEvent(object sender, DeleteEventArgs a)
- {
- if (wrapper != null)
- {
- wrapper.Kill();
- wrapper.Join();
- }
- if (statusIcon != null) statusIcon.Dispose();
- Application.Quit();
- a.RetVal = true;
- }
-
- void OnStartClicked(object sender, EventArgs e)
- {
- ClientResponseState s = Client.SendDataMessage(DataMessage.Start);
- switch (s)
- {
- case ClientResponseState.Error:
- case ClientResponseState.ServerError:
- case ClientResponseState.Offline:
- UpdateStatus(s);
- MessageDialog md = new MessageDialog(this,
- DialogFlags.DestroyWithParent, MessageType.Error,
- ButtonsType.Ok, "Error starting VPN. Is the daemon running?");
- md.Run();
- md.Destroy();
- break;
- }
- }
-
- void OnStopClicked(object sender, EventArgs e)
- {
- ClientResponseState s = Client.SendDataMessage(DataMessage.Stop);
- switch (s)
- {
- case ClientResponseState.Error:
- case ClientResponseState.ServerError:
- case ClientResponseState.Online:
- UpdateStatus(s);
- MessageDialog md = new MessageDialog(this,
- DialogFlags.DestroyWithParent, MessageType.Error,
- ButtonsType.Ok, "Error stopping VPN. Is the daemon running?");
- md.Run();
- md.Destroy();
- break;
- }
- }
-
- void OnRefreshClicked(object sender, EventArgs e)
- {
- Refresh();
- }
-
- void OnViewClientsClicked(object sender, EventArgs e)
- {
- Refresh();
+ if (ConnectedToVPN) Visible = false;
+ else QuitApp();
+ a.RetVal = true;
+ }
+
+ protected void QuitApp()
+ {
+ if (wrapper != null)
+ {
+ wrapper.Kill();
+ wrapper.Join();
+ }
+ if (trayIcon != null) trayIcon.Dispose();
+ Application.Quit();
+ }
+
+ void OnStartClicked(object sender, EventArgs e)
+ {
+ ClientResponseState s = Client.SendDataMessage(DataMessage.Start);
+ switch (s)
+ {
+ case ClientResponseState.Error:
+ case ClientResponseState.ServerError:
+ case ClientResponseState.Offline:
+ UpdateStatus(s);
+ MessageDialog md = new MessageDialog(this,
+ DialogFlags.DestroyWithParent, MessageType.Error,
+ ButtonsType.Ok, "Error starting VPN. Is the daemon running?");
+ md.Run();
+ md.Destroy();
+ break;
+ }
+ }
+
+ void OnStopClicked(object sender, EventArgs e)
+ {
+ ClientResponseState s = Client.SendDataMessage(DataMessage.Stop);
+ switch (s)
+ {
+ case ClientResponseState.Error:
+ case ClientResponseState.ServerError:
+ case ClientResponseState.Online:
+ UpdateStatus(s);
+ MessageDialog md = new MessageDialog(this,
+ DialogFlags.DestroyWithParent, MessageType.Error,
+ ButtonsType.Ok, "Error stopping VPN. Is the daemon running?");
+ md.Run();
+ md.Destroy();
+ break;
+ }
+ }
+
+ void OnRefreshClicked(object sender, EventArgs e)
+ {
+ Refresh();
+ }
+
+ void OnViewClientsClicked(object sender, EventArgs e)
+ {
+ Refresh();
}
}
diff --git a/MelonVPNClient/MelonVPNClient.csproj b/MelonVPNClient/MelonVPNClient.csproj
index 738c40d..57600f4 100644
--- a/MelonVPNClient/MelonVPNClient.csproj
+++ b/MelonVPNClient/MelonVPNClient.csproj
@@ -60,6 +60,9 @@
..\..\..\net-libs\Newtonsoft.Json.dll
+
+ ..\..\..\net-libs\libnotify.net.dll
+
diff --git a/MelonVPNClient/Program.cs b/MelonVPNClient/Program.cs
index d7679ba..cae1fd6 100644
--- a/MelonVPNClient/Program.cs
+++ b/MelonVPNClient/Program.cs
@@ -6,7 +6,7 @@ namespace MelonVPNClient
{
public static void Main(string[] args)
{
- Application.Init();
+ Application.Init("Melon VPN", ref args);
MainWindow win = new MainWindow();
win.Show();
Application.Run();
diff --git a/install-components b/install-components
index a413329..b6ac914 100755
--- a/install-components
+++ b/install-components
@@ -36,6 +36,7 @@ fi
sudo chown root:root /etc/melon-vpn/client.cfg
# copy more files
+sudo cp MelonVPNClient/bin/Release/libnotify.net.dll /usr/lib/melon-vpn/
sudo cp MelonVPNClient/bin/Release/MelonVPNClient.exe /usr/lib/melon-vpn/
sudo cp MelonVPNClient/bin/Release/MelonVPNClient.exe.config /usr/lib/melon-vpn/
sudo cp MelonVPNClient/bin/Release/MiniMelonVPNIcon.png /usr/lib/melon-vpn/