Add tray icon and notifications

This commit is contained in:
Melon 2020-10-05 13:06:11 +01:00
parent 52150b29e9
commit 2573e3211a
4 changed files with 224 additions and 155 deletions

View File

@ -5,6 +5,7 @@ using System.Reflection;
using System.Threading; using System.Threading;
using Gtk; using Gtk;
using MelonVPNCore; using MelonVPNCore;
using Notify;
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
@ -16,7 +17,8 @@ public partial class MainWindow : Window
private TextView clientsListText; private TextView clientsListText;
private Thread startupCheckThread; private Thread startupCheckThread;
private ThreadWrapper wrapper; private ThreadWrapper wrapper;
private StatusIcon statusIcon; private StatusIcon trayIcon;
private bool ConnectedToVPN;
public static readonly string MelonIconImg = "MiniMelonVPNIcon.png"; public static readonly string MelonIconImg = "MiniMelonVPNIcon.png";
public static readonly string MelonOnlineImg = "MiniMelonVPNOnline.png"; public static readonly string MelonOnlineImg = "MiniMelonVPNOnline.png";
@ -31,7 +33,9 @@ public partial class MainWindow : Window
SetPosition(WindowPosition.CenterAlways); SetPosition(WindowPosition.CenterAlways);
Resizable = false; Resizable = false;
TypeHint = Gdk.WindowTypeHint.Normal; TypeHint = Gdk.WindowTypeHint.Dialog;
ShowAll();
Present(); Present();
statusLabel = new Label statusLabel = new Label
@ -105,170 +109,231 @@ public partial class MainWindow : Window
}; };
startupCheckThread.Start(); 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); Visible = !Visible;
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);
}
} }
string GenerateTable(string[][] a) private void OnTrayIconPopup(object o, PopupMenuArgs args)
{ {
if (a.Length == 0) return ""; Menu popupMenu = new Menu();
int maxlength = a[0][0].Length;
foreach (string b in a[0]) ImageMenuItem menuItemToggle = new ImageMenuItem(ConnectedToVPN ? "Disconnect" : "Connect");
{ Image toggleimg = new Image(ConnectedToVPN ? Stock.MediaPause : Stock.MediaPlay);
if (b.Length > maxlength) maxlength = b.Length; menuItemToggle.Image = toggleimg;
} popupMenu.Add(menuItemToggle);
string[] c = new string[a.Length]; menuItemToggle.Activated += OnToggleMenuItem;
for (int i = 0; i < a.Length; i++) c[i] = a[i][0].PadRight(maxlength, ' ') + a[i][1];
return string.Join("\n", c); 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()) bool oldState = ConnectedToVPN;
{ switch (s)
foreach (NetworkInterface f in NetworkInterface.GetAllNetworkInterfaces()) {
{ case ClientResponseState.Error:
IPInterfaceProperties p = f.GetIPProperties(); statusLabel.Text = "Client Error";
IPAddressInformationCollection addressesColl = p.AnycastAddresses; ConnectedToVPN = false;
foreach (IPAddressInformation ip in addressesColl) break;
{ case ClientResponseState.ServerError:
if (ip.Address.ToString().StartsWith("10.137.248.", StringComparison.CurrentCulture)) statusLabel.Text = "Server Error";
{ ConnectedToVPN = false;
return ip.Address.ToString(); 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 ""; }
}
protected void OnDeleteEvent(object sender, DeleteEventArgs a)
void StartupCheckMethod()
{ {
ClientResponseState s = Client.SendDataMessage(DataMessage.Status); if (ConnectedToVPN) Visible = false;
Console.WriteLine(s); else QuitApp();
Application.Invoke(delegate a.RetVal = true;
{ }
UpdateStatus(s);
}); protected void QuitApp()
} {
if (wrapper != null)
void Refresh() {
{ wrapper.Kill();
ClientResponseState s = Client.SendDataMessage(DataMessage.Status); wrapper.Join();
switch (s) }
{ if (trayIcon != null) trayIcon.Dispose();
case ClientResponseState.Error: Application.Quit();
case ClientResponseState.ServerError: }
UpdateStatus(s);
MessageDialog md = new MessageDialog(this, void OnStartClicked(object sender, EventArgs e)
DialogFlags.DestroyWithParent, MessageType.Error, {
ButtonsType.Ok, "Error stopping VPN. Is the daemon running?"); ClientResponseState s = Client.SendDataMessage(DataMessage.Start);
md.Run(); switch (s)
md.Destroy(); {
break; case ClientResponseState.Error:
case ClientResponseState.Online: case ClientResponseState.ServerError:
case ClientResponseState.Offline: case ClientResponseState.Offline:
UpdateStatus(s); UpdateStatus(s);
break; MessageDialog md = new MessageDialog(this,
} DialogFlags.DestroyWithParent, MessageType.Error,
} ButtonsType.Ok, "Error starting VPN. Is the daemon running?");
md.Run();
void UpdateStatus(ClientResponseState s) md.Destroy();
{ break;
switch (s) }
{ }
case ClientResponseState.Error:
statusLabel.Text = "Client Error"; void OnStopClicked(object sender, EventArgs e)
UpdateIcon(MelonIconImg); {
break; ClientResponseState s = Client.SendDataMessage(DataMessage.Stop);
case ClientResponseState.ServerError: switch (s)
statusLabel.Text = "Server Error"; {
UpdateIcon(MelonIconImg); case ClientResponseState.Error:
break; case ClientResponseState.ServerError:
case ClientResponseState.Blank: case ClientResponseState.Online:
statusLabel.Text = "No reply"; UpdateStatus(s);
UpdateIcon(MelonIconImg); MessageDialog md = new MessageDialog(this,
break; DialogFlags.DestroyWithParent, MessageType.Error,
case ClientResponseState.Online: ButtonsType.Ok, "Error stopping VPN. Is the daemon running?");
statusLabel.Text = "Online - " + GetVpnInternalIp(); md.Run();
UpdateIcon(MelonOnlineImg); md.Destroy();
break; break;
case ClientResponseState.Offline: }
statusLabel.Text = "Offline"; }
UpdateIcon(MelonIconImg);
break; void OnRefreshClicked(object sender, EventArgs e)
} {
} Refresh();
}
protected void OnDeleteEvent(object sender, DeleteEventArgs a)
{ void OnViewClientsClicked(object sender, EventArgs e)
if (wrapper != null) {
{ Refresh();
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();
} }
} }

View File

@ -60,6 +60,9 @@
<Reference Include="Newtonsoft.Json"> <Reference Include="Newtonsoft.Json">
<HintPath>..\..\..\net-libs\Newtonsoft.Json.dll</HintPath> <HintPath>..\..\..\net-libs\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
<Reference Include="libnotify.net">
<HintPath>..\..\..\net-libs\libnotify.net.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="gtk-gui\gui.stetic"> <EmbeddedResource Include="gtk-gui\gui.stetic">

View File

@ -6,7 +6,7 @@ namespace MelonVPNClient
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
Application.Init(); Application.Init("Melon VPN", ref args);
MainWindow win = new MainWindow(); MainWindow win = new MainWindow();
win.Show(); win.Show();
Application.Run(); Application.Run();

View File

@ -36,6 +36,7 @@ fi
sudo chown root:root /etc/melon-vpn/client.cfg sudo chown root:root /etc/melon-vpn/client.cfg
# copy more files # 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 /usr/lib/melon-vpn/
sudo cp MelonVPNClient/bin/Release/MelonVPNClient.exe.config /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/ sudo cp MelonVPNClient/bin/Release/MiniMelonVPNIcon.png /usr/lib/melon-vpn/