Signed-off-by: Ivailo Monev <xakepa10@laimg.moc>
This commit is contained in:
Ivailo Monev 2016-09-12 19:06:20 +00:00
parent 9150798aeb
commit 6c9ec21ad1
8 changed files with 675 additions and 0 deletions

4
kemu/.kateproject Normal file
View file

@ -0,0 +1,4 @@
{
"name": "kemu" ,
"files": [ { "git": 1 } ]
};

20
kemu/CMakeLists.txt Normal file
View file

@ -0,0 +1,20 @@
project(kemu)
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
find_package(KDE4)
include(KDE4Defaults)
include_directories(${KDE4_INCLUDES})
set(kemu_sources
main.cpp
kemumainwindow.cpp
)
add_executable(kemu ${kemu_sources})
target_link_libraries(kemu KDE4::kdeui KDE4::kfile)
install(TARGETS kemu DESTINATION ${BIN_INSTALL_DIR})
install(FILES kemuui.rc DESTINATION ${DATA_INSTALL_DIR}/kemu)
install(PROGRAMS kemu.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})

9
kemu/kemu.desktop Normal file
View file

@ -0,0 +1,9 @@
[Desktop Entry]
Icon=applications-engineering
Name=KEmu
GenericName=KEmu
Comment=Simple QEMU frontend for KDE
Exec=kemu --icon '%i' --caption '%c' %U
Terminal=false
Type=Application
Categories=Qt;KDE;

250
kemu/kemu.ui Normal file
View file

@ -0,0 +1,250 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KEmuWindow</class>
<widget class="QMainWindow" name="KEmuWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>KEmu</string>
</property>
<property name="windowIcon">
<iconset theme="applications-engineering">
<normaloff/>
</iconset>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="startStopButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Start</string>
</property>
<property name="icon">
<iconset theme="system-run">
<normaloff/>
</iconset>
</property>
</widget>
</item>
<item row="2" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" rowspan="2" colspan="3">
<widget class="KEditListWidget" name="machinesList">
<property name="maximumSize">
<size>
<width>300</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="0" column="3" rowspan="3">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Hard Drive image</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KUrlRequester" name="CDROMInput"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>CD-ROM image</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="systemComboBox"/>
</item>
<item row="1" column="1">
<widget class="KUrlRequester" name="HardDriveInput"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>System type</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Video output</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="videoComboBox">
<item>
<property name="text">
<string>cirrus</string>
</property>
</item>
<item>
<property name="text">
<string>vmware</string>
</property>
</item>
<item>
<property name="text">
<string>qxl</string>
</property>
</item>
<item>
<property name="text">
<string>xenfb</string>
</property>
</item>
<item>
<property name="text">
<string>tcx</string>
</property>
</item>
<item>
<property name="text">
<string>cg3</string>
</property>
</item>
<item>
<property name="text">
<string>virtio</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Random Access Memory</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="KIntNumInput" name="RAMInput">
<property name="minimum">
<number>32</number>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="KVMCheckBox">
<property name="text">
<string>Kernel-based Virtual Machine </string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>471</width>
<height>373</height>
</size>
</property>
</spacer>
</item>
<item row="7" column="1">
<widget class="KLineEdit" name="argumentsLineEdit"/>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Extra arguments</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>20</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<customwidgets>
<customwidget>
<class>KEditListWidget</class>
<extends>QWidget</extends>
<header>keditlistwidget.h</header>
</customwidget>
<customwidget>
<class>KLineEdit</class>
<extends>QLineEdit</extends>
<header>klineedit.h</header>
</customwidget>
<customwidget>
<class>KUrlRequester</class>
<extends>QFrame</extends>
<header>kurlrequester.h</header>
</customwidget>
<customwidget>
<class>KIntNumInput</class>
<extends>QWidget</extends>
<header>knuminput.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

280
kemu/kemumainwindow.cpp Normal file
View file

