mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-23 10:22:52 +00:00
kemu: implement runner to control KEmu virtual machines
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
31ed908a24
commit
13de02bdd6
11 changed files with 519 additions and 112 deletions
|
@ -13,8 +13,15 @@ set(kemu_sources
|
|||
)
|
||||
|
||||
add_executable(kemu ${kemu_sources})
|
||||
target_link_libraries(kemu ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS})
|
||||
target_link_libraries(kemu
|
||||
${KDE4_KDEUI_LIBS}
|
||||
${KDE4_KFILE_LIBS}
|
||||
${QT_QTDBUS_LIBRARY}
|
||||
)
|
||||
|
||||
install(TARGETS kemu DESTINATION ${KDE4_BIN_INSTALL_DIR})
|
||||
install(FILES kemuui.rc DESTINATION ${KDE4_DATA_INSTALL_DIR}/kemu)
|
||||
install(PROGRAMS kemu.desktop DESTINATION ${KDE4_XDG_APPS_INSTALL_DIR})
|
||||
|
||||
add_subdirectory(kded)
|
||||
add_subdirectory(krunner)
|
||||
|
|
26
kemu/kded/CMakeLists.txt
Normal file
26
kemu/kded/CMakeLists.txt
Normal file
|
@ -0,0 +1,26 @@
|
|||
set(kded_kemu_SRCS
|
||||
kded_kemu.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/org.kde.kemu.xml
|
||||
)
|
||||
|
||||
qt4_generate_dbus_interface(kded_kemu.h org.kde.kemu.xml)
|
||||
|
||||
kde4_add_plugin(kded_kemu ${kded_kemu_SRCS})
|
||||
target_link_libraries(kded_kemu ${KDE4_KDECORE_LIBS})
|
||||
|
||||
install(
|
||||
TARGETS kded_kemu
|
||||
DESTINATION ${KDE4_PLUGIN_INSTALL_DIR}
|
||||
)
|
||||
|
||||
install(
|
||||
FILES kemu.desktop
|
||||
DESTINATION ${KDE4_SERVICES_INSTALL_DIR}/kded
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kemu.xml
|
||||
DESTINATION ${KDE4_DBUS_INTERFACES_INSTALL_DIR}
|
||||
)
|
||||
|
||||
|
171
kemu/kded/kded_kemu.cpp
Normal file
171
kemu/kded/kded_kemu.cpp
Normal file
|
@ -0,0 +1,171 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2021 Ivailo Monev <xakepa10@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2, as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <QUrl>
|
||||
#include <KDebug>
|
||||
#include <KLocale>
|
||||
#include <ksettings.h>
|
||||
|
||||
#include "kded_kemu.h"
|
||||
#include "kpluginfactory.h"
|
||||
|
||||
K_PLUGIN_FACTORY(KEmuModuleFactory, registerPlugin<KEmuModule>();)
|
||||
K_EXPORT_PLUGIN(KEmuModuleFactory("kemu"))
|
||||
|
||||
KEmuModule::KEmuModule(QObject *parent, const QList<QVariant>&)
|
||||
: KDEDModule(parent)
|
||||
{
|
||||
}
|
||||
|
||||
KEmuModule::~KEmuModule()
|
||||
{
|
||||
foreach(QProcess* machineProcess, m_machines) {
|
||||
const QString machine = m_machines.key(machineProcess);
|
||||
kDebug() << "stopping machine" << machine;
|
||||
machineProcess->terminate();
|
||||
machineProcess->deleteLater();
|
||||
m_machines.remove(machine);
|
||||
}
|
||||
}
|
||||
|
||||
bool KEmuModule::start(const QString &machine)
|
||||
{
|
||||
if (m_machines.contains(machine)) {
|
||||
emit error(i18n("Machine is already running: %1", machine));
|
||||
return false;
|
||||
}
|
||||
|
||||
KSettings settings("kemu", KSettings::SimpleConfig);
|
||||
const bool enable = settings.value(machine + "/enable").toBool();
|
||||
if (!enable) {
|
||||
emit error(i18n("Machine is not enabled: %1", machine));
|
||||
return false;
|
||||
}
|
||||
|
||||
kDebug() << "loading machine settings" << machine;
|
||||
const QUrl cdrom = settings.value(machine + "/cdrom").toUrl();
|
||||
const QUrl harddisk = settings.value(machine + "/harddisk").toUrl();
|
||||
const QString system = settings.value(machine + "/system").toString();
|
||||
const QString video = settings.value(machine + "/video", "virtio").toString();
|
||||
const QString audio = settings.value(machine + "/audio", "ac97").toString();
|
||||
const int ram = settings.value(machine + "/ram", 512).toInt();
|
||||
const int cpu = settings.value(machine + "/cpu", 1).toInt();
|
||||
const bool kvm = settings.value(machine + "/kvm", false).toBool();
|
||||
const bool acpi = settings.value(machine + "/acpi", false).toBool();
|
||||
const QString args = settings.value(machine + "/args").toString();
|
||||
|
||||
if (cdrom.isEmpty() && harddisk.isEmpty()) {
|
||||
emit error(i18n("Either CD-ROM or Hard Disk image must be set"));
|
||||
return false;
|
||||
}
|
||||
|
||||
kDebug() << "starting machine" << machine;
|
||||
QStringList machineArgs;
|
||||
machineArgs << "-name" << machine;
|
||||
if (!cdrom.isEmpty()) {
|
||||
machineArgs << "-cdrom" << cdrom.toString();
|
||||
}
|
||||
if (!harddisk.isEmpty()) {
|
||||
machineArgs << "-hda" << harddisk.toString();
|
||||
}
|
||||
machineArgs << "-vga" << video;
|
||||
machineArgs << "-soundhw" << audio;
|
||||
machineArgs << "-m" << QString::number(ram);
|
||||
machineArgs << "-smp" << QString::number(cpu);
|
||||
if (kvm) {
|
||||
machineArgs << "-enable-kvm";
|
||||
}
|
||||
if (!acpi) {
|
||||
machineArgs << "-no-acpi";
|
||||
}
|
||||
if (!args.isEmpty()) {
|
||||
foreach (const QString argument, args.split(" ")) {
|
||||
machineArgs << argument;
|
||||
}
|
||||
}
|
||||
|
||||
QProcess* machineProcess = new QProcess(this);
|
||||
machineProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_machines.insert(machine, machineProcess);
|
||||
connect(machineProcess, SIGNAL(finished(int,QProcess::ExitStatus)),
|
||||
this, SLOT(_machineFinished(int,QProcess::ExitStatus)));
|
||||
machineProcess->start(system, machineArgs);
|
||||
const bool success = machineProcess->waitForStarted();
|
||||
emit started(machine);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool KEmuModule::stop(const QString &machine)
|
||||
{
|
||||
if (!m_machines.contains(machine)) {
|
||||
emit error(i18n("Machine is not running: %1", machine));
|
||||
return false;
|
||||
}
|
||||
|
||||
kDebug() << "stopping machine" << machine;
|
||||
QProcess* machineProcess = m_machines.take(machine);
|
||||
machineProcess->terminate();
|
||||
machineProcess->deleteLater();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KEmuModule::isRunning(const QString &machine) const
|
||||
{
|
||||
return m_machines.contains(machine);
|
||||
}
|
||||
|
||||
QStringList KEmuModule::running() const
|
||||
{
|
||||
return m_machines.keys();
|
||||
}
|
||||
|
||||
QStringList KEmuModule::machines() const
|
||||
{
|
||||
QStringList addedMachines;
|
||||
KSettings settings("kemu", KSettings::SimpleConfig);
|
||||
foreach(const QString key, settings.keys()) {
|
||||
const int sepindex = key.indexOf("/");
|
||||
if (sepindex < 1) {
|
||||
continue;
|
||||
}
|
||||
QString machine = key.left(sepindex);
|
||||
if (addedMachines.contains(machine)) {
|
||||
continue;
|
||||
}
|
||||
if (settings.value(machine + "/enable").toBool() == true) {
|
||||
addedMachines.append(machine);
|
||||
} else {
|
||||
kDebug() << "garbage machine" << machine;
|
||||
}
|
||||
}
|
||||
return addedMachines;
|
||||
}
|
||||
|
||||
void KEmuModule::_machineFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
QProcess *machineProcess = qobject_cast<QProcess*>(sender());
|
||||
disconnect(machineProcess, SIGNAL(finished(int,QProcess::ExitStatus)),
|
||||
this, SLOT(_machineFinished(int,QProcess::ExitStatus)));
|
||||
emit stopped(exitCode, QString(machineProcess->readAll()));
|
||||
|
||||
const QString machine = m_machines.key(machineProcess);
|
||||
m_machines.remove(machine);
|
||||
machineProcess->deleteLater();
|
||||
}
|
||||
|
||||
#include "moc_kded_kemu.cpp"
|
55
kemu/kded/kded_kemu.h
Normal file
55
kemu/kded/kded_kemu.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2021 Ivailo Monev <xakepa10@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2, as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KDED_KEMU_H
|
||||
#define KDED_KEMU_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QProcess>
|
||||
|
||||
#include "kdedmodule.h"
|
||||
|
||||
class KEmuModule: public KDEDModule
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.kde.kemu")
|
||||
|
||||
public:
|
||||
KEmuModule(QObject *parent, const QList<QVariant>&);
|
||||
~KEmuModule();
|
||||
|
||||
public Q_SLOTS:
|
||||
Q_SCRIPTABLE bool start(const QString &machine);
|
||||
Q_SCRIPTABLE bool stop(const QString &machine);
|
||||
|
||||
Q_SCRIPTABLE bool isRunning(const QString &machine) const;
|
||||
Q_SCRIPTABLE QStringList running() const;
|
||||
Q_SCRIPTABLE QStringList machines() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
Q_SCRIPTABLE void started(const QString &machine) const;
|
||||
Q_SCRIPTABLE void stopped(int status, const QString &error) const;
|
||||
Q_SCRIPTABLE void error(const QString &error) const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void _machineFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
private:
|
||||
QMap<QString,QProcess*> m_machines;
|
||||
};
|
||||
#endif // KDED_KEMU_H
|
11
kemu/kded/kemu.desktop
Normal file
11
kemu/kded/kemu.desktop
Normal file
|
@ -0,0 +1,11 @@
|
|||
[Desktop Entry]
|
||||
Icon=applications-engineering
|
||||
Name=QEMU virtual machine manager
|
||||
Comment=QEMU virtual machine manager service
|
||||
Type=Service
|
||||
X-KDE-ServiceTypes=KDEDModule
|
||||
X-KDE-Library=kemu
|
||||
X-KDE-DBus-ModuleName=kemu
|
||||
X-KDE-Kded-autoload=false
|
||||
X-KDE-Kded-load-on-demand=true
|
||||
OnlyShowIn=KDE;
|
|
@ -25,7 +25,6 @@
|
|||
#include <KStandardDirs>
|
||||
#include <KStatusBar>
|
||||
#include <KDebug>
|
||||
#include <ksettings.h>
|
||||
#include <QApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QThread>
|
||||
|
@ -34,7 +33,8 @@
|
|||
#include "ui_kemu.h"
|
||||
|
||||
KEmuMainWindow::KEmuMainWindow(QWidget *parent, Qt::WindowFlags flags)
|
||||
: KXmlGuiWindow(parent, flags), m_loading(false), m_installed(false), m_kemuui(new Ui_KEmuWindow)
|
||||
: KXmlGuiWindow(parent, flags), m_loading(false), m_installed(false), m_kemuui(new Ui_KEmuWindow),
|
||||
m_interface(new QDBusInterface("org.kde.kded", "/modules/kemu", "org.kde.kemu"))
|
||||
{
|
||||
m_kemuui->setupUi(this);
|
||||
|
||||
|
@ -84,9 +84,6 @@ KEmuMainWindow::KEmuMainWindow(QWidget *parent, Qt::WindowFlags flags)
|
|||
}
|
||||
|
||||
m_settings = new KSettings("kemu", KSettings::SimpleConfig);
|
||||
#ifndef QT_KATIE
|
||||
foreach(const QString machine, m_settings->childGroups()) {
|
||||
#else
|
||||
QStringList addedMachines;
|
||||
foreach(const QString key, m_settings->keys()) {
|
||||
const int sepindex = key.indexOf("/");
|
||||
|
@ -98,7 +95,6 @@ KEmuMainWindow::KEmuMainWindow(QWidget *parent, Qt::WindowFlags flags)
|
|||
continue;
|
||||
}
|
||||
addedMachines.append(machine);
|
||||
#endif
|
||||
if (m_settings->value(machine + "/enable").toBool() == true) {
|
||||
m_kemuui->machinesList->insertItem(machine);
|
||||
machineLoad(machine);
|
||||
|
@ -142,6 +138,10 @@ KEmuMainWindow::KEmuMainWindow(QWidget *parent, Qt::WindowFlags flags)
|
|||
connect(m_kemuui->KVMCheckBox, SIGNAL(stateChanged(int)), this, SLOT(machineSave(int)));
|
||||
connect(m_kemuui->ACPICheckBox, SIGNAL(stateChanged(int)), this, SLOT(machineSave(int)));
|
||||
connect(m_kemuui->argumentsLineEdit, SIGNAL(textChanged(QString)), this, SLOT(machineSave(QString)));
|
||||
|
||||
connect(m_interface, SIGNAL(started(QString)), this, SLOT(machineStarted(QString)));
|
||||
connect(m_interface, SIGNAL(stopped(int,QString)), this, SLOT(machineStopped(int,QString)));
|
||||
connect(m_interface, SIGNAL(error(QString)), this, SLOT(machineError(QString)));
|
||||
}
|
||||
|
||||
KEmuMainWindow::~KEmuMainWindow()
|
||||
|
@ -150,16 +150,9 @@ KEmuMainWindow::~KEmuMainWindow()
|
|||
const QString lastSelected = m_kemuui->machinesList->currentText();
|
||||
if (!lastSelected.isEmpty()) {
|
||||
m_settings->setValue("lastselected", lastSelected);
|
||||
m_settings->sync();
|
||||
}
|
||||
m_settings->sync();
|
||||
m_settings->deleteLater();
|
||||
foreach(QProcess* machineProcess, m_machines) {
|
||||
const QString machine = m_machines.key(machineProcess);
|
||||
kDebug() << "stopping machine" << machine;
|
||||
machineProcess->terminate();
|
||||
machineProcess->deleteLater();
|
||||
m_machines.remove(machine);
|
||||
}
|
||||
delete m_kemuui;
|
||||
}
|
||||
|
||||
|
@ -198,30 +191,15 @@ void KEmuMainWindow::quit()
|
|||
qApp->quit();
|
||||
}
|
||||
|
||||
void KEmuMainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
const int running = m_machines.size();
|
||||
if (running != 0) {
|
||||
const QMessageBox::StandardButton answer = QMessageBox::question(this,
|
||||
i18n("Stop machines and quit?"),
|
||||
i18n("There are still %1 machines running, do you really want to quit?", running),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
if (answer != QMessageBox::Yes) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void KEmuMainWindow::updateStatus()
|
||||
{
|
||||
if (m_machines.size() == 0) {
|
||||
const QStringList running = m_interface->call("running").arguments().at(0).toStringList();
|
||||
if (running.size() == 0) {
|
||||
statusBar()->showMessage(i18n("No machines running"));
|
||||
} else {
|
||||
QString machineNames;
|
||||
bool firstMachine = true;
|
||||
foreach (const QString name, m_machines.keys()) {
|
||||
foreach (const QString name, running) {
|
||||
if (firstMachine) {
|
||||
machineNames += name;
|
||||
firstMachine = false;
|
||||
|
@ -229,9 +207,21 @@ void KEmuMainWindow::updateStatus()
|
|||
machineNames += ", " + name;
|
||||
}
|
||||
}
|
||||
const QString statusText = i18n("Machines running: %1 (%2)", machineNames, m_machines.size());
|
||||
const QString statusText = i18n("Machines running: %1 (%2)", machineNames, running.size());
|
||||
statusBar()->showMessage(statusText);
|
||||
}
|
||||
|
||||
const QString machine = m_kemuui->machinesList->currentText();
|
||||
const bool isrunning = m_interface->call("isRunning", machine).arguments().at(0).toBool();
|
||||
if (isrunning) {
|
||||
kDebug() << "machine is running" << machine;
|
||||
m_kemuui->startStopButton->setText(i18n("Stop"));
|
||||
m_kemuui->startStopButton->setIcon(KIcon("system-shutdown"));
|
||||
} else {
|
||||
kDebug() << "machine is stopped" << machine;
|
||||
m_kemuui->startStopButton->setText(i18n("Start"));
|
||||
m_kemuui->startStopButton->setIcon(KIcon("system-run"));
|
||||
}
|
||||
}
|
||||
|
||||
void KEmuMainWindow::machineSave(const QString ignored)
|
||||
|
@ -261,7 +251,6 @@ void KEmuMainWindow::machineSave(int ignored)
|
|||
machineSave(QString());
|
||||
}
|
||||
|
||||
|
||||
void KEmuMainWindow::machineLoad(const QString machine)
|
||||
{
|
||||
m_loading = true;
|
||||
|
@ -283,6 +272,8 @@ void KEmuMainWindow::machineLoad(const QString machine)
|
|||
m_kemuui->ACPICheckBox->setChecked(m_settings->value(machine + "/acpi", false).toBool());
|
||||
m_kemuui->argumentsLineEdit->setText(m_settings->value(machine + "/args").toString());
|
||||
m_loading = false;
|
||||
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
void KEmuMainWindow::machineChanged(QItemSelection ignored, QItemSelection ignored2)
|
||||
|
@ -296,15 +287,6 @@ void KEmuMainWindow::machineChanged(QItemSelection ignored, QItemSelection ignor
|
|||
|
||||
m_kemuui->startStopButton->setEnabled(m_installed);
|
||||
m_kemuui->groupBox->setEnabled(true);
|
||||
if (m_machines.contains(machine)) {
|
||||
kDebug() << "machine is running" << machine;
|
||||
m_kemuui->startStopButton->setText(i18n("Stop"));
|
||||
m_kemuui->startStopButton->setIcon(KIcon("system-shutdown"));
|
||||
} else {
|
||||
kDebug() << "machine is stopped" << machine;
|
||||
m_kemuui->startStopButton->setText(i18n("Start"));
|
||||
m_kemuui->startStopButton->setIcon(KIcon("system-run"));
|
||||
}
|
||||
machineLoad(machine);
|
||||
} else {
|
||||
m_kemuui->startStopButton->setEnabled(false);
|
||||
|
@ -312,24 +294,30 @@ void KEmuMainWindow::machineChanged(QItemSelection ignored, QItemSelection ignor
|
|||
}
|
||||
}
|
||||
|
||||
void KEmuMainWindow::machineFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
void KEmuMainWindow::machineStarted(const QString machine)
|
||||
{
|
||||
Q_UNUSED(machine);
|
||||
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
void KEmuMainWindow::machineStopped(int exitCode, const QString error)
|
||||
{
|
||||
QProcess *machineProcess = qobject_cast<QProcess*>(sender());
|
||||
disconnect(machineProcess, SIGNAL(finished(int,QProcess::ExitStatus)),
|
||||
this, SLOT(machineFinished(int,QProcess::ExitStatus)));
|
||||
if (exitCode != 0) {
|
||||
QMessageBox::warning(this, i18n("QEMU error"),
|
||||
i18n("An error occured:\n%1", QString(machineProcess->readAll())));
|
||||
i18n("An error occured:\n%1", error));
|
||||
}
|
||||
m_kemuui->startStopButton->setText(i18n("Start"));
|
||||
m_kemuui->startStopButton->setIcon(KIcon("system-run"));
|
||||
const QString machine = m_machines.key(machineProcess);
|
||||
m_machines.remove(machine);
|
||||
machineProcess->deleteLater();
|
||||
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
void KEmuMainWindow::machineError(const QString error)
|
||||
{
|
||||
QMessageBox::warning(this, i18n("KEmu error"),
|
||||
i18n("An error occured:\n%1", error));
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
void KEmuMainWindow::addMachine(const QString machine)
|
||||
{
|
||||
m_settings->setValue(machine + "/enable", true);
|
||||
|
@ -341,69 +329,27 @@ void KEmuMainWindow::startStopMachine()
|
|||
{
|
||||
const QString machine = m_kemuui->machinesList->currentText();
|
||||
if (!machine.isEmpty()) {
|
||||
if (m_machines.contains(machine)) {
|
||||
const bool isrunning = m_interface->call("isRunning", machine).arguments().at(0).toBool();
|
||||
if (isrunning) {
|
||||
kDebug() << "stopping machine" << machine;
|
||||
QProcess* machineProcess = m_machines.take(machine);
|
||||
machineProcess->terminate();
|
||||
machineProcess->deleteLater();
|
||||
m_kemuui->startStopButton->setText(i18n("Start"));
|
||||
m_kemuui->startStopButton->setIcon(KIcon("system-run"));
|
||||
m_interface->call("stop", machine);
|
||||
} else {
|
||||
kDebug() << "starting machine" << machine;
|
||||
QStringList machineArgs;
|
||||
machineArgs << "-name" << machine;
|
||||
const QString CDRom = m_kemuui->CDROMInput->url().prettyUrl();
|
||||
if (!CDRom.isEmpty()) {
|
||||
machineArgs << "-cdrom" << CDRom;
|
||||
}
|
||||
const QString HardDisk = m_kemuui->HardDiskInput->url().prettyUrl();
|
||||
if (!HardDisk.isEmpty()) {
|
||||
machineArgs << "-hda" << HardDisk;
|
||||
}
|
||||
if (CDRom.isEmpty() && HardDisk.isEmpty()) {
|
||||
QMessageBox::warning(this, i18n("Requirements not met"),
|
||||
i18n("Either CD-ROM or Hard Disk image must be set"));
|
||||
return;
|
||||
}
|
||||
machineArgs << "-vga" << m_kemuui->videoComboBox->currentText();
|
||||
machineArgs << "-soundhw" << m_kemuui->audioComboBox->currentText();
|
||||
machineArgs << "-m" << QString::number(m_kemuui->RAMInput->value());
|
||||
machineArgs << "-smp" << QString::number(m_kemuui->CPUInput->value());
|
||||
if (m_kemuui->KVMCheckBox->isEnabled() && m_kemuui->KVMCheckBox->isChecked()) {
|
||||
machineArgs << "-enable-kvm";
|
||||
}
|
||||
if (!m_kemuui->ACPICheckBox->isChecked()) {
|
||||
machineArgs << "-no-acpi";
|
||||
}
|
||||
const QString extraArgs = m_kemuui->argumentsLineEdit->text();
|
||||
if (!extraArgs.isEmpty()) {
|
||||
foreach (const QString argument, extraArgs.split(" ")) {
|
||||
machineArgs << argument;
|
||||
}
|
||||
}
|
||||
QProcess* machineProcess = new QProcess(this);
|
||||
machineProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
m_kemuui->startStopButton->setText(i18n("Stop"));
|
||||
m_kemuui->startStopButton->setIcon(KIcon("system-shutdown"));
|
||||
m_machines.insert(machine, machineProcess);
|
||||
connect(machineProcess, SIGNAL(finished(int,QProcess::ExitStatus)),
|
||||
this, SLOT(machineFinished(int,QProcess::ExitStatus)));
|
||||
machineProcess->start(m_kemuui->systemComboBox->currentText(), machineArgs);
|
||||
m_interface->call("start", machine);
|
||||
}
|
||||
|
||||
updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
void KEmuMainWindow::removeMachine(const QString machine)
|
||||
{
|
||||
if (m_machines.contains(machine)) {
|
||||
const bool isrunning = m_interface->call("isRunning", machine).arguments().at(0).toBool();
|
||||
if (isrunning) {
|
||||
kDebug() << "stopping machine" << machine;
|
||||
QProcess* machineProcess = m_machines.take(machine);
|
||||
machineProcess->terminate();
|
||||
machineProcess->deleteLater();
|
||||
m_interface->call("stop", machine);
|
||||
}
|
||||
kDebug() << "removing machine" << machine;
|
||||
m_settings->setValue(machine + "/enable", false);
|
||||
m_settings->sync();
|
||||
|
||||
updateStatus();
|
||||
}
|
||||
|
|
|
@ -21,9 +21,10 @@
|
|||
|
||||
#include <KXmlGuiWindow>
|
||||
#include <QListWidgetItem>
|
||||
#include <QSettings>
|
||||
#include <QProcess>
|
||||
#include <QCloseEvent>
|
||||
#include <QDBusInterface>
|
||||
#include <ksettings.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class Ui_KEmuWindow;
|
||||
|
@ -40,15 +41,14 @@ public slots:
|
|||
void createHardDisk();
|
||||
void quit();
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent *event);
|
||||
|
||||
private slots:
|
||||
void machineLoad(const QString machine);
|
||||
void machineSave(const QString ignored);
|
||||
void machineSave(int ignored);
|
||||
void machineChanged(QItemSelection ignored, QItemSelection ignored2);
|
||||
void machineFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void machineStarted(const QString machine);
|
||||
void machineStopped(int exitCode, const QString error);
|
||||
void machineError(const QString error);
|
||||
void addMachine(const QString machine);
|
||||
void startStopMachine();
|
||||
void removeMachine(const QString machine);
|
||||
|
@ -59,8 +59,8 @@ private:
|
|||
bool m_loading;
|
||||
bool m_installed;
|
||||
Ui_KEmuWindow *m_kemuui;
|
||||
QSettings *m_settings;
|
||||
QHash<QString,QProcess*> m_machines;
|
||||
KSettings *m_settings;
|
||||
QDBusInterface *m_interface;
|
||||
};
|
||||
|
||||
#endif // KEMUMAINWINDOW_H
|
19
kemu/krunner/CMakeLists.txt
Normal file
19
kemu/krunner/CMakeLists.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
set(krunner_kemu_SRCS
|
||||
krunner_kemu.cpp
|
||||
)
|
||||
|
||||
kde4_add_plugin(krunner_kemu ${krunner_kemu_SRCS})
|
||||
target_link_libraries(krunner_kemu
|
||||
${KDE4_KDEUI_LIBS}
|
||||
${KDE4_PLASMA_LIBS}
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS krunner_kemu
|
||||
DESTINATION ${KDE4_PLUGIN_INSTALL_DIR}
|
||||
)
|
||||
|
||||
install(
|
||||
FILES plasma-runner-kemu.desktop
|
||||
DESTINATION ${KDE4_SERVICES_INSTALL_DIR}
|
||||
)
|
115
kemu/krunner/krunner_kemu.cpp
Normal file
115
kemu/krunner/krunner_kemu.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2021 Ivailo Monev <xakepa10@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2, as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusReply>
|
||||
#include <KDebug>
|
||||
#include <KIcon>
|
||||
#include <KMessageBox>
|
||||
|
||||
#include "krunner_kemu.h"
|
||||
|
||||
KEmuControlRunner::KEmuControlRunner(QObject *parent, const QVariantList& args)
|
||||
: Plasma::AbstractRunner(parent, args)
|
||||
{
|
||||
Q_UNUSED(args);
|
||||
|
||||
setObjectName(QLatin1String("QEMU virtual machine manager runner"));
|
||||
setSpeed(AbstractRunner::SlowSpeed);
|
||||
|
||||
connect(this, SIGNAL(prepare()), this, SLOT(prep()));
|
||||
}
|
||||
|
||||
KEmuControlRunner::~KEmuControlRunner()
|
||||
{
|
||||
}
|
||||
|
||||
void KEmuControlRunner::prep()
|
||||
{
|
||||
QList<Plasma::RunnerSyntax> syntaxes;
|
||||
|
||||
syntaxes << Plasma::RunnerSyntax("vm start :q:", i18n("Starts virtual machine"));
|
||||
syntaxes << Plasma::RunnerSyntax("vm stop :q:", i18n("Stops virtual machine"));
|
||||
|
||||
setSyntaxes(syntaxes);
|
||||
}
|
||||
|
||||
void KEmuControlRunner::match(Plasma::RunnerContext &context)
|
||||
{
|
||||
const QString term = context.query();
|
||||
if (term.length() < 7) {
|
||||
return;
|
||||
}
|
||||
|
||||
QDBusInterface interface("org.kde.kded", "/modules/kemu", "org.kde.kemu");
|
||||
const QStringList machines = interface.call("machines").arguments().at(0).toStringList();
|
||||
if (term.startsWith("vm start")) {
|
||||
foreach (const QString &machine, machines) {
|
||||
const bool isrunning = interface.call("isRunning", machine).arguments().at(0).toBool();
|
||||
if (!isrunning) {
|
||||
Plasma::QueryMatch match(this);
|
||||
match.setType(Plasma::QueryMatch::PossibleMatch);
|
||||
match.setIcon(KIcon(QLatin1String("system-run")));
|
||||
match.setText(i18n("Start %1 virtual machine", machine));
|
||||
match.setData(QStringList() << "start" << machine);
|
||||
context.addMatch(term, match);
|
||||
}
|
||||
}
|
||||
} else if (term.startsWith("vm stop")) {
|
||||
foreach (const QString &machine, machines) {
|
||||
const bool isrunning = interface.call("isRunning", machine).arguments().at(0).toBool();
|
||||
if (isrunning) {
|
||||
Plasma::QueryMatch match(this);
|
||||
match.setType(Plasma::QueryMatch::PossibleMatch);
|
||||
match.setIcon(KIcon(QLatin1String("system-shutdown")));
|
||||
match.setText(i18n("Stop %1 virtual machine", machine));
|
||||
match.setData(QStringList() << "stop" << machine);
|
||||
context.addMatch(term, match);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KEmuControlRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match)
|
||||
{
|
||||
Q_UNUSED(context)
|
||||
|
||||
const QStringList data = match.data().toStringList();
|
||||
Q_ASSERT(data.size() == 2);
|
||||
const QString command = data.at(0);
|
||||
const QString machine = data.at(1);
|
||||
|
||||
QDBusInterface interface("org.kde.kded", "/modules/kemu", "org.kde.kemu");
|
||||
if (command == "start") {
|
||||
QDBusReply<bool> reply = interface.call("start", machine);
|
||||
if (!reply.value()) {
|
||||
KMessageBox::error(nullptr, i18n("Unable to start VM"),
|
||||
i18n("Unable to start %1 virtual machine.", machine));
|
||||
}
|
||||
} else if (command == "stop") {
|
||||
QDBusReply<bool> reply = interface.call("stop", machine);
|
||||
if (!reply.value()) {
|
||||
KMessageBox::error(nullptr, i18n("Unable to stop VM"),
|
||||
i18n("Unable to stop %1 virtual machine.", machine));
|
||||
}
|
||||
} else {
|
||||
kWarning() << "invalid command" << command;
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_krunner_kemu.cpp"
|
44
kemu/krunner/krunner_kemu.h
Normal file
44
kemu/krunner/krunner_kemu.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2021 Ivailo Monev <xakepa10@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2, as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KRUNNER_KEMU_H
|
||||
#define KRUNNER_KEMU_H
|
||||
|
||||
#include <plasma/abstractrunner.h>
|
||||
|
||||
#include <KIcon>
|
||||
#include <KUrl>
|
||||
|
||||
class KEmuControlRunner : public Plasma::AbstractRunner
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
KEmuControlRunner(QObject *parent, const QVariantList& args);
|
||||
~KEmuControlRunner();
|
||||
|
||||
void match(Plasma::RunnerContext &context);
|
||||
void run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match);
|
||||
|
||||
private slots:
|
||||
void prep();
|
||||
};
|
||||
|
||||
K_EXPORT_PLASMA_RUNNER(kemucontrol, KEmuControlRunner)
|
||||
|
||||
#endif
|
13
kemu/krunner/plasma-runner-kemu.desktop
Normal file
13
kemu/krunner/plasma-runner-kemu.desktop
Normal file
|
@ -0,0 +1,13 @@
|
|||
[Desktop Entry]
|
||||
Name=Control QEMU virtual machines
|
||||
Comment=Allows to control virtual machines
|
||||
X-KDE-ServiceTypes=Plasma/Runner
|
||||
Type=Service
|
||||
Icon=applications-engineering
|
||||
X-KDE-Library=krunner_kemu
|
||||
X-KDE-PluginInfo-Author=Ivailo Monev
|
||||
X-KDE-PluginInfo-Email=xakepa10@gmail.com
|
||||
X-KDE-PluginInfo-Name=QEMU virtual machine manager runner
|
||||
X-KDE-PluginInfo-Version=1.0
|
||||
X-KDE-PluginInfo-License=GPL
|
||||
X-KDE-PluginInfo-EnabledByDefault=true
|
Loading…
Add table
Reference in a new issue