forked from clan/clan-core
Merge pull request 'UI: Added tray icon' (#831) from Qubasa-main into main
This commit is contained in:
commit
33787a6aab
@ -177,7 +177,7 @@ class Machine:
|
|||||||
[
|
[
|
||||||
"--impure",
|
"--impure",
|
||||||
"--expr",
|
"--expr",
|
||||||
f'(builtins.fetchTree {{ type = "file"; url = "{config_json.name}"; }}).narHash',
|
f'(builtins.fetchTree {{ type = "file"; url = "file://{config_json.name}"; }}).narHash',
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
).stdout.strip()
|
).stdout.strip()
|
||||||
|
@ -12,7 +12,6 @@ from collections.abc import Iterator
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from typing import IO
|
|
||||||
|
|
||||||
from ..cmd import Log, run
|
from ..cmd import Log, run
|
||||||
from ..dirs import machine_gcroot, module_root, user_cache_dir, vm_state_dir
|
from ..dirs import machine_gcroot, module_root, user_cache_dir, vm_state_dir
|
||||||
@ -147,9 +146,7 @@ def qemu_command(
|
|||||||
|
|
||||||
|
|
||||||
# TODO move this to the Machines class
|
# TODO move this to the Machines class
|
||||||
def get_vm_create_info(
|
def build_vm(machine: Machine, vm: VmConfig, nix_options: list[str]) -> dict[str, str]:
|
||||||
machine: Machine, vm: VmConfig, nix_options: list[str]
|
|
||||||
) -> dict[str, str]:
|
|
||||||
config = nix_config()
|
config = nix_config()
|
||||||
system = config["system"]
|
system = config["system"]
|
||||||
|
|
||||||
@ -272,19 +269,12 @@ def start_waypipe(cid: int | None, title_prefix: str) -> Iterator[None]:
|
|||||||
proc.kill()
|
proc.kill()
|
||||||
|
|
||||||
|
|
||||||
def run_vm(
|
def run_vm(vm: VmConfig, nix_options: list[str] = []) -> None:
|
||||||
vm: VmConfig,
|
|
||||||
nix_options: list[str] = [],
|
|
||||||
log_fd: IO[str] | None = None,
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
log_fd can be used to stream the output of all commands to a UI
|
|
||||||
"""
|
|
||||||
machine = Machine(vm.machine_name, vm.flake_url)
|
machine = Machine(vm.machine_name, vm.flake_url)
|
||||||
log.debug(f"Creating VM for {machine}")
|
log.debug(f"Creating VM for {machine}")
|
||||||
|
|
||||||
# TODO: We should get this from the vm argument
|
# TODO: We should get this from the vm argument
|
||||||
nixos_config = get_vm_create_info(machine, vm, nix_options)
|
nixos_config = build_vm(machine, vm, nix_options)
|
||||||
|
|
||||||
# store the temporary rootfs inside XDG_CACHE_HOME on the host
|
# store the temporary rootfs inside XDG_CACHE_HOME on the host
|
||||||
# otherwise, when using /tmp, we risk running out of memory
|
# otherwise, when using /tmp, we risk running out of memory
|
||||||
|
@ -26,7 +26,6 @@ def test_history_add(
|
|||||||
"add",
|
"add",
|
||||||
str(uri),
|
str(uri),
|
||||||
]
|
]
|
||||||
|
|
||||||
cli.run(cmd)
|
cli.run(cmd)
|
||||||
|
|
||||||
history_file = user_history_file()
|
history_file = user_history_file()
|
||||||
|
@ -6,6 +6,8 @@ from .app import MainApplication
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Trayicon support
|
||||||
|
# https://github.com/nicotine-plus/nicotine-plus/blob/b08552584eb6f35782ad77da93ae4aae3362bf64/pynicotine/gtkgui/widgets/trayicon.py#L982
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
app = MainApplication()
|
app = MainApplication()
|
||||||
return app.run(sys.argv)
|
return app.run(sys.argv)
|
||||||
|
@ -16,6 +16,7 @@ from clan_vm_manager.models.interfaces import ClanConfig
|
|||||||
from clan_vm_manager.models.use_join import GLib, GObject
|
from clan_vm_manager.models.use_join import GLib, GObject
|
||||||
from clan_vm_manager.models.use_vms import VMS
|
from clan_vm_manager.models.use_vms import VMS
|
||||||
|
|
||||||
|
from .trayicon import TrayIcon
|
||||||
from .windows.main_window import MainWindow
|
from .windows.main_window import MainWindow
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -29,9 +30,11 @@ class MainApplication(Adw.Application):
|
|||||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
*args,
|
*args,
|
||||||
|
application_id="lol.clan.vm.manager",
|
||||||
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
|
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
self.tray_icon: TrayIcon | None = None
|
||||||
|
|
||||||
self.add_main_option(
|
self.add_main_option(
|
||||||
"debug",
|
"debug",
|
||||||
@ -42,7 +45,7 @@ class MainApplication(Adw.Application):
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.win: Adw.ApplicationWindow | None = None
|
self.window: Adw.ApplicationWindow | None = None
|
||||||
self.connect("shutdown", self.on_shutdown)
|
self.connect("shutdown", self.on_shutdown)
|
||||||
self.connect("activate", self.show_window)
|
self.connect("activate", self.show_window)
|
||||||
|
|
||||||
@ -70,19 +73,33 @@ class MainApplication(Adw.Application):
|
|||||||
|
|
||||||
def on_shutdown(self, app: Gtk.Application) -> None:
|
def on_shutdown(self, app: Gtk.Application) -> None:
|
||||||
log.debug("Shutting down")
|
log.debug("Shutting down")
|
||||||
|
|
||||||
|
if self.tray_icon is not None:
|
||||||
|
self.tray_icon.destroy()
|
||||||
|
|
||||||
VMS.use().kill_all()
|
VMS.use().kill_all()
|
||||||
|
|
||||||
|
def on_window_hide_unhide(self, *_args: Any) -> None:
|
||||||
|
assert self.window is not None
|
||||||
|
if self.window.is_visible():
|
||||||
|
self.window.hide()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.window.present()
|
||||||
|
|
||||||
|
def dummy_menu_entry(self) -> None:
|
||||||
|
log.info("Dummy menu entry called")
|
||||||
|
|
||||||
def do_activate(self) -> None:
|
def do_activate(self) -> None:
|
||||||
self.show_window()
|
self.show_window()
|
||||||
|
|
||||||
def show_window(self, app: Any = None) -> None:
|
def show_window(self, app: Any = None) -> None:
|
||||||
if not self.win:
|
if not self.window:
|
||||||
self.init_style()
|
self.init_style()
|
||||||
self.win = MainWindow(config=ClanConfig(initial_view="list"))
|
self.window = MainWindow(config=ClanConfig(initial_view="list"))
|
||||||
self.win.set_application(self)
|
self.window.set_application(self)
|
||||||
icon_path = assets.loc / "clan_black.png"
|
self.tray_icon = TrayIcon(self)
|
||||||
self.win.set_default_icon_name(str(icon_path))
|
self.window.present()
|
||||||
self.win.present()
|
|
||||||
|
|
||||||
# TODO: For css styling
|
# TODO: For css styling
|
||||||
def init_style(self) -> None:
|
def init_style(self) -> None:
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import StrEnum
|
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
@ -9,8 +8,3 @@ gi.require_version("Gtk", "4.0")
|
|||||||
@dataclass
|
@dataclass
|
||||||
class ClanConfig:
|
class ClanConfig:
|
||||||
initial_view: str
|
initial_view: str
|
||||||
|
|
||||||
|
|
||||||
class VMStatus(StrEnum):
|
|
||||||
RUNNING = "Running"
|
|
||||||
STOPPED = "Stopped"
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
import weakref
|
import weakref
|
||||||
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import IO, Any, ClassVar
|
from typing import IO, Any, ClassVar
|
||||||
|
|
||||||
@ -13,7 +14,6 @@ from clan_cli.history.list import list_history
|
|||||||
|
|
||||||
from clan_vm_manager import assets
|
from clan_vm_manager import assets
|
||||||
from clan_vm_manager.errors.show_error import show_error_dialog
|
from clan_vm_manager.errors.show_error import show_error_dialog
|
||||||
from clan_vm_manager.models.interfaces import VMStatus
|
|
||||||
|
|
||||||
from .executor import MPProcess, spawn
|
from .executor import MPProcess, spawn
|
||||||
|
|
||||||
@ -98,27 +98,26 @@ class VM(GObject.Object):
|
|||||||
# Define a custom signal with the name "vm_stopped" and a string argument for the message
|
# Define a custom signal with the name "vm_stopped" and a string argument for the message
|
||||||
__gsignals__: ClassVar = {
|
__gsignals__: ClassVar = {
|
||||||
"vm_status_changed": (GObject.SignalFlags.RUN_FIRST, None, [GObject.Object]),
|
"vm_status_changed": (GObject.SignalFlags.RUN_FIRST, None, [GObject.Object]),
|
||||||
|
"build_vm": (GObject.SignalFlags.RUN_FIRST, None, [GObject.Object, bool]),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
icon: Path,
|
icon: Path,
|
||||||
status: VMStatus,
|
|
||||||
data: HistoryEntry,
|
data: HistoryEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.data = data
|
self.data = data
|
||||||
self.process = MPProcess("dummy", mp.Process(), Path("./dummy"))
|
self.process = MPProcess("dummy", mp.Process(), Path("./dummy"))
|
||||||
self._watcher_id: int = 0
|
self._watcher_id: int = 0
|
||||||
|
self._stop_watcher_id: int = 0
|
||||||
|
self._stop_timer_init: datetime | None = None
|
||||||
self._logs_id: int = 0
|
self._logs_id: int = 0
|
||||||
self._log_file: IO[str] | None = None
|
self._log_file: IO[str] | None = None
|
||||||
self.status = status
|
|
||||||
self._last_liveness: bool = False
|
|
||||||
self.log_dir = tempfile.TemporaryDirectory(
|
self.log_dir = tempfile.TemporaryDirectory(
|
||||||
prefix="clan_vm-", suffix=f"-{self.data.flake.flake_attr}"
|
prefix="clan_vm-", suffix=f"-{self.data.flake.flake_attr}"
|
||||||
)
|
)
|
||||||
self._finalizer = weakref.finalize(self, self.stop)
|
self._finalizer = weakref.finalize(self, self.stop)
|
||||||
self.connect("vm_status_changed", self._start_logs_task)
|
|
||||||
|
|
||||||
uri = ClanURI.from_str(
|
uri = ClanURI.from_str(
|
||||||
url=self.data.flake.flake_url, flake_attr=self.data.flake.flake_attr
|
url=self.data.flake.flake_url, flake_attr=self.data.flake.flake_attr
|
||||||
@ -136,50 +135,47 @@ class VM(GObject.Object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __start(self) -> None:
|
def __start(self) -> None:
|
||||||
if self.is_running():
|
log.info(f"Starting VM {self.get_id()}")
|
||||||
log.warn("VM is already running")
|
|
||||||
return
|
|
||||||
vm = vms.run.inspect_vm(self.machine)
|
vm = vms.run.inspect_vm(self.machine)
|
||||||
|
|
||||||
|
GLib.idle_add(self.emit, "build_vm", self, True)
|
||||||
|
vms.run.build_vm(self.machine, vm, [])
|
||||||
|
GLib.idle_add(self.emit, "build_vm", self, False)
|
||||||
|
|
||||||
self.process = spawn(
|
self.process = spawn(
|
||||||
on_except=None,
|
on_except=None,
|
||||||
log_dir=Path(str(self.log_dir.name)),
|
log_dir=Path(str(self.log_dir.name)),
|
||||||
func=vms.run.run_vm,
|
func=vms.run.run_vm,
|
||||||
vm=vm,
|
vm=vm,
|
||||||
)
|
)
|
||||||
log.debug("Starting VM")
|
log.debug(f"Started VM {self.get_id()}")
|
||||||
|
GLib.idle_add(self.emit, "vm_status_changed", self)
|
||||||
|
log.debug(f"Starting logs watcher on file: {self.process.out_file}")
|
||||||
|
self._logs_id = GLib.timeout_add(50, self._get_logs_task)
|
||||||
|
if self._logs_id == 0:
|
||||||
|
raise ClanError("Failed to add logs watcher")
|
||||||
|
|
||||||
|
log.debug(f"Starting VM watcher for: {self.machine.name}")
|
||||||
|
self._watcher_id = GLib.timeout_add(50, self._vm_watcher_task)
|
||||||
|
if self._watcher_id == 0:
|
||||||
|
raise ClanError("Failed to add watcher")
|
||||||
|
|
||||||
self.machine.qmp_connect()
|
self.machine.qmp_connect()
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
if self.is_running():
|
if self.is_running():
|
||||||
log.warn("VM is already running")
|
log.warn("VM is already running")
|
||||||
return
|
return
|
||||||
|
|
||||||
threading.Thread(target=self.__start).start()
|
threading.Thread(target=self.__start).start()
|
||||||
|
|
||||||
# Every 50ms check if the VM is still running
|
|
||||||
self._watcher_id = GLib.timeout_add(50, self._vm_watcher_task)
|
|
||||||
if self._watcher_id == 0:
|
|
||||||
raise ClanError("Failed to add watcher")
|
|
||||||
|
|
||||||
def _vm_watcher_task(self) -> bool:
|
def _vm_watcher_task(self) -> bool:
|
||||||
if self.is_running() != self._last_liveness:
|
if not self.is_running():
|
||||||
self.emit("vm_status_changed", self)
|
self.emit("vm_status_changed", self)
|
||||||
prev_liveness = self._last_liveness
|
log.debug("Removing VM watcher")
|
||||||
self._last_liveness = self.is_running()
|
return GLib.SOURCE_REMOVE
|
||||||
|
|
||||||
# If the VM was running and now it is not, remove the watcher
|
|
||||||
if prev_liveness and not self.is_running():
|
|
||||||
log.debug("Removing VM watcher")
|
|
||||||
return GLib.SOURCE_REMOVE
|
|
||||||
return GLib.SOURCE_CONTINUE
|
return GLib.SOURCE_CONTINUE
|
||||||
|
|
||||||
def _start_logs_task(self, obj: Any, vm: Any) -> None:
|
|
||||||
if self.is_running():
|
|
||||||
log.debug(f"Starting logs watcher on file: {self.process.out_file}")
|
|
||||||
self._logs_id = GLib.timeout_add(50, self._get_logs_task)
|
|
||||||
else:
|
|
||||||
log.debug("Not starting logs watcher")
|
|
||||||
|
|
||||||
def _get_logs_task(self) -> bool:
|
def _get_logs_task(self) -> bool:
|
||||||
if not self.process.out_file.exists():
|
if not self.process.out_file.exists():
|
||||||
return GLib.SOURCE_CONTINUE
|
return GLib.SOURCE_CONTINUE
|
||||||
@ -192,15 +188,15 @@ class VM(GObject.Object):
|
|||||||
self._log_file = None
|
self._log_file = None
|
||||||
return GLib.SOURCE_REMOVE
|
return GLib.SOURCE_REMOVE
|
||||||
|
|
||||||
|
line = os.read(self._log_file.fileno(), 4096)
|
||||||
|
if len(line) != 0:
|
||||||
|
print(line.decode("utf-8"), end="", flush=True)
|
||||||
|
|
||||||
if not self.is_running():
|
if not self.is_running():
|
||||||
log.debug("Removing logs watcher")
|
log.debug("Removing logs watcher")
|
||||||
self._log_file = None
|
self._log_file = None
|
||||||
return GLib.SOURCE_REMOVE
|
return GLib.SOURCE_REMOVE
|
||||||
|
|
||||||
line = os.read(self._log_file.fileno(), 4096)
|
|
||||||
if len(line) != 0:
|
|
||||||
print(line.decode("utf-8"), end="", flush=True)
|
|
||||||
|
|
||||||
return GLib.SOURCE_CONTINUE
|
return GLib.SOURCE_CONTINUE
|
||||||
|
|
||||||
def is_running(self) -> bool:
|
def is_running(self) -> bool:
|
||||||
@ -209,12 +205,32 @@ class VM(GObject.Object):
|
|||||||
def get_id(self) -> str:
|
def get_id(self) -> str:
|
||||||
return f"{self.data.flake.flake_url}#{self.data.flake.flake_attr}"
|
return f"{self.data.flake.flake_url}#{self.data.flake.flake_attr}"
|
||||||
|
|
||||||
|
def __shutdown_watchdog(self) -> None:
|
||||||
|
if self.is_running():
|
||||||
|
assert self._stop_timer_init is not None
|
||||||
|
diff = datetime.now() - self._stop_timer_init
|
||||||
|
if diff.seconds > 10:
|
||||||
|
log.error(f"VM {self.get_id()} has not stopped. Killing it")
|
||||||
|
self.process.kill_group()
|
||||||
|
return GLib.SOURCE_CONTINUE
|
||||||
|
else:
|
||||||
|
log.info(f"VM {self.get_id()} has stopped")
|
||||||
|
return GLib.SOURCE_REMOVE
|
||||||
|
|
||||||
|
def __stop(self) -> None:
|
||||||
|
log.info(f"Stopping VM {self.get_id()}")
|
||||||
|
|
||||||
|
self.machine.qmp_command("system_powerdown")
|
||||||
|
self._stop_timer_init = datetime.now()
|
||||||
|
self._stop_watcher_id = GLib.timeout_add(100, self.__shutdown_watchdog)
|
||||||
|
if self._stop_watcher_id == 0:
|
||||||
|
raise ClanError("Failed to add stop watcher")
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
if not self.is_running():
|
if not self.is_running():
|
||||||
return
|
return
|
||||||
log.info(f"Stopping VM {self.get_id()}")
|
log.info(f"Stopping VM {self.get_id()}")
|
||||||
# TODO: add fallback to kill the process if the QMP command fails
|
threading.Thread(target=self.__stop).start()
|
||||||
self.machine.qmp_command("system_powerdown")
|
|
||||||
|
|
||||||
def read_whole_log(self) -> str:
|
def read_whole_log(self) -> str:
|
||||||
if not self.process.out_file.exists():
|
if not self.process.out_file.exists():
|
||||||
@ -296,7 +312,6 @@ def get_saved_vms() -> list[VM]:
|
|||||||
|
|
||||||
base = VM(
|
base = VM(
|
||||||
icon=Path(icon),
|
icon=Path(icon),
|
||||||
status=VMStatus.STOPPED,
|
|
||||||
data=entry,
|
data=entry,
|
||||||
)
|
)
|
||||||
vm_list.append(base)
|
vm_list.append(base)
|
||||||
|
1218
pkgs/clan-vm-manager/clan_vm_manager/trayicon.py
Normal file
1218
pkgs/clan-vm-manager/clan_vm_manager/trayicon.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -100,12 +100,10 @@ class ClanList(Gtk.Box):
|
|||||||
box.set_valign(Gtk.Align.CENTER)
|
box.set_valign(Gtk.Align.CENTER)
|
||||||
|
|
||||||
add_button = Gtk.MenuButton()
|
add_button = Gtk.MenuButton()
|
||||||
add_button.set_icon_name("list-add")
|
|
||||||
add_button.set_has_frame(False)
|
add_button.set_has_frame(False)
|
||||||
add_button.set_menu_model(menu_model)
|
add_button.set_menu_model(menu_model)
|
||||||
|
add_button.set_label("Add machine")
|
||||||
box.append(add_button)
|
box.append(add_button)
|
||||||
box.append(Gtk.Label.new("Add machine"))
|
|
||||||
|
|
||||||
grp.set_header_suffix(box)
|
grp.set_header_suffix(box)
|
||||||
|
|
||||||
@ -192,6 +190,7 @@ class ClanList(Gtk.Box):
|
|||||||
|
|
||||||
switch.connect("notify::active", partial(self.on_row_toggle, vm))
|
switch.connect("notify::active", partial(self.on_row_toggle, vm))
|
||||||
vm.connect("vm_status_changed", partial(self.vm_status_changed, switch))
|
vm.connect("vm_status_changed", partial(self.vm_status_changed, switch))
|
||||||
|
vm.connect("build_vm", self.build_vm)
|
||||||
|
|
||||||
# suffix.append(box)
|
# suffix.append(box)
|
||||||
row.add_suffix(box)
|
row.add_suffix(box)
|
||||||
@ -295,6 +294,12 @@ class ClanList(Gtk.Box):
|
|||||||
row.set_state(True)
|
row.set_state(True)
|
||||||
vm.stop()
|
vm.stop()
|
||||||
|
|
||||||
|
def build_vm(self, vm: VM, _vm: VM, building: bool) -> None:
|
||||||
|
if building:
|
||||||
|
log.info("Building VM")
|
||||||
|
else:
|
||||||
|
log.info("VM built")
|
||||||
|
|
||||||
def vm_status_changed(self, switch: Gtk.Switch, vm: VM, _vm: VM) -> None:
|
def vm_status_changed(self, switch: Gtk.Switch, vm: VM, _vm: VM) -> None:
|
||||||
switch.set_active(vm.is_running())
|
switch.set_active(vm.is_running())
|
||||||
switch.set_state(vm.is_running())
|
switch.set_state(vm.is_running())
|
||||||
|
@ -57,7 +57,7 @@ python3.pkgs.buildPythonApplication {
|
|||||||
'';
|
'';
|
||||||
desktopItems = [
|
desktopItems = [
|
||||||
(makeDesktopItem {
|
(makeDesktopItem {
|
||||||
name = "clan-vm-manager";
|
name = "lol.clan.vm.manager";
|
||||||
exec = "clan-vm-manager %u";
|
exec = "clan-vm-manager %u";
|
||||||
icon = ./clan_vm_manager/assets/clan_white.png;
|
icon = ./clan_vm_manager/assets/clan_white.png;
|
||||||
desktopName = "cLAN Manager";
|
desktopName = "cLAN Manager";
|
||||||
|
@ -20,18 +20,18 @@ mkShell {
|
|||||||
|
|
||||||
ln -snf ${clan-vm-manager} result
|
ln -snf ${clan-vm-manager} result
|
||||||
|
|
||||||
|
|
||||||
# install desktop file
|
# install desktop file
|
||||||
set -eou pipefail
|
set -eou pipefail
|
||||||
DESKTOP_DST=~/.local/share/applications/clan-vm-manager.desktop
|
DESKTOP_FILE_NAME=lol.clan.vm.manager.desktop
|
||||||
DESKTOP_SRC=${clan-vm-manager}/share/applications/clan-vm-manager.desktop
|
DESKTOP_DST=~/.local/share/applications/$DESKTOP_FILE_NAME
|
||||||
|
DESKTOP_SRC=${clan-vm-manager}/share/applications/$DESKTOP_FILE_NAME
|
||||||
# UI_BIN="env GTK_DEBUG=interactive ${clan-vm-manager}/bin/clan-vm-manager"
|
# UI_BIN="env GTK_DEBUG=interactive ${clan-vm-manager}/bin/clan-vm-manager"
|
||||||
UI_BIN="${clan-vm-manager}/bin/clan-vm-manager"
|
UI_BIN="${clan-vm-manager}/bin/clan-vm-manager"
|
||||||
|
|
||||||
cp -f $DESKTOP_SRC $DESKTOP_DST
|
cp -f $DESKTOP_SRC $DESKTOP_DST
|
||||||
sleep 2
|
sleep 2
|
||||||
sed -i "s|Exec=.*clan-vm-manager|Exec=$UI_BIN|" $DESKTOP_DST
|
sed -i "s|Exec=.*clan-vm-manager|Exec=$UI_BIN|" $DESKTOP_DST
|
||||||
xdg-mime default clan-vm-manager.desktop x-scheme-handler/clan
|
xdg-mime default $DESKTOP_FILE_NAME x-scheme-handler/clan
|
||||||
echo "==== Validating desktop file installation ===="
|
echo "==== Validating desktop file installation ===="
|
||||||
set -x
|
set -x
|
||||||
desktop-file-validate $DESKTOP_DST
|
desktop-file-validate $DESKTOP_DST
|
||||||
|
Loading…
Reference in New Issue
Block a user