@ -0,0 +1,280 @@
/* This file is part of the KDE libraries
Copyright (C) 2016 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 <KAction>
#include <KActionCollection>
#include <KLocale>
#include <KIcon>
#include <KInputDialog>
#include <KStandardDirs>
#include <KDebug>
#include <QApplication>
#include <QMessageBox>
#include "kemumainwindow.h"
#include "ui_kemu.h"
KEmuMainWindow::KEmuMainWindow(QWidget *parent, Qt::WindowFlags flags)
: KXmlGuiWindow(parent, flags), m_kemuui(new Ui_KEmuWindow)
{
m_kemuui->setupUi(this);
m_kemuui->startStopButton->setText(i18n("Start"));
m_kemuui->startStopButton->setIcon(KIcon("system-run"));
KAction *a = actionCollection()->addAction("harddisk_create", this, SLOT(createHardDrive()));
a->setText(i18n("Create Hard Disk image"));
a->setIcon(KIcon("hard-drive"));
a->setShortcut(KStandardShortcut::openNew());
a->setWhatsThis(i18n("Create a new Hard Disk image for later use."));
KAction *b = actionCollection()->addAction("file_quit", this, SLOT(quit()));
b->setText(i18n("Quit"));
b->setIcon(KIcon("application-exit"));
b->setShortcut(KStandardShortcut::quit());
b->setWhatsThis(i18n("Close the application."));
setupGUI();
setAutoSaveSettings();
setWindowIcon(KIcon("applications-engineering"));
m_kemuui->groupBox->setEnabled(false);
connect(m_kemuui->machinesList->listView()->selectionModel(),
SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(machineChanged(QItemSelection,QItemSelection)));
connect(m_kemuui->machinesList, SIGNAL(added(QString)), this, SLOT(addMachine(QString)));
connect(m_kemuui->startStopButton, SIGNAL(clicked()), this, SLOT(startStopMachine()));
connect(m_kemuui->machinesList, SIGNAL(removed(QString)), this, SLOT(removeMachine(QString)));
// TODO: more machine-type entries
static const QStringList qemuBins = QStringList() << "qemu-system-i386";
foreach (const QString bin, qemuBins) {
if(!KStandardDirs::findExe(bin).isEmpty()) {
m_kemuui->systemComboBox->addItem(bin);
}
}
m_settings = new QSettings("KEmu", "kemu");
foreach(const QString machine, m_settings->childGroups()) {
if (m_settings->value(machine + "/enable") == true) {
m_kemuui->machinesList->insertItem(machine);
machineLoad(machine);
} else {
kDebug() << "garbage machine" << machine;
}
}
QFile kvmdev("/dev/kvm");
if (!kvmdev.exists()) {
const QString modprobeBin = KStandardDirs::findExe("modprobe");
if (!modprobeBin.isEmpty()) {
QProcess modprobe(this);
modprobe.start(modprobeBin, QStringList() << "-b" << "kvm");
modprobe.waitForFinished();
if (!kvmdev.exists()) {
QMessageBox::warning(this, i18n("KVM not available"), i18n("KVM not available"));
}
} else {
kDebug() << "modprobe not found";
}
}
connect(m_kemuui->CDROMInput, SIGNAL(textChanged(QString)), this, SLOT(machineSave(QString)));
connect(m_kemuui->HardDriveInput, SIGNAL(textChanged(QString)), this, SLOT(machineSave(QString)));
connect(m_kemuui->systemComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(machineSave(QString)));
connect(m_kemuui->videoComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(machineSave(QString)));
connect(m_kemuui->RAMInput, SIGNAL(valueChanged(int)), this, SLOT(machineSave(int)));
connect(m_kemuui->KVMCheckBox, SIGNAL(stateChanged(int)), this, SLOT(machineSave(int)));
connect(m_kemuui->argumentsLineEdit, SIGNAL(textChanged(QString)), this, SLOT(machineSave(QString)));
}
KEmuMainWindow::~KEmuMainWindow()
{
saveAutoSaveSettings();
m_settings->sync();
delete m_settings;
foreach(QProcess* machineProcess, m_machines) {
const QString machine = m_machines.key(machineProcess);
if (machineProcess->state() == QProcess::Running) {
kDebug() << "stopping machine" << machine;
machineProcess->terminate();
}
machineProcess->deleteLater();
m_machines.remove(machine);
}
}
void KEmuMainWindow::createHardDrive()
{
// TODO: implement
QMessageBox::warning(this, "NOT IMPLEMENTED", "NOT IMPLEMENTED");
}
void KEmuMainWindow::quit()
{
qApp->quit();
}
void KEmuMainWindow::machineSave(const QString ignored)
{
QString machine = m_kemuui->machinesList->currentText();
kDebug() << "saving machine" << machine;
m_settings->setValue(machine + "/cdrom", m_kemuui->CDROMInput->url().prettyUrl());
m_settings->setValue(machine + "/harddrive", m_kemuui->HardDriveInput->url().prettyUrl());
m_settings->setValue(machine + "/system", m_kemuui->systemComboBox->currentText());
m_settings->setValue(machine + "/video", m_kemuui->videoComboBox->currentText());
m_settings->setValue(machine + "/ram", m_kemuui->RAMInput->value());
m_settings->setValue(machine + "/kvm", m_kemuui->KVMCheckBox->isChecked());
m_settings->setValue(machine + "/args", m_kemuui->argumentsLineEdit->text());
m_settings->sync();
}
void KEmuMainWindow::machineSave(int ignored)
{
machineSave(QString());
}
void KEmuMainWindow::machineLoad(const QString machine)
{
kDebug() << "loading machine" << machine;
m_kemuui->CDROMInput->setUrl(m_settings->value(machine + "/cdrom").toUrl());
m_kemuui->HardDriveInput->setUrl(m_settings->value(machine + "/harddrive").toUrl());
const QString system = m_settings->value(machine + "/system").toString();
const int systemIndex = m_kemuui->systemComboBox->findText(system);
m_kemuui->systemComboBox->setCurrentIndex(systemIndex);
const QString video = m_settings->value(machine + "/video", "virtio").toString();
const int videoIndex = m_kemuui->videoComboBox->findText(video);
m_kemuui->videoComboBox->setCurrentIndex(videoIndex);
m_kemuui->RAMInput->setValue(m_settings->value(machine + "/ram", 32).toInt());
m_kemuui->KVMCheckBox->setChecked(m_settings->value(machine + "/kvm", false).toBool());
m_kemuui->argumentsLineEdit->setText(m_settings->value(machine + "/args").toString());
}
void KEmuMainWindow::machineChanged(QItemSelection ignored, QItemSelection ignored2)
{
QString machine = m_kemuui->machinesList->currentText();
if (!machine.isEmpty()) {
QFile kvmdev("/dev/kvm");
m_kemuui->KVMCheckBox->setEnabled(kvmdev.exists());
m_kemuui->startStopButton->setEnabled(true);
m_kemuui->groupBox->setEnabled(true);
if (m_machines.contains(machine)) {
kDebug() << "machine is running" << machine;
QProcess* machineProcess = m_machines.value(machine);
if (machineProcess->state() == QProcess::Running) {
m_kemuui->startStopButton->setText(i18n("Stop"));
m_kemuui->startStopButton->setIcon(KIcon("system-shutdown"));
} else {
m_kemuui->startStopButton->setText(i18n("Start"));
m_kemuui->startStopButton->setIcon(KIcon("system-run"));
}
}
machineLoad(machine);
} else {
m_kemuui->startStopButton->setEnabled(false);
m_kemuui->groupBox->setEnabled(false);
}
}
void KEmuMainWindow::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)));
if (exitCode != 0) {
QMessageBox::warning(this, i18n("QEMU error"),
i18n("An error occured:\n%1", QString(machineProcess->readAll())));
}
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();
}
void KEmuMainWindow::addMachine(const QString machine)
{
m_settings->setValue(machine + "/enable", true);
// TODO: set defaults?
m_settings->sync();
}
void KEmuMainWindow::startStopMachine()
{
const QString machine = m_kemuui->machinesList->currentText();
if (!machine.isEmpty()) {
if (m_machines.contains(machine)) {
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"));
} else {
kDebug() << "starting machine" << machine;
QStringList machineArgs;
const QString CDRom = m_kemuui->CDROMInput->url().prettyUrl();
if (!CDRom.isEmpty()) {
machineArgs << "-cdrom" << CDRom;
}
const QString HDrive = m_kemuui->HardDriveInput->url().prettyUrl();
if (!HDrive.isEmpty()) {
machineArgs << "-hda" << HDrive;
}
if (CDRom.isEmpty() && HDrive.isEmpty()) {
QMessageBox::warning(this, i18n("Requirements not met"),
i18n("Either CD-ROM or Hard-Drive image must be set"));
return;
}
machineArgs << "-vga" << m_kemuui->videoComboBox->currentText();
machineArgs << "-m" << QByteArray::number(m_kemuui->RAMInput->value());
if (m_kemuui->KVMCheckBox->isChecked()) {
machineArgs << "-enable-kvm";
}
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);
machineProcess->start(m_kemuui->systemComboBox->currentText(), machineArgs);
machineProcess->waitForStarted();
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)));
}
}
}
void KEmuMainWindow::removeMachine(const QString machine)
{
if (m_machines.contains(machine)) {
kDebug() << "stopping machine" << machine;
QProcess* machineProcess = m_machines.take(machine);
machineProcess->terminate();
machineProcess->deleteLater();
}
kDebug() << "removing machine" << machine;
m_settings->setValue(machine + "/enable", false);
m_settings->sync();
}

56
kemu/kemumainwindow.h Normal file
View file

@ -0,0 +1,56 @@
/* This file is part of the KDE libraries
Copyright (C) 2016 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 KEMUMAINWINDOW_H
#define KEMUMAINWINDOW_H
#include <KXmlGuiWindow>
#include <QListWidgetItem>
#include <QSettings>
#include <QProcess>
class Ui_KEmuWindow;
class KEmuMainWindow: public KXmlGuiWindow
{
Q_OBJECT
public:
KEmuMainWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0);
~KEmuMainWindow();
public slots:
void createHardDrive();
void quit();
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 addMachine(const QString machine);
void startStopMachine();
void removeMachine(const QString machine);
private:
Ui_KEmuWindow *m_kemuui;
QSettings *m_settings;
QHash<QString,QProcess*> m_machines;
};
#endif // KEMUMAINWINDOW_H

13
kemu/kemuui.rc Normal file
View file

@ -0,0 +1,13 @@
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<kpartgui name="kemu" version="1" >
<MenuBar>
<Menu name="file"><text>&amp;File</text>
<Action name="harddisk_create"/>
<Separator/>
<Action name="file_quit"/>
</Menu>
</MenuBar>
<ToolBar name="mainToolBar" disable="1"><text>Main Toolbar</text>
</ToolBar>
</kpartgui>

43
kemu/main.cpp Normal file
View file

@ -0,0 +1,43 @@
/* This file is part of the KDE libraries
Copyright (C) 2016 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 <KAboutData>
#include <KCmdLineArgs>
#include <KApplication>
#include "kemumainwindow.h"
int main(int argc, char** argv)
{
KAboutData aboutData("kemu", 0, ki18n("KEmu"),
"1.0.0", ki18n("Simple QEMU frontend for KDE."),
KAboutData::License_GPL_V2,
ki18n("(c) 2016 Ivailo Monev"),
KLocalizedString(),
"http://github.com/fluxer/katana"
);
aboutData.addAuthor(ki18n("Ivailo Monev"),
ki18n("Maintainer"),
"xakepa10@gmail.com");
aboutData.setProgramIconName(QLatin1String("applications-engineering"));
KCmdLineArgs::init(argc, argv, &aboutData);
KApplication *kemuapp = new KApplication();
KEmuMainWindow *kemuwindow = new KEmuMainWindow();
kemuwindow->show();
return kemuapp->exec();
}