diff --git a/desktop.jl b/desktop.jl
index 7f27065286014f1ffff51dd5e4a1f8b3bcb133df..fd96f76b97c043b6bad596932cc2c31370cebf8f 100755
--- a/desktop.jl
+++ b/desktop.jl
@@ -1,12 +1,10 @@
 #!/usr/bin/env julia
-# Start Persefone Desktop
+# A very thin wrapper to start Persefone Desktop.
+# (This is not included in PersefoneDesktop.jl in order to enable the latter to
+# be used as a library/package.) #XXX does this make sense?
 
-#using GLMakie
-#using Makie
-using QML
+using Pkg
+Pkg.activate(".")
+using PersefoneDesktop
 
-# MUST disable threading in Qt
-ENV["QSG_RENDER_LOOP"] = "basic"
-
-loadqml("src/main.qml")
-exec()
+launch()
diff --git a/run.jl b/run.jl
deleted file mode 100755
index fd96f76b97c043b6bad596932cc2c31370cebf8f..0000000000000000000000000000000000000000
--- a/run.jl
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env julia
-# A very thin wrapper to start Persefone Desktop.
-# (This is not included in PersefoneDesktop.jl in order to enable the latter to
-# be used as a library/package.) #XXX does this make sense?
-
-using Pkg
-Pkg.activate(".")
-using PersefoneDesktop
-
-launch()
diff --git a/src/config.jl b/src/config.jl
new file mode 100644
index 0000000000000000000000000000000000000000..f8d2c111ac25525f49b2ae1bf80c010016eec5f9
--- /dev/null
+++ b/src/config.jl
@@ -0,0 +1,60 @@
+### PersefoneDesktop - a GUI to the Persefone model of agriculture and ecosystems
+###
+### This file manages the backend of the setup window.
+###
+
+const userconfigfile = Observable("userconfig.toml")
+
+const outdir = Observable("persefone_output")
+const csvoutput = Observable(false)
+const loglevel = Observable("warn")
+const processors = Observable(1)
+const seed = Observable(2)
+const startdateday = Observable(1)
+const startdatemonth = Observable(1)
+const startdateyear = Observable(2022)
+const enddateday = Observable(31)
+const enddatemonth = Observable(12)
+const enddateyear = Observable(2022)
+const region = Observable("Jena")
+#const farmmodel = Observable("FieldManager") #XXX not yet implemented
+#const targetspecies = Observable()
+#const insectmodel = Observable() #TODO not sure how to configure a list?
+const cropmodel = Observable("ALMaSS") #XXX alternatives not yet implemented
+
+
+# TODO create config file with fixed options:
+# - overwrite = true (?)
+# - storedata = true
+# - indoutfreq = "daily"
+# - popoutfreq = "daily"
+
+
+function writeconfig()
+    println("Wrote config file.")
+    #TODO
+end
+
+function configwindow()
+    #TODO
+    @qmlfunction splashscreen
+    @qmlfunction writeconfig
+    @qmlfunction newsimulation
+    qmlfile = joinpath(dirname(@__FILE__), "config.qml")
+    loadqml(qmlfile,
+            conf = JuliaPropertyMap("userconfigfile" => userconfigfile,
+                                    "outdir" => outdir,
+                                    "csvoutput" => csvoutput,
+                                    "loglevel" => loglevel,
+                                    "processors" => processors,
+                                    "seed" => seed,
+                                    "startdateday" => startdateday,
+                                    "startdatemonth" => startdatemonth,
+                                    "startdateyear" => startdateyear,
+                                    "enddateday" => enddateday,
+                                    "enddatemonth" => enddatemonth,
+                                    "enddateyear" => enddateyear,
+                                    "region" => region,
+                                    "cropmodel" => cropmodel))
+    exec()
+end
diff --git a/src/config.qml b/src/config.qml
new file mode 100644
index 0000000000000000000000000000000000000000..3acab11631661dfb8eb247571c99dcca9627e749
--- /dev/null
+++ b/src/config.qml
@@ -0,0 +1,246 @@
+/// PersefoneDesktop - a GUI to the Persefone model of agriculture and ecosystems
+///
+/// This file defines the layout of the configuration window.
+///
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Dialogs
+import org.julialang
+
+ApplicationWindow {
+	id: configWindow
+	title: "Configure simulation"
+	width: 700
+	height: 350
+	visible: true
+
+	ColumnLayout {
+		id: generalSettings
+		anchors.top: parent.top
+		anchors.left: parent.left
+		anchors.leftMargin: 20
+		anchors.topMargin: 20
+		spacing: 8
+		
+		Text {
+			text: "General Settings:"
+			horizontalAlignment: Text.AlignCenter
+			font.bold: true
+		}
+
+		RowLayout {
+			Label { text: "Configuration file:" }
+			Label {
+				text: conf.userconfigfile
+				font.italic: true
+			}
+			Button {
+				text: "Select..."
+				onClicked: conffileChooser.open()
+			}
+			FileDialog {
+				id: conffileChooser
+				defaultSuffix: "toml"
+				fileMode: FileDialog.SaveFile
+				nameFilters: ["Configuration files (*.toml)"]
+				onAccepted: conf.userconfigfile = selectedFile
+			}
+		}
+
+		RowLayout {
+			Label { text: "Output folder:" }
+			Label {
+				text: conf.outdir
+				font.italic: true
+			}
+			Button {
+				text: "Select..."
+				onClicked: outdirChooser.open()
+			}
+			FolderDialog {
+				id: outdirChooser
+				selectedFolder: conf.outdir
+				onAccepted: conf.outdir = selectedFolder
+			}
+		}
+
+		//XXX Not all regions have data available!
+		// (currently, only Jena is fully configured)
+		RowLayout {
+			Label { text: "Region:" }
+			ComboBox {
+				currentIndex: 3
+				model: ["Bodensee", "Eichsfeld", "Hohenlohe", "Jena",
+						"Oberrhein", "Thüringer Becken"]
+				onCurrentIndexChanged: conf.region = currentText
+			}
+		}
+		
+		RowLayout {
+			Label { text: "Crop model:" }
+			ComboBox {
+				currentIndex: 0
+				model: ["ALMaSS", "AquaCrop"]
+				onCurrentIndexChanged: conf.cropmodel = currentText
+			}
+		}
+
+		//XXX For which years do I actually have data?
+		RowLayout {
+			Label { text: "Start date:" }
+			SpinBox {
+				Layout.maximumWidth: 100
+				from: 1
+				to: 31
+				value: conf.startdateday
+				editable: true
+				wrap: true
+				onValueModified: conf.startdateday = value
+			}
+			SpinBox {
+				Layout.maximumWidth: 100
+				from: 1
+				to: 12
+				value: conf.startdatemonth
+				editable: true
+				wrap: true
+				onValueModified: conf.startdatemonth = value
+			}
+			SpinBox {
+				Layout.maximumWidth: 100
+				from: 2000
+				to: 2050
+				value: conf.startdateyear
+				editable: true
+				wrap: true
+				textFromValue: function(value) { return Number(value).toString(); }
+				onValueModified: conf.startdateyear = value
+			}				
+		}
+
+		RowLayout {
+			Label { text: "End date:" }
+			SpinBox {
+				Layout.maximumWidth: 100
+				from: 1
+				to: 31
+				value: conf.enddateday
+				editable: true
+				wrap: true
+				onValueModified: conf.enddateday = value
+			}
+			SpinBox {
+				Layout.maximumWidth: 100
+				from: 1
+				to: 12
+				value: conf.enddatemonth
+				editable: true
+				wrap: true
+				onValueModified: conf.enddatemonth = value
+			}
+			SpinBox {
+				Layout.maximumWidth: 100
+				from: 2000
+				to: 2050
+				value: conf.enddateyear
+				editable: true
+				wrap: true
+				textFromValue: function(value) { return Number(value).toString(); }
+				onValueModified: conf.enddateyear = value
+			}				
+		}
+	}
+
+	ColumnLayout {
+		id: advancedSettings
+		anchors.top: parent.top
+		anchors.right: parent.right
+		anchors.left: generalSettings.right
+		anchors.rightMargin: 20
+		anchors.topMargin: 20
+		anchors.leftMargin: 20
+		spacing: 8
+		
+		Text {
+			text: "Advanced Settings:"
+			horizontalAlignment: Text.AlignCenter
+			font.bold: true
+		}
+
+		RowLayout {
+			Label { text: "Random seed:" }
+			SpinBox {
+				from: 0
+				to: 2147483647
+				value: 2
+				editable: true
+				onValueModified: conf.seed = value
+			}
+		}
+
+		RowLayout {
+			Label { text: "Log level:" }
+			ComboBox {
+				currentIndex: 2
+				model: ["debug", "info", "warn"]
+				onCurrentIndexChanged: conf.loglevel = currentText
+			}
+		}
+
+		RowLayout {
+			Label { text: "Processors:" }
+			SpinBox {
+				from: 1
+				to: 8
+				value: conf.processors
+				editable: true
+				onValueModified: conf.processors = value
+			}
+		}
+
+		CheckBox {
+			checked: conf.csvoutput
+			text: "Write CSV output"
+			onClicked: conf.csvoutput = checked
+		}
+
+		//TODO add config options for:
+		// farmmodel
+		// targetspecies
+		// insectmodel
+		
+	}
+
+	footer: ToolBar {
+		RowLayout {
+			anchors.right: parent.right
+			layoutDirection: Qt.RightToLeft
+			
+			Button {
+				text: "Start new simulation"
+				onClicked: {
+					//Julia.splashscreen() //FIXME crashes
+					Julia.writeconfig()
+					Julia.newsimulation()
+					configWindow.close()
+				}
+			}
+			
+			Button {
+				text: "Save configuration"
+				onClicked: {
+					Julia.writeconfig()
+					configWindow.close()
+				}
+			}
+
+			Button {
+				text: "Cancel"
+				onClicked: configWindow.close()
+			}
+		}
+	}
+}