diff --git a/dlangide-monod-linux.dproj b/dlangide-monod-linux.dproj
index 72f829f..70f2bc5 100644
--- a/dlangide-monod-linux.dproj
+++ b/dlangide-monod-linux.dproj
@@ -218,6 +218,8 @@
     <Compile Include="src\dlangide\workspace\workspace.d" />
     <Compile Include="src\ddebug\common\debugger.d" />
     <Compile Include="src\ddebug\common\queue.d" />
+    <Compile Include="src\ddebug\common\execution.d" />
+    <Compile Include="src\ddebug\common\nodebug.d" />
     <Compile Include="src\ddebug\gdb\gdbinterface.d" />
   </ItemGroup>
   <ItemGroup>
diff --git a/dlangide-monod-osx.dproj b/dlangide-monod-osx.dproj
index b5b35e0..9ea4c6d 100644
--- a/dlangide-monod-osx.dproj
+++ b/dlangide-monod-osx.dproj
@@ -128,6 +128,8 @@
     <Compile Include="src\dlangide\builders\extprocess.d" />
     <Compile Include="src\ddebug\common\debugger.d" />
     <Compile Include="src\ddebug\common\queue.d" />
+    <Compile Include="src\ddebug\common\execution.d" />
+    <Compile Include="src\ddebug\common\nodebug.d" />
     <Compile Include="src\ddebug\gdb\gdbinterface.d" />
     <Compile Include="src\ddebug\windows\debuginfo.d" />
     <Compile Include="src\ddebug\windows\mago.d" />
diff --git a/dlangide_msvc.visualdproj b/dlangide_msvc.visualdproj
index aaa86a9..6dd4474 100644
--- a/dlangide_msvc.visualdproj
+++ b/dlangide_msvc.visualdproj
@@ -419,6 +419,15 @@
    </Folder>
   </Folder>
   <Folder name="ddebug">
+   <Folder name="common">
+    <File path="src\ddebug\common\debugger.d" />
+    <File path="src\ddebug\common\execution.d" />
+    <File path="src\ddebug\common\nodebug.d" />
+    <File path="src\ddebug\common\queue.d" />
+   </Folder>
+   <Folder name="gdbinterface">
+    <File path="src\ddebug\gdb\gdbinterface.d" />
+   </Folder>
    <Folder name="windows">
     <File path="src\ddebug\windows\debuginfo.d" />
     <File path="src\ddebug\windows\mago.d" />
diff --git a/src/ddebug/common/execution.d b/src/ddebug/common/execution.d
new file mode 100644
index 0000000..6dc35d5
--- /dev/null
+++ b/src/ddebug/common/execution.d
@@ -0,0 +1,31 @@
+/**
+    Support for running stopping of project executable.
+
+ */
+module ddebug.common.execution;
+
+enum ExecutionStatus {
+    NotStarted,
+    Running,
+    Finished, // finished normally
+    Killed,   // killed
+    Error     // error while trying to start program
+}
+
+interface ProgramExecutionStatusListener {
+    /// called when program execution is stopped
+    void onProgramExecutionStatus(ProgramExecution process, ExecutionStatus status, int exitCode);
+}
+
+interface ProgramExecution {
+    /// returns true if it's debugger
+    @property bool isDebugger();
+    /// executable file
+    @property string executableFile();
+    /// returns execution status
+    @property ExecutionStatus status();
+    /// start execution
+    bool run();
+    /// stop execution
+    bool stop();
+}
diff --git a/src/ddebug/common/nodebug.d b/src/ddebug/common/nodebug.d
new file mode 100644
index 0000000..5dd5dad
--- /dev/null
+++ b/src/ddebug/common/nodebug.d
@@ -0,0 +1,146 @@
+module ddebug.common.nodebug;
+
+import ddebug.common.execution;
+
+import core.thread;
+import std.process;
+import dlangui.core.logger;
+
+class ProgramExecutionNoDebug : Thread, ProgramExecution {
+
+    // parameters
+    protected string _executableFile;
+    protected string[] _args;
+    protected string _workDir;
+    protected string _externalConsole;
+    protected ProgramExecutionStatusListener _listener;
+
+
+    // status
+	protected Pid _pid;
+    protected ExecutionStatus _status = ExecutionStatus.NotStarted;
+    protected int _exitCode = 0;
+
+
+
+    /// initialize but do not run
+    this(string executable, string[] args, string workDir, string externalConsole, ProgramExecutionStatusListener listener) {
+        super(&threadFunc);
+        _executableFile = executable;
+        _args = args;
+        _workDir = workDir;
+        _externalConsole = externalConsole;
+        _listener = listener;
+        assert(_listener !is null);
+    }
+
+    ~this() {
+        stop();
+    }
+
+	private bool isProcessActive() {
+		if (_pid is null)
+			return false;
+		auto res = tryWait(_pid);
+		if (res.terminated) {
+			Log.d("Process ", _executableFile, " is stopped");
+			_exitCode = wait(_pid);
+			_pid = Pid.init;
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+	private void killProcess() {
+		if (_pid is null)
+			return;
+		try {
+			Log.d("Trying to kill process", _executableFile);
+			kill(_pid, 9);
+			Log.d("Waiting for process termination");
+			_exitCode = wait(_pid);
+			_pid = Pid.init;
+			Log.d("Killed");
+		} catch (Exception e) {
+			Log.d("Exception while killing process " ~ _executableFile, e);
+			_pid = Pid.init;
+		}
+	}
+
+    private void threadFunc() {
+        import std.stdio;
+        string[] params;
+        params ~= _executableFile;
+        params ~= _args;
+        File newstdin;
+        File newstdout;
+        File newstderr;
+        try {
+		    _pid = spawnProcess(params, newstdin, newstdout, newstderr, null, Config.none, _workDir);
+        } catch (Exception e) {
+            Log.e("ProgramExecutionNoDebug: Failed to spawn process: ", e);
+            killProcess();
+            _status = ExecutionStatus.Error;
+        }
+
+        if (_status != ExecutionStatus.Error) {
+            // thread loop: poll process status
+            while (!_stopRequested) {
+		        Thread.sleep(dur!"msecs"(50));
+                if (!isProcessActive()) {
+                    _status = ExecutionStatus.Finished;
+                    break;
+                }
+            }
+            if (_stopRequested) {
+                killProcess();
+                _status = ExecutionStatus.Killed;
+            }
+        }
+
+        // finished
+        _listener.onProgramExecutionStatus(this, _status, _exitCode);
+    }
+
+    // implement ProgramExecution interface
+
+    /// returns true if it's debugger
+    @property bool isDebugger() { return false; }
+
+    /// executable file
+    @property string executableFile() { return _executableFile; }
+
+    /// returns execution status
+    @property ExecutionStatus status() { return _status; }
+
+    /// start execution
+    bool run() {
+        if (_runRequested)
+            return false; // already running
+        _runRequested = true;
+        _threadStarted = true;
+        _status = ExecutionStatus.Running;
+        start();
+        return true;
+    }
+
+    /// stop execution (call from GUI thread)
+    bool stop() {
+        if (!_runRequested)
+            return false;
+        if (_stopRequested)
+            return true;
+        _stopRequested = true;
+        if (_threadStarted && !_threadJoined) {
+            _threadJoined = true;
+            join();
+        }
+        return true;
+    }
+
+    protected bool _threadStarted;
+    protected bool _threadJoined;
+    protected bool _stopRequested;
+    protected bool _runRequested;
+}
diff --git a/src/dlangide/ui/frame.d b/src/dlangide/ui/frame.d
index d1070f9..bf6005a 100644
--- a/src/dlangide/ui/frame.d
+++ b/src/dlangide/ui/frame.d
@@ -31,6 +31,9 @@ import dlangide.workspace.project;
 import dlangide.builders.builder;
 import dlangide.tools.editorTool;
 
+import ddebug.common.execution;
+import ddebug.common.nodebug;
+
 import std.conv;
 import std.utf;
 import std.algorithm;
@@ -61,7 +64,7 @@ class BackgroundOperationWatcherTest : BackgroundOperationWatcher {
 }
 
 /// DIDE app frame
-class IDEFrame : AppFrame {
+class IDEFrame : AppFrame, ProgramExecutionStatusListener {
 
 	private ToolBarComboBox projectConfigurationCombo;
 	
@@ -72,6 +75,7 @@ class IDEFrame : AppFrame {
     TabWidget _tabs;
     DCDServer _dcdServer;
     IDESettings _settings;
+    ProgramExecution _execution;
 
     dstring frameWindowCaptionSuffix = "DLangIDE"d;
 
@@ -84,6 +88,65 @@ class IDEFrame : AppFrame {
         applySettings(_settings);
     }
 
+    /// stop current program execution
+    void stopExecution() {
+        if (_execution) {
+            _execution.stop();
+            destroy(_execution);
+            _execution = null;
+        }
+    }
+
+    /// called when program execution is stopped
+    protected void onProgramExecutionStatus(ProgramExecution process, ExecutionStatus status, int exitCode) {
+        executeInUiThread(delegate() {
+            Log.d("onProgramExecutionStatus process: ", process.executableFile, " status: ", status, " exitCode: ", exitCode);
+            _execution = null;
+            // TODO: update state
+            switch(status) {
+                case ExecutionStatus.Error:
+                    _logPanel.logLine("Cannot run program " ~ process.executableFile);
+                    break;
+                case ExecutionStatus.Finished:
+                    _logPanel.logLine("Program " ~ process.executableFile ~ " finished with exit code " ~ to!string(exitCode));
+                    break;
+                case ExecutionStatus.Killed:
+                    _logPanel.logLine("Program " ~ process.executableFile ~ " is killed");
+                    break;
+                default:
+                    _logPanel.logLine("Program " ~ process.executableFile ~ " is finished");
+                    break;
+            }
+        });
+    }
+
+    protected void runProject() {
+        import std.file;
+        stopExecution();
+        if (!currentWorkspace)
+            return;
+        Project project = currentWorkspace.startupProject;
+        if (!project) {
+             window.showMessageBox(UIString("Cannot run project"d), UIString("Startup project is not specified"d));
+            return;
+        }
+        // build project
+        // TODO
+        string executableFileName = project.executableFileName;
+        if (!executableFileName || !exists(executableFileName) || !isFile(executableFileName)) {
+            window.showMessageBox(UIString("Cannot run project"d), UIString("Cannot find executable"d));
+            return;
+        }
+        string[] args;
+        string externalConsoleExecutable = null; // TODO
+        string workingDirectory = null; // TODO
+        // TODO: provide thread safe listener
+        _logPanel.logLine("Starting " ~ executableFileName);
+        _execution = new ProgramExecutionNoDebug(executableFileName, args, workingDirectory, externalConsoleExecutable, this);
+        _execution.run();
+        // TODO: update status
+    }
+
     override protected void init() {
         _appName = "dlangide";
         //_editorTool = new DEditorTool(this);
@@ -586,7 +649,8 @@ class IDEFrame : AppFrame {
                 case IDEActions.DebugStart:
                 case IDEActions.DebugStartNoDebug:
                 case IDEActions.DebugContinue:
-                    buildProject(BuildOperation.Run);
+                    runProject();
+                    //buildProject(BuildOperation.Run);
                     return true;
                 case IDEActions.UpdateProjectDependencies:
                     buildProject(BuildOperation.Upgrade);
@@ -980,6 +1044,7 @@ class IDEFrame : AppFrame {
     /// called when main window is closing
     void onWindowClose() {
         Log.i("onWindowClose()");
+        stopExecution();
         if (_dcdServer) {
             if (_dcdServer.isRunning)
                 _dcdServer.stop();
diff --git a/src/dlangide/workspace/project.d b/src/dlangide/workspace/project.d
index c507d06..b57b6c2 100644
--- a/src/dlangide/workspace/project.d
+++ b/src/dlangide/workspace/project.d
@@ -438,6 +438,25 @@ class Project : WorkspaceItem {
         return buildNormalizedPath(_filename.dirName, toUTF8(name) ~ WORKSPACE_EXTENSION);
     }
 
+    @property bool isExecutable() {
+        // TODO: use targetType
+        return true;
+    }
+
+    /// return executable file name, or null if it's library project or executable is not found
+    @property string executableFileName() {
+        if (!isExecutable)
+            return null;
+        string exename = toUTF8(name);
+        // TODO: use targetName
+        version (Windows) {
+            exename = exename ~ ".exe";
+        }
+        // TODO: use targetPath
+        string exePath = buildNormalizedPath(_filename.dirName, "bin", exename);
+        return exePath;
+    }
+
     ProjectFolder findItems(string[] srcPaths) {
         ProjectFolder folder = new ProjectFolder(_filename);
         folder.project = this;
diff --git a/workspaces/tetris/src/gui.d b/workspaces/tetris/src/gui.d
index 9a5ba39..8f11ae1 100644
--- a/workspaces/tetris/src/gui.d
+++ b/workspaces/tetris/src/gui.d
@@ -33,7 +33,7 @@ Widget createAboutWidget()
 	res.addChild(new TextWidget(null, "(C) Vadim Lopatin, 2014"d));
 	res.addChild(new TextWidget(null, "http://github.com/buggins/dlangui"d));
 	Button closeButton = new Button("close", "Close"d);
-	closeButton.onClickListener = delegate(Widget src) {
+	closeButton.click = delegate(Widget src) {
 		Log.i("Closing window");
 		res.window.close();
 		return true;
@@ -521,7 +521,7 @@ class StatusWidget : VerticalLayout {
 
         ImageWidget image = new ImageWidget(null, "tetris_logo_big");
         image.layoutWidth(FILL_PARENT).alignment(Align.Center).clickable(true);
-        image.onClickListener = delegate(Widget src) {
+        image.click = delegate(Widget src) {
             _cup.handleAction(ACTION_PAUSE);
             // about dialog when clicking on image
             Window wnd = Platform.instance.createWindow("About...", window, WindowFlag.Modal);