Use libappindicator for tray icon
This commit is contained in:
parent
2573e3211a
commit
7c0b73ea8c
@ -3,7 +3,9 @@ using System.IO;
|
|||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using AppIndicator3;
|
||||||
using Gtk;
|
using Gtk;
|
||||||
|
using MelonVPNClient;
|
||||||
using MelonVPNCore;
|
using MelonVPNCore;
|
||||||
using Notify;
|
using Notify;
|
||||||
|
|
||||||
@ -17,7 +19,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 trayIcon;
|
private Indicator trayIcon;
|
||||||
|
private PopupMenu trayMenu;
|
||||||
private bool ConnectedToVPN;
|
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";
|
||||||
@ -109,231 +112,236 @@ public partial class MainWindow : Window
|
|||||||
};
|
};
|
||||||
startupCheckThread.Start();
|
startupCheckThread.Start();
|
||||||
|
|
||||||
trayIcon = new StatusIcon
|
trayMenu = new PopupMenu(OnActivateTrayIcon, OnToggleMenuItem, QuitApp);
|
||||||
{
|
trayIcon = new Indicator("melonvpn", "MiniMelonVPNIcon", (int)IndicatorCategory.ApplicationStatus)
|
||||||
Visible = true,
|
{
|
||||||
TooltipText = "Melon VPN"
|
Menu = trayMenu.GetMenu(),
|
||||||
|
Status = (int)IndicatorStatus.Active
|
||||||
};
|
};
|
||||||
trayIcon.Activate += OnActivateTrayIcon;
|
|
||||||
trayIcon.PopupMenu += OnTrayIconPopup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnActivateTrayIcon(object sender, EventArgs args)
|
private void OnActivateTrayIcon()
|
||||||
{
|
{
|
||||||
Visible = !Visible;
|
Visible = !Visible;
|
||||||
|
UpdateTrayMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnToggleMenuItem()
|
||||||
|
{
|
||||||
|
if (ConnectedToVPN) OnStopClicked(this, new EventArgs());
|
||||||
|
else OnStartClicked(this, new EventArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTrayIconPopup(object o, PopupMenuArgs args)
|
void UpdateTrayMenu()
|
||||||
{
|
{
|
||||||
Menu popupMenu = new Menu();
|
if (trayMenu != null) trayMenu.Update(ConnectedToVPN, Visible);
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnToggleMenuItem(object sender, EventArgs args)
|
void UpdateIcon(string file)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
bool oldState = ConnectedToVPN;
|
DirectoryInfo ExeLocation = Directory.GetParent(Assembly.GetEntryAssembly().Location);
|
||||||
switch (s)
|
string IconPath = System.IO.Path.Combine(ExeLocation.FullName, file);
|
||||||
{
|
Console.WriteLine("Trying to update icon to: " + IconPath);
|
||||||
case ClientResponseState.Error:
|
if (File.Exists(IconPath))
|
||||||
statusLabel.Text = "Client Error";
|
{
|
||||||
ConnectedToVPN = false;
|
Icon = new Gdk.Pixbuf(IconPath);
|
||||||
break;
|
if (trayIcon != null) trayIcon.Icon = file.Replace(".png", "");
|
||||||
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)
|
string GenerateTable(string[][] a)
|
||||||
{
|
{
|
||||||
if(old!=current)
|
if (a.Length == 0) return "";
|
||||||
{
|
int maxlength = a[0][0].Length;
|
||||||
if(current==true)
|
foreach (string b in a[0])
|
||||||
{
|
{
|
||||||
Notification pop = new Notification("Melon VPN", "Connected to the VPN");
|
if (b.Length > maxlength) maxlength = b.Length;
|
||||||
pop.Show();
|
}
|
||||||
}
|
string[] c = new string[a.Length];
|
||||||
else
|
for (int i = 0; i < a.Length; i++) c[i] = a[i][0].PadRight(maxlength, ' ') + a[i][1];
|
||||||
{
|
return string.Join("\n", c);
|
||||||
Notification pop = new Notification("Melon VPN", "Disconnected from the VPN");
|
}
|
||||||
pop.Show();
|
|
||||||
|
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 "";
|
||||||
|
}
|
||||||
protected void OnDeleteEvent(object sender, DeleteEventArgs a)
|
|
||||||
|
void StartupCheckMethod()
|
||||||
{
|
{
|
||||||
if (ConnectedToVPN) Visible = false;
|
ClientResponseState s = Client.SendDataMessage(DataMessage.Status);
|
||||||
else QuitApp();
|
Console.WriteLine(s);
|
||||||
a.RetVal = true;
|
Application.Invoke(delegate
|
||||||
}
|
{
|
||||||
|
UpdateStatus(s);
|
||||||
protected void QuitApp()
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
UpdateTrayMenu();
|
||||||
|
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();
|
||||||
|
CloseNotificationAfterWait(pop, 1000);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Notification pop = new Notification("Melon VPN", "Disconnected from the VPN");
|
||||||
|
pop.Show();
|
||||||
|
CloseNotificationAfterWait(pop, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseNotificationAfterWait(Notification pop, int timeout)
|
||||||
{
|
{
|
||||||
if (wrapper != null)
|
Thread wait = new Thread(() =>
|
||||||
{
|
{
|
||||||
wrapper.Kill();
|
Thread.Sleep(timeout);
|
||||||
wrapper.Join();
|
pop.Close();
|
||||||
}
|
})
|
||||||
if (trayIcon != null) trayIcon.Dispose();
|
|
||||||
Application.Quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnStartClicked(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
ClientResponseState s = Client.SendDataMessage(DataMessage.Start);
|
|
||||||
switch (s)
|
|
||||||
{
|
{
|
||||||
case ClientResponseState.Error:
|
IsBackground = true
|
||||||
case ClientResponseState.ServerError:
|
};
|
||||||
case ClientResponseState.Offline:
|
wait.Start();
|
||||||
UpdateStatus(s);
|
}
|
||||||
MessageDialog md = new MessageDialog(this,
|
|
||||||
DialogFlags.DestroyWithParent, MessageType.Error,
|
protected void OnDeleteEvent(object sender, DeleteEventArgs a)
|
||||||
ButtonsType.Ok, "Error starting VPN. Is the daemon running?");
|
{
|
||||||
md.Run();
|
if (ConnectedToVPN)
|
||||||
md.Destroy();
|
{
|
||||||
break;
|
Visible = false;
|
||||||
}
|
UpdateTrayMenu();
|
||||||
}
|
}
|
||||||
|
else QuitApp();
|
||||||
void OnStopClicked(object sender, EventArgs e)
|
a.RetVal = true;
|
||||||
{
|
}
|
||||||
ClientResponseState s = Client.SendDataMessage(DataMessage.Stop);
|
|
||||||
switch (s)
|
protected void QuitApp()
|
||||||
{
|
{
|
||||||
case ClientResponseState.Error:
|
if (wrapper != null)
|
||||||
case ClientResponseState.ServerError:
|
{
|
||||||
case ClientResponseState.Online:
|
wrapper.Kill();
|
||||||
UpdateStatus(s);
|
wrapper.Join();
|
||||||
MessageDialog md = new MessageDialog(this,
|
}
|
||||||
DialogFlags.DestroyWithParent, MessageType.Error,
|
if (trayIcon != null) trayIcon.Dispose();
|
||||||
ButtonsType.Ok, "Error stopping VPN. Is the daemon running?");
|
Application.Quit();
|
||||||
md.Run();
|
}
|
||||||
md.Destroy();
|
|
||||||
break;
|
void OnStartClicked(object sender, EventArgs e)
|
||||||
}
|
{
|
||||||
}
|
ClientResponseState s = Client.SendDataMessage(DataMessage.Start);
|
||||||
|
switch (s)
|
||||||
void OnRefreshClicked(object sender, EventArgs e)
|
{
|
||||||
{
|
case ClientResponseState.Error:
|
||||||
Refresh();
|
case ClientResponseState.ServerError:
|
||||||
}
|
case ClientResponseState.Offline:
|
||||||
|
UpdateStatus(s);
|
||||||
void OnViewClientsClicked(object sender, EventArgs e)
|
MessageDialog md = new MessageDialog(this,
|
||||||
{
|
DialogFlags.DestroyWithParent, MessageType.Error,
|
||||||
Refresh();
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,9 @@
|
|||||||
<Reference Include="libnotify.net">
|
<Reference Include="libnotify.net">
|
||||||
<HintPath>..\..\..\net-libs\libnotify.net.dll</HintPath>
|
<HintPath>..\..\..\net-libs\libnotify.net.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="appindicator3-sharp">
|
||||||
|
<HintPath>..\..\..\..\..\lib\mono\gac\appindicator3-sharp\12.10.0.0__bf81553d61d0bd64\appindicator3-sharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="gtk-gui\gui.stetic">
|
<EmbeddedResource Include="gtk-gui\gui.stetic">
|
||||||
@ -75,6 +78,7 @@
|
|||||||
<Compile Include="gtk-gui\MainWindow.cs" />
|
<Compile Include="gtk-gui\MainWindow.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="PopupMenu.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\MelonVPNCore\MelonVPNCore.csproj">
|
<ProjectReference Include="..\MelonVPNCore\MelonVPNCore.csproj">
|
||||||
|
39
MelonVPNClient/PopupMenu.cs
Normal file
39
MelonVPNClient/PopupMenu.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using Gtk;
|
||||||
|
|
||||||
|
namespace MelonVPNClient
|
||||||
|
{
|
||||||
|
public class PopupMenu
|
||||||
|
{
|
||||||
|
private Menu me;
|
||||||
|
private MenuItem openItem;
|
||||||
|
private MenuItem toggleItem;
|
||||||
|
private MenuItem quitItem;
|
||||||
|
|
||||||
|
public PopupMenu(System.Action open, System.Action toggle, System.Action quit)
|
||||||
|
{
|
||||||
|
me = new Menu();
|
||||||
|
|
||||||
|
openItem = new MenuItem("...");
|
||||||
|
me.Add(openItem);
|
||||||
|
openItem.Activated += delegate { open(); };
|
||||||
|
|
||||||
|
toggleItem = new MenuItem("...");
|
||||||
|
me.Add(toggleItem);
|
||||||
|
toggleItem.Activated += delegate { toggle(); };
|
||||||
|
|
||||||
|
quitItem = new MenuItem("Quit");
|
||||||
|
me.Add(quitItem);
|
||||||
|
quitItem.Activated += delegate { quit(); };
|
||||||
|
|
||||||
|
me.ShowAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(bool ConnectedToVPN, bool Visible)
|
||||||
|
{
|
||||||
|
openItem.Label = Visible ? "Hide" : "Show";
|
||||||
|
toggleItem.Label = ConnectedToVPN ? "Disconnect" : "Connect";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Menu GetMenu() => me;
|
||||||
|
}
|
||||||
|
}
|
@ -43,6 +43,11 @@ sudo cp MelonVPNClient/bin/Release/MiniMelonVPNIcon.png /usr/lib/melon-vpn/
|
|||||||
sudo cp MelonVPNClient/bin/Release/MiniMelonVPNOnline.png /usr/lib/melon-vpn/
|
sudo cp MelonVPNClient/bin/Release/MiniMelonVPNOnline.png /usr/lib/melon-vpn/
|
||||||
sudo cp MelonVPNClient/bin/Release/MelonVPNDesktopIcon.png /usr/lib/melon-vpn/
|
sudo cp MelonVPNClient/bin/Release/MelonVPNDesktopIcon.png /usr/lib/melon-vpn/
|
||||||
|
|
||||||
|
# copy app indicator icons
|
||||||
|
mkdir -p ~/.local/share/icons/hicolor/128x128/apps/
|
||||||
|
cp MelonVPNClient/bin/Release/MiniMelonVPNIcon.png ~/.local/share/icons/hicolor/128x128/apps/
|
||||||
|
cp MelonVPNClient/bin/Release/MiniMelonVPNOnline.png ~/.local/share/icons/hicolor/128x128/apps/
|
||||||
|
|
||||||
# make all exe files in melon vpn projects executable
|
# make all exe files in melon vpn projects executable
|
||||||
sudo chmod +x /usr/lib/melon-vpn/*.exe
|
sudo chmod +x /usr/lib/melon-vpn/*.exe
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user