Cleanup: use consistent names for projects and packages.
diff --git a/com.google.eclipse.elt.emulator/.classpath b/com.google.eclipse.elt.emulator/.classpath
new file mode 100644
index 0000000..deb6736
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/com.google.eclipse.elt.emulator/.gitignore b/com.google.eclipse.elt.emulator/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/com.google.eclipse.elt.emulator/.project b/com.google.eclipse.elt.emulator/.project
new file mode 100644
index 0000000..f17f0b6
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.google.eclipse.elt.emulator</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.emulator/.settings/org.eclipse.jdt.core.prefs b/com.google.eclipse.elt.emulator/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..468cbfa
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,14 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
diff --git a/com.google.eclipse.elt.emulator/META-INF/MANIFEST.MF b/com.google.eclipse.elt.emulator/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..b27807c
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.google.eclipse.elt.emulator;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-Activator: com.google.eclipse.elt.emulator.impl.TerminalPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.ui,
+ org.eclipse.jface.text
+Bundle-ActivationPolicy: lazy
+Eclipse-LazyStart: true
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ClassPath: .
+Export-Package: com.google.eclipse.elt.emulator.actions;x-friends:="com.google.eclipse.elt.view",
+ com.google.eclipse.elt.emulator.connector;x-friends:="com.google.eclipse.elt.view",
+ com.google.eclipse.elt.emulator.control;x-friends:="com.google.eclipse.elt.view",
+ com.google.eclipse.elt.emulator.core;x-friends:="com.google.eclipse.elt.view",
+ com.google.eclipse.elt.emulator.provisional.api;x-friends:="com.google.eclipse.elt.view",
+ com.google.eclipse.elt.emulator.provisional.api.provider;x-friends:="com.google.eclipse.elt.view"
diff --git a/com.google.eclipse.elt.emulator/build.properties b/com.google.eclipse.elt.emulator/build.properties
new file mode 100644
index 0000000..c46bda7
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/build.properties
@@ -0,0 +1,26 @@
+###############################################################################
+# Copyright (c) 2003, 2011 Wind River Systems, Inc. and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Initial Contributors:
+# The following Wind River employees contributed to the Terminal component
+# that contains this file: Chris Thew, Fran Litterio, Stephen Lamb,
+# Helmut Haigermoser and Ted Williams.
+#
+# Contributors:
+# Michael Scharf (Wind River) - split into core, view and connector plugins
+# Martin Oberhuber (Wind River) - fixed copyright headers and beautified
+# Anna Dushistova (MontaVista) - added icons
+###############################################################################
+bin.includes = .,\
+ META-INF/,\
+ plugin.xml,\
+ plugin.properties,\
+ icons/
+source.. = src/
+output.. = bin/
+javacSource=1.6
+javacTarget=1.6
diff --git a/com.google.eclipse.elt.emulator/icons/clcl16/clear_co.gif b/com.google.eclipse.elt.emulator/icons/clcl16/clear_co.gif
new file mode 100644
index 0000000..af30a42
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/icons/clcl16/clear_co.gif
Binary files differ
diff --git a/com.google.eclipse.elt.emulator/icons/dlcl16/clear_co.gif b/com.google.eclipse.elt.emulator/icons/dlcl16/clear_co.gif
new file mode 100644
index 0000000..6775edf
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/icons/dlcl16/clear_co.gif
Binary files differ
diff --git a/com.google.eclipse.elt.emulator/icons/elcl16/clear_co.gif b/com.google.eclipse.elt.emulator/icons/elcl16/clear_co.gif
new file mode 100644
index 0000000..af30a42
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/icons/elcl16/clear_co.gif
Binary files differ
diff --git a/com.google.eclipse.elt.emulator/plugin.properties b/com.google.eclipse.elt.emulator/plugin.properties
new file mode 100644
index 0000000..26ccbac
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/plugin.properties
@@ -0,0 +1,28 @@
+###############################################################################
+# Copyright (c) 2003, 2011 Wind River Systems, Inc. and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Initial Contributors:
+# The following Wind River employees contributed to the Terminal component
+# that contains this file: Chris Thew, Fran Litterio, Stephen Lamb,
+# Helmut Haigermoser and Ted Williams.
+#
+# Contributors:
+# Michael Scharf (Wind River) - split into core, view and connector plugins
+# Martin Oberhuber (Wind River) - fixed copyright headers and beautified
+###############################################################################
+
+# NLS_MESSAGEFORMAT_NONE
+
+pluginName = Target Management Terminal Widget
+providerName = Eclipse TM Project
+
+terminal.context.name=Terminal Widget
+terminal.context.description=Override ALT+x menu access keys
+
+terminal.insertion.description=Terminal view insertion
+terminal.insertion.name=Terminal view insert
+terminal.insertion.category.name=Terminal Widget
diff --git a/com.google.eclipse.elt.emulator/plugin.xml b/com.google.eclipse.elt.emulator/plugin.xml
new file mode 100644
index 0000000..c7125f8
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/plugin.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<!--
+# Copyright (c) 2006, 2008 Wind River Systems, Inc. and others.
+# All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+# Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<plugin>
+ <extension point="org.eclipse.ui.contexts">
+ <context
+ name="%terminal.context.name"
+ description="%terminal.context.description"
+ id="com.google.eclipse.elt.emulator.TerminalContext" />
+ </extension>
+ <extension point="org.eclipse.ui.commands">
+ <!-- Dummy commands for bindings, see below -->
+ <command
+ categoryId="com.google.eclipse.elt.emulator.category1"
+ id="com.google.eclipse.elt.emulator.command1"
+ name="%terminal.insertion.name" />
+ <category
+ description="%terminal.view.insertion.description"
+ id="com.google.eclipse.elt.emulator.category1"
+ name="%terminal.insertion.category.name" />
+ </extension>
+ <extension point="org.eclipse.ui.bindings">
+ <!--
+ These keybindings are needed to disable the menu-activation keys (e.g., Alt-F for the File menu, etc.). The code
+ in method TerminalControl.TerminalFocusListener.focusGained() disables the Eclipse key binding service, but it
+ doesn't disable the global menu-activation keys.
+ -->
+ <key
+ commandId="com.google.eclipse.elt.emulator.command1"
+ contextId="com.google.eclipse.elt.emulator.TerminalContext"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="Alt+A" /> <!-- Search -->
+ <key
+ commandId="com.google.eclipse.elt.emulator.command1"
+ contextId="com.google.eclipse.elt.emulator.TerminalContext"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="Alt+E" /> <!-- Edit -->
+ <key
+ commandId="com.google.eclipse.elt.emulator.command1"
+ contextId="com.google.eclipse.elt.emulator.TerminalContext"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="Alt+F" /> <!-- File -->
+ <key
+ commandId="com.google.eclipse.elt.emulator.command1"
+ contextId="com.google.eclipse.elt.emulator.TerminalContext"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="Alt+G" />
+ <key
+ commandId="com.google.eclipse.elt.emulator.command1"
+ contextId="com.google.eclipse.elt.emulator.TerminalContext"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="Alt+H" /> <!-- Help -->
+ <key
+ commandId="com.google.eclipse.elt.emulator.command1"
+ contextId="com.google.eclipse.elt.emulator.TerminalContext"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="Alt+N" /> <!-- Navigate -->
+ <key
+ commandId="com.google.eclipse.elt.emulator.command1"
+ contextId="com.google.eclipse.elt.emulator.TerminalContext"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="Alt+P" /> <!-- Project -->
+ <key
+ commandId="com.google.eclipse.elt.emulator.command1"
+ contextId="com.google.eclipse.elt.emulator.TerminalContext"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="Alt+R" /> <!-- Run -->
+ <key
+ commandId="com.google.eclipse.elt.emulator.command1"
+ contextId="com.google.eclipse.elt.emulator.TerminalContext"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="Alt+S" /> <!-- Source -->
+ <key
+ commandId="com.google.eclipse.elt.emulator.command1"
+ contextId="com.google.eclipse.elt.emulator.TerminalContext"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="Alt+T" /> <!-- Refactor -->
+ <key
+ commandId="com.google.eclipse.elt.emulator.command1"
+ contextId="com.google.eclipse.elt.emulator.TerminalContext"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="Alt+V" /> <!-- CVS -->
+ <key
+ commandId="com.google.eclipse.elt.emulator.command1"
+ contextId="com.google.eclipse.elt.emulator.TerminalContext"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="Alt+W" /> <!-- Window -->
+ </extension>
+</plugin>
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/AbstractTerminalAction.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/AbstractTerminalAction.java
new file mode 100644
index 0000000..65fb0be
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/AbstractTerminalAction.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.resource.*;
+
+import com.google.eclipse.elt.emulator.control.ITerminalViewControl;
+import com.google.eclipse.elt.emulator.impl.TerminalPlugin;
+
+public abstract class AbstractTerminalAction extends Action {
+ private final ITerminalViewControl target;
+
+ public AbstractTerminalAction(String id) {
+ this(null, id, 0);
+ }
+
+ public AbstractTerminalAction(ITerminalViewControl target, String id) {
+ this(target, id, 0);
+ }
+
+ public AbstractTerminalAction(ITerminalViewControl target, String id, int style) {
+ super("", style);
+ this.target = target;
+ setId(id);
+ }
+
+ @Override public abstract void run();
+
+ protected void setUpAction(
+ String text, String toolTip, String image, String enabledImage, String disabledImage, boolean enabled) {
+ setUpAction(
+ text, toolTip, image, enabledImage, disabledImage, enabled, TerminalPlugin.getDefault().getImageRegistry());
+ }
+
+ protected void setUpAction(
+ String text, String toolTip, String hoverImage, String enabledImage, String disabledImage, boolean enabled,
+ ImageRegistry imageRegistry) {
+ setUpAction(
+ text, toolTip, imageRegistry.getDescriptor(hoverImage), imageRegistry.getDescriptor(enabledImage),
+ imageRegistry.getDescriptor(disabledImage), enabled);
+ }
+
+ protected void setUpAction(
+ String text, String toolTip, ImageDescriptor hoverImage, ImageDescriptor enabledImage,
+ ImageDescriptor disabledImage, boolean enabled) {
+ setText(text);
+ setToolTipText(toolTip);
+ setEnabled(enabled);
+ if (enabledImage != null) {
+ setImageDescriptor(enabledImage);
+ }
+ if (disabledImage != null) {
+ setDisabledImageDescriptor(disabledImage);
+ }
+ if (hoverImage != null) {
+ setHoverImageDescriptor(hoverImage);
+ }
+ }
+
+ /**
+ * Returns the terminal instance on which the action should operate.
+ *
+ * @return the terminal instance on which the action should operate.
+ */
+ protected ITerminalViewControl getTarget() {
+ return target;
+ }
+
+ /**
+ * Subclasses can update their action.
+ *
+ * @param aboutToShow {@code true} before the menu is shown, {@code false} when the menu gets hidden.
+ */
+ public void updateAction(boolean aboutToShow) {}
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/ActionMessages.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/ActionMessages.java
new file mode 100644
index 0000000..5db6675
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/ActionMessages.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import org.eclipse.osgi.util.NLS;
+
+public class ActionMessages extends NLS {
+ public static String copy;
+ public static String cut;
+ public static String paste;
+ public static String selectAll;
+ public static String clearAll;
+
+ static {
+ Class<?> clazz = ActionMessages.class;
+ NLS.initializeMessages(clazz.getName(), clazz);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/ActionMessages.properties b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/ActionMessages.properties
new file mode 100644
index 0000000..881315b
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/ActionMessages.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2003, 2009 Wind River Systems, Inc. and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+# NLS_MESSAGEFORMAT_NONE
+
+copy = Copy
+cut = Cut
+paste = Paste
+selectAll = Select All
+clearAll = Clear Terminal
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/Images.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/Images.java
new file mode 100644
index 0000000..f5e1961
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/Images.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+public interface Images {
+ String IMAGE_DIR_ROOT = "icons/";
+ String IMAGE_DIR_LOCALTOOL = IMAGE_DIR_ROOT + "clcl16/";
+ String IMAGE_DIR_DLCL = IMAGE_DIR_ROOT + "dlcl16/";
+ String IMAGE_DIR_ELCL = IMAGE_DIR_ROOT + "elcl16/";
+ String IMAGE_CLCL_CLEAR_ALL = "ImageClclClearAll";
+ String IMAGE_DLCL_CLEAR_ALL = "ImageDlclClearAll";
+ String IMAGE_ELCL_CLEAR_ALL = "ImageElclClearAll";
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionClearAll.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionClearAll.java
new file mode 100644
index 0000000..983ec85
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionClearAll.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ ********************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import static com.google.eclipse.elt.emulator.actions.ActionMessages.clearAll;
+import static com.google.eclipse.elt.emulator.actions.Images.*;
+
+import com.google.eclipse.elt.emulator.control.ITerminalViewControl;
+
+public class TerminalActionClearAll extends AbstractTerminalAction {
+ public TerminalActionClearAll() {
+ super(TerminalActionClearAll.class.getName());
+ setupAction();
+ }
+
+ public TerminalActionClearAll(ITerminalViewControl target) {
+ super(target, TerminalActionClearAll.class.getName());
+ setupAction();
+ }
+
+ private void setupAction() {
+ setUpAction(clearAll, clearAll, IMAGE_CLCL_CLEAR_ALL, IMAGE_ELCL_CLEAR_ALL, IMAGE_DLCL_CLEAR_ALL, false);
+ }
+
+ @Override public void run() {
+ ITerminalViewControl target = getTarget();
+ if (target != null) {
+ target.clearTerminal();
+ }
+ }
+
+ @Override public void updateAction(boolean aboutToShow) {
+ ITerminalViewControl target = getTarget();
+ setEnabled(target != null && !target.isEmpty());
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionCopy.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionCopy.java
new file mode 100644
index 0000000..f4664bc
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionCopy.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import static org.eclipse.ui.ISharedImages.*;
+
+import static com.google.eclipse.elt.emulator.actions.ActionMessages.copy;
+
+import org.eclipse.ui.*;
+
+import com.google.eclipse.elt.emulator.control.ITerminalViewControl;
+
+public class TerminalActionCopy extends AbstractTerminalAction {
+ public TerminalActionCopy() {
+ super(TerminalActionCopy.class.getName());
+ setUpAction();
+ }
+
+ public TerminalActionCopy(ITerminalViewControl target) {
+ super(target, TerminalActionCopy.class.getName());
+ setUpAction();
+ }
+
+ private void setUpAction() {
+ ISharedImages shared = PlatformUI.getWorkbench().getSharedImages();
+ setUpAction(
+ copy, copy, shared.getImageDescriptor(IMG_TOOL_COPY), shared.getImageDescriptor(IMG_TOOL_COPY),
+ shared.getImageDescriptor(IMG_TOOL_COPY_DISABLED), true);
+ }
+
+ @Override public void run() {
+ ITerminalViewControl target = getTarget();
+ if (target != null) {
+ String selection = target.getSelection();
+ if (!selection.isEmpty()) {
+ target.copy();
+ } else {
+ target.sendKey('\u0003');
+ }
+ }
+ }
+
+ @Override public void updateAction(boolean aboutToShow) {
+ ITerminalViewControl target = getTarget();
+ boolean enabled = target != null;
+ if (aboutToShow && enabled) {
+ enabled = target.getSelection().length() > 0;
+ }
+ setEnabled(enabled);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionCut.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionCut.java
new file mode 100644
index 0000000..f1559c8
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionCut.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import static org.eclipse.ui.ISharedImages.*;
+
+import static com.google.eclipse.elt.emulator.actions.ActionMessages.cut;
+
+import org.eclipse.ui.*;
+
+import com.google.eclipse.elt.emulator.control.ITerminalViewControl;
+
+public class TerminalActionCut extends AbstractTerminalAction {
+ public TerminalActionCut() {
+ super(TerminalActionCut.class.getName());
+ setUpAction();
+ }
+
+ public TerminalActionCut(ITerminalViewControl target) {
+ super(target, TerminalActionCut.class.getName());
+ setUpAction();
+ }
+
+ private void setUpAction() {
+ ISharedImages shared = PlatformUI.getWorkbench().getSharedImages();
+ setUpAction(
+ cut, cut, shared.getImageDescriptor(IMG_TOOL_CUT), shared.getImageDescriptor(IMG_TOOL_CUT),
+ shared.getImageDescriptor(IMG_TOOL_CUT_DISABLED), true);
+ }
+
+ @Override public void run() {
+ ITerminalViewControl target = getTarget();
+ if (target != null) {
+ target.sendKey('\u0018');
+ }
+ }
+
+ @Override public void updateAction(boolean aboutToShow) {
+ // Cut is always disabled
+ setEnabled(false);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionPaste.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionPaste.java
new file mode 100644
index 0000000..0303f5c
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionPaste.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Initial Contributors:
+ * The following Wind River employees contributed to the Terminal component
+ * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb,
+ * Helmut Haigermoser and Ted Williams.
+ *
+ * Contributors:
+ * Michael Scharf (Wind River) - split into core, view and connector plugins
+ * Martin Oberhuber (Wind River) - fixed copyright headers and beautified
+ * Anna Dushistova (MontaVista) - [227537] moved actions from terminal.view to terminal plugin
+ * Uwe Stieber (Wind River) - [260372] [terminal] Certain terminal actions are enabled if no target terminal control is available
+ * Uwe Stieber (Wind River) - [294719] [terminal] SWT Widget disposed in TerminalActionPaste
+ * Martin Oberhuber (Wind River) - [296212] Cannot paste text into terminal on some Linux hosts
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import static org.eclipse.ui.ISharedImages.*;
+
+import static com.google.eclipse.elt.emulator.actions.ActionMessages.paste;
+import static com.google.eclipse.elt.emulator.provisional.api.TerminalState.CONNECTED;
+
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.ui.*;
+
+import com.google.eclipse.elt.emulator.control.ITerminalViewControl;
+
+public class TerminalActionPaste extends AbstractTerminalAction {
+ public TerminalActionPaste() {
+ super(TerminalActionPaste.class.getName());
+ setUpAction();
+ }
+
+ public TerminalActionPaste(ITerminalViewControl target) {
+ super(target, TerminalActionPaste.class.getName());
+ setUpAction();
+ }
+
+ private void setUpAction() {
+ ISharedImages shared = PlatformUI.getWorkbench().getSharedImages();
+ setUpAction(
+ paste, paste, shared.getImageDescriptor(IMG_TOOL_PASTE), shared.getImageDescriptor(IMG_TOOL_PASTE_DISABLED),
+ shared.getImageDescriptor(IMG_TOOL_PASTE), false);
+ }
+
+ @Override public void run() {
+ ITerminalViewControl target = getTarget();
+ if (target != null) {
+ target.paste();
+ }
+ }
+
+ @Override public void updateAction(boolean aboutToShow) {
+ ITerminalViewControl target = getTarget();
+ boolean enabled = target != null && target.getClipboard() != null && !target.getClipboard().isDisposed();
+ if (enabled) {
+ String text = (String) target.getClipboard().getContents(TextTransfer.getInstance());
+ enabled = text != null && !text.isEmpty() && target.getState() == CONNECTED;
+ }
+ setEnabled(enabled);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionSelectAll.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionSelectAll.java
new file mode 100644
index 0000000..f52d742
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/actions/TerminalActionSelectAll.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.actions;
+
+import static com.google.eclipse.elt.emulator.actions.ActionMessages.selectAll;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import com.google.eclipse.elt.emulator.control.ITerminalViewControl;
+
+public class TerminalActionSelectAll extends AbstractTerminalAction {
+ public TerminalActionSelectAll() {
+ super(TerminalActionSelectAll.class.getName());
+ setUpAction();
+ }
+
+ public TerminalActionSelectAll(ITerminalViewControl target) {
+ super(target, TerminalActionSelectAll.class.getName());
+ setUpAction();
+ }
+
+ private void setUpAction() {
+ setUpAction(selectAll, selectAll, (ImageDescriptor) null, null, null, false);
+ }
+
+ @Override public void run() {
+ ITerminalViewControl target = getTarget();
+ if (target != null) {
+ target.selectAll();
+ }
+ }
+
+ @Override public void updateAction(boolean aboutToShow) {
+ ITerminalViewControl target = getTarget();
+ setEnabled(target != null && !target.isEmpty());
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/connector/TerminalConnector.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/connector/TerminalConnector.java
new file mode 100644
index 0000000..ae5b7f1
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/connector/TerminalConnector.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Michael Scharf (Wind River) - initial API and implementation
+ * Michael Scharf (Wind River) - [200541] Extract from TerminalConnectorExtension.TerminalConnectorProxy
+ * Martin Oberhuber (Wind River) - [225853][api] Provide more default functionality in TerminalConnectorImpl
+ * Uwe Stieber (Wind River) - [282996] [terminal][api] Add "hidden" attribute to terminal connector extension point
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.connector;
+
+import static com.google.eclipse.elt.emulator.provisional.api.TerminalState.CLOSED;
+
+import java.io.OutputStream;
+
+import org.eclipse.core.runtime.*;
+
+import com.google.eclipse.elt.emulator.impl.TerminalMessages;
+import com.google.eclipse.elt.emulator.provisional.api.*;
+import com.google.eclipse.elt.emulator.provisional.api.provider.TerminalConnectorDelegate;
+
+/**
+ * An {@link ITerminalConnector} instance, also known as terminal connection type, for maintaining a single terminal
+ * connection.
+ *
+ * It provides all terminal connector functions that can be provided by static markup without loading the actual
+ * implementation class. The actual {@link TerminalConnectorDelegate} implementation class is lazily loaded by the provided
+ * {@link TerminalConnector.Factory} interface when needed. class, and delegates to the actual implementation when
+ * needed. The following methods can be called without initializing the contributed implementation class:
+ * {@link #getId()}, {@link #getName()}, {@link #getSettingsSummary()}, {@link #setTerminalSize(int, int)},
+ * {@link #getAdapter(Class)}.
+ */
+public class TerminalConnector implements ITerminalConnector {
+ /**
+ * Creates an instance of TerminalConnectorImpl. This is used to lazily load classed defined in extensions.
+ */
+ public interface Factory {
+ TerminalConnectorDelegate makeConnector() throws Exception;
+ }
+
+ private final TerminalConnector.Factory terminalConnectorFactory;
+ private final String name;
+ private final String id;
+
+ private TerminalConnectorDelegate connector;
+
+ //If the initialization of the class specified in the extension fails, this variable contains the error.
+ private Exception initializationException;
+
+ /**
+ * Constructor for the terminal connector.
+ *
+ * @param terminalConnectorFactory Factory for lazily instantiating the TerminalConnectorImpl when needed.
+ * @param id terminal connector ID. The connector is publicly known under this ID.
+ * @param name translatable name to display the connector in the UI.
+ */
+ public TerminalConnector(TerminalConnector.Factory terminalConnectorFactory, String id, String name) {
+ this.terminalConnectorFactory = terminalConnectorFactory;
+ this.id = id;
+ this.name = name;
+ }
+
+ @Override public String getInitializationErrorMessage() {
+ getConnectorDelegate();
+ if (initializationException != null) {
+ return initializationException.getLocalizedMessage();
+ }
+ return null;
+ }
+
+ @Override public String getId() {
+ return id;
+ }
+
+ @Override public String getName() {
+ return name;
+ }
+
+ private TerminalConnectorDelegate getConnectorDelegate() {
+ if (!isInitialized()) {
+ initializeConnector();
+ }
+ return connector;
+ }
+
+ private void initializeConnector() {
+ try {
+ connector = terminalConnectorFactory.makeConnector();
+ connector.initialize();
+ } catch (Exception e) {
+ initializationException = e;
+ connector = new TerminalConnectorDelegate() {
+ @Override protected void connect() {
+ terminalControl.setState(CLOSED);
+ terminalControl.setErrorMessage(getInitializationErrorMessage());
+ }
+
+ @Override public OutputStream getTerminalToRemoteStream() {
+ return null;
+ }
+
+ @Override public String getSettingsSummary() {
+ return null;
+ }
+ };
+ Logger.logException(e);
+ }
+ }
+
+ @Override public boolean isInitialized() {
+ return connector != null || initializationException != null;
+ }
+
+ @Override public void connect(ITerminalControl control) {
+ getConnectorDelegate().connect(control);
+ }
+
+ @Override public void disconnect() {
+ getConnectorDelegate().disconnect();
+ }
+
+ @Override public OutputStream getTerminalToRemoteStream() {
+ return getConnectorDelegate().getTerminalToRemoteStream();
+ }
+
+ @Override public String getSettingsSummary() {
+ if (connector != null) {
+ return getConnectorDelegate().getSettingsSummary();
+ }
+ return TerminalMessages.notInitialized;
+ }
+
+ @Override public boolean isLocalEcho() {
+ return getConnectorDelegate().isLocalEcho();
+ }
+
+ @Override public void setTerminalSize(int newWidth, int newHeight) {
+ // We assume that setTerminalSize is called also after the terminal has been initialized, otherwise we would have to
+ // cache the values.
+ if (connector != null) {
+ connector.setTerminalSize(newWidth, newHeight);
+ }
+ }
+
+ @Override public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+ TerminalConnectorDelegate connector = null;
+ if (isInitialized()) {
+ connector = getConnectorDelegate();
+ }
+ // If we cannot create the connector then we cannot adapt.
+ if (connector != null) {
+ // Maybe the connector is adaptable.
+ if (connector instanceof IAdaptable) {
+ Object result = ((IAdaptable) connector).getAdapter(adapter);
+ // Not sure if the next block is needed.
+ if (result == null) {
+ // Defer to the platform.
+ result = Platform.getAdapterManager().getAdapter(connector, adapter);
+ }
+ if (result != null) {
+ return result;
+ }
+ }
+ // Maybe the real adapter is what we need.
+ if (adapter.isInstance(connector)) {
+ return connector;
+ }
+ }
+ // Maybe we have to be adapted.
+ return Platform.getAdapterManager().getAdapter(this, adapter);
+ }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/connector/TerminalToRemoteInjectionOutputStream.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/connector/TerminalToRemoteInjectionOutputStream.java
new file mode 100644
index 0000000..514c5b0
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/connector/TerminalToRemoteInjectionOutputStream.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.connector;
+
+import java.io.*;
+
+class TerminalToRemoteInjectionOutputStream extends FilterOutputStream {
+ /**
+ * This class handles bytes written to the {@link TerminalToRemoteInjectionOutputStream}.
+ */
+ static public abstract class Interceptor {
+ protected OutputStream original;
+
+ public void begin(OutputStream original) throws IOException {
+ this.original = original;
+ }
+
+ public void write(int b) throws IOException {}
+
+ public void write(byte[] b, int off, int len) throws IOException {}
+
+ public void close() throws IOException {}
+
+ public void flush() {}
+ }
+
+ public static class BufferInterceptor extends Interceptor {
+ private final ByteArrayOutputStream fBuffer = new ByteArrayOutputStream();
+
+ @Override public void close() throws IOException {
+ original.write(fBuffer.toByteArray());
+ }
+
+ @Override public void write(byte[] b, int off, int len) throws IOException {
+ fBuffer.write(b, off, len);
+ }
+
+ @Override public void write(int b) throws IOException {
+ fBuffer.write(b);
+ }
+ }
+
+ private class TerminalFilterOutputStream extends OutputStream {
+ final private Object lock = TerminalToRemoteInjectionOutputStream.this;
+
+ @Override public void close() throws IOException {
+ synchronized (lock) {
+ if (injection == this) {
+ flush();
+ ungrabOutput();
+ }
+ }
+ }
+
+ @Override public void write(byte[] b, int off, int len) throws IOException {
+ synchronized (lock) {
+ checkStream();
+ out.write(b, off, len);
+ }
+ }
+
+ @Override public void write(byte[] b) throws IOException {
+ synchronized (lock) {
+ checkStream();
+ out.write(b);
+ }
+ }
+
+ @Override public void flush() throws IOException {
+ synchronized (lock) {
+ checkStream();
+ out.flush();
+ }
+ }
+
+ @Override public void write(int b) throws IOException {
+ synchronized (lock) {
+ checkStream();
+ out.write(b);
+ }
+ }
+
+ private void checkStream() throws IOException {
+ if (injection != this)
+ {
+ throw new IOException("Stream is closed");
+ }
+ }
+ }
+
+ private Interceptor interceptor;
+ private TerminalFilterOutputStream injection;
+
+ public TerminalToRemoteInjectionOutputStream(OutputStream out) {
+ super(out);
+ }
+
+ synchronized protected void ungrabOutput() throws IOException {
+ if (interceptor != null) {
+ interceptor.close();
+ interceptor = null;
+ }
+ injection = null;
+ }
+
+ /**
+ * There can only be one injection stream active at a time. You must call close on the returned output stream to end
+ * the injection.
+ * @param interceptor This is used handle bytes sent while the injection stream is active.
+ * @return a output stream that can be used to write to the decorated stream.
+ * @throws IOException if something goes wrong.
+ */
+ public synchronized OutputStream grabOutput(Interceptor interceptor) throws IOException {
+ if (injection != null) {
+ throw new IOException("Buffer in use");
+ }
+ this.interceptor = interceptor;
+ this.interceptor.begin(out);
+ injection = new TerminalFilterOutputStream();
+ return injection;
+ }
+
+ public synchronized OutputStream grabOutput() throws IOException {
+ return grabOutput(new BufferInterceptor());
+ }
+
+ @Override synchronized public void close() throws IOException {
+ if (injection != null) {
+ injection.close();
+ }
+ super.close();
+ }
+
+ @Override synchronized public void flush() throws IOException {
+ if (interceptor != null) {
+ interceptor.flush();
+ }
+ out.flush();
+ }
+
+ @Override synchronized public void write(byte[] b, int off, int len) throws IOException {
+ if (interceptor != null) {
+ interceptor.write(b, off, len);
+ } else {
+ out.write(b, off, len);
+ }
+ }
+
+ @Override synchronized public void write(int b) throws IOException {
+ if (interceptor != null) {
+ interceptor.write(b);
+ } else {
+ out.write(b);
+ }
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/CommandInputFieldWithHistory.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/CommandInputFieldWithHistory.java
new file mode 100644
index 0000000..5eeda0e
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/CommandInputFieldWithHistory.java
@@ -0,0 +1,287 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.control;
+
+import static java.util.Arrays.asList;
+
+import java.util.*;
+import java.util.List;
+
+import org.eclipse.jface.fieldassist.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter;
+
+/**
+ * Manages the Command History for the command line input of the terminal control.
+ *
+ * <p>
+ * <ul>
+ * <li>Navigate with ARROW_UP,ARROW_DOWN,PAGE_UP,PAGE_DOWN</li>
+ * <li>ESC to cancel history editing</li>
+ * <li>History can be edited (by moving up and edit) but changes are not persistent (like in bash)</li>
+ * <li>If the same command is entered multiple times in a row, only one entry is kept in the history</li>
+ * </ul>
+ * </p>
+ */
+public class CommandInputFieldWithHistory implements ICommandInputField {
+ final List<String> allHistory = new ArrayList<String>();
+
+ // Keeps a modifiable history while in history editing mode.
+ List<String> editedHistory;
+
+ // The current position in the edit history.
+ private int editHistoryPosition = 0;
+
+ // The limit of the history.
+ private final int maxHistorySize;
+
+ private Text inputField;
+ private Sash sash;
+
+ public CommandInputFieldWithHistory(int maxHistorySize) {
+ this.maxHistorySize = maxHistorySize;
+ }
+
+ /**
+ * Add a line to the history.
+ * @param line The line to be added to the history.
+ */
+ protected void pushLine(String line) {
+ endHistoryMode();
+ // Anything to remember?
+ if (line == null || line.trim().length() == 0) {
+ return;
+ }
+ allHistory.add(0, line);
+ // Ignore if the same as last/
+ if (allHistory.size() > 1 && line.equals(allHistory.get(1))) {
+ allHistory.remove(0);
+ }
+ // Limit the history size.
+ if (allHistory.size() >= maxHistorySize) {
+ allHistory.remove(allHistory.size() - 1);
+ }
+ }
+
+ public void setHistory(String history) {
+ endHistoryMode();
+ allHistory.clear();
+ if (history == null) {
+ return;
+ }
+ allHistory.addAll(asList(history.split("\n")));
+ }
+
+ public String getHistory() {
+ StringBuilder buffer = new StringBuilder();
+ boolean separate = false;
+ for (String line : allHistory) {
+ if (line.length() > 0) {
+ if (separate) {
+ buffer.append("\n");
+ } else {
+ separate = true;
+ }
+ buffer.append(line);
+ }
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Moves a line of text in history.
+ * @param lineToBeMoved the line of text to be moved.
+ * @param count (+1 or -1) for forward and backward movement (-1 goes back.)
+ * @return the new string to be displayed in the command line or {@code null}, if the limit is reached.
+ */
+ public String move(String lineToBeMoved, int count) {
+ if (!inHistoryMode()) {
+ editedHistory = new ArrayList<String>(allHistory.size() + 1);
+ editedHistory.add(lineToBeMoved);
+ editedHistory.addAll(allHistory);
+ editHistoryPosition = 0;
+ }
+ editedHistory.set(editHistoryPosition, lineToBeMoved);
+ if (editHistoryPosition + count >= editedHistory.size()) {
+ return null;
+ }
+ if (editHistoryPosition + count < 0) {
+ return null;
+ }
+ editHistoryPosition += count;
+ return editedHistory.get(editHistoryPosition);
+ }
+
+ private boolean inHistoryMode() {
+ return editedHistory != null;
+ }
+
+ /**
+ * Exit the history movements and go to position 0;
+ * @return the string to be shown in the command line
+ */
+ protected String escape() {
+ if (!inHistoryMode()) {
+ return null;
+ }
+ String line = editedHistory.get(0);
+ endHistoryMode();
+ return line;
+ }
+
+ private void endHistoryMode() {
+ editedHistory = null;
+ editHistoryPosition = 0;
+ }
+
+ @Override public void createControl(final Composite parent, final ITerminalViewControl terminal) {
+ sash = new Sash(parent, SWT.HORIZONTAL);
+ final GridData sashLayoutData = new GridData(SWT.FILL, SWT.CENTER, true, false);
+ sashLayoutData.heightHint = 5;
+ sash.setLayoutData(sashLayoutData);
+ sash.addListener(SWT.Selection, new Listener() {
+ @Override public void handleEvent(Event e) {
+ if (e.detail == SWT.DRAG) {
+ // Don't redraw during drag, it causes paint errors - bug 220971.
+ return;
+ }
+ // No idea why this is needed.
+ GridData inputFieldLayoutData = (GridData) inputField.getLayoutData();
+ Rectangle sashRect = sash.getBounds();
+ Rectangle containerRect = parent.getClientArea();
+
+ int h = inputField.getLineHeight();
+ // Make sure the input filed height is a multiple of the line height.
+ inputFieldLayoutData.heightHint = Math.max(((containerRect.height - e.y - sashRect.height) / h) * h, h);
+ // Do not show less then one line.
+ e.y = Math.min(e.y, containerRect.height - h);
+ inputField.setLayoutData(inputFieldLayoutData);
+ parent.layout();
+ // else the content assist icon will be replicated
+ parent.redraw();
+ }
+ });
+ inputField = new Text(parent, SWT.MULTI | SWT.BORDER | SWT.WRAP | SWT.V_SCROLL);
+ GridData inputFieldLayoutData = new GridData(SWT.FILL, SWT.FILL, true, false);
+ boolean installDecoration = true;
+ if (installDecoration) {
+ // The ContentAssistCommandAdapter says: "The client is responsible for ensuring that adequate space is reserved
+ // for the decoration."
+ // TODO: What is the "adequate space"?
+ inputFieldLayoutData.horizontalIndent = 6;
+ }
+ inputField.setLayoutData(inputFieldLayoutData);
+ inputField.setFont(terminal.getFont());
+ // Register field assist *before* the key listener.
+ // Otherwise the ENTER key is sent *first* to the input field and then to the field assist pop-up.
+ // (https://bugs.eclipse.org/bugs/show_bug.cgi?id=211659)
+ new ContentAssistCommandAdapter(
+ inputField, new TextContentAdapter(), new FieldAssist(), null, null, installDecoration);
+ inputField.addKeyListener(new KeyListener() {
+ @Override public void keyPressed(KeyEvent e) {
+ // If the field assist has handled the key already then ignore it.
+ // (https://bugs.eclipse.org/bugs/show_bug.cgi?id=211659)
+ if (!e.doit) {
+ return;
+ }
+ if (e.keyCode == '\n' || e.keyCode == '\r') {
+ e.doit = false;
+ String line = inputField.getText();
+ if (!terminal.pasteString(line + "\n")) {
+ return;
+ }
+ pushLine(line);
+ setCommand("");
+ }
+ if (e.keyCode == SWT.ARROW_UP || e.keyCode == SWT.PAGE_UP) {
+ e.doit = false;
+ setCommand(move(inputField.getText(), 1));
+ }
+ if (e.keyCode == SWT.ARROW_DOWN || e.keyCode == SWT.PAGE_DOWN) {
+ e.doit = false;
+ setCommand(move(inputField.getText(), -1));
+ }
+ if (e.keyCode == SWT.ESC) {
+ e.doit = false;
+ setCommand(escape());
+ }
+ }
+
+ private void setCommand(String line) {
+ if (line == null) {
+ return;
+ }
+ inputField.setText(line);
+ inputField.setSelection(inputField.getCharCount());
+ }
+
+ @Override public void keyReleased(KeyEvent e) {
+ }
+ });
+ }
+
+ @Override public void setFont(Font font) {
+ inputField.setFont(font);
+ inputField.getParent().layout(true);
+ }
+
+ @Override public void dispose() {
+ sash.dispose();
+ sash = null;
+ inputField.dispose();
+ inputField = null;
+ }
+
+ private class FieldAssist implements IContentProposalProvider {
+ @Override public IContentProposal[] getProposals(String contents, int position) {
+ String prefix = contents.substring(0, position);
+ List<IContentProposal> result = new ArrayList<IContentProposal>();
+ Collection<String> seen = new HashSet<String>();
+ for (String history : allHistory) {
+ if (history.startsWith(prefix) && !seen.contains(history)) {
+ // The content is the rest of the history item.
+ String content = history.substring(prefix.length());
+ result.add(new Proposal(content, history));
+ // Don't add this proposal again.
+ seen.add(history);
+ }
+ }
+ return result.toArray(new IContentProposal[result.size()]);
+ }
+ }
+
+ private static class Proposal implements IContentProposal {
+ private final String content;
+ private final String label;
+
+ Proposal(String content, String label) {
+ this.content = content;
+ this.label = label;
+ }
+
+ @Override public String getContent() {
+ return content;
+ }
+
+ @Override public int getCursorPosition() {
+ return content.length();
+ }
+
+ @Override public String getDescription() {
+ return null;
+ }
+
+ @Override public String getLabel() {
+ return label;
+ }
+ }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ICommandInputField.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ICommandInputField.java
new file mode 100644
index 0000000..d70450d
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ICommandInputField.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.control;
+
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * Interface to create a command input control.
+ */
+public interface ICommandInputField {
+ void createControl(Composite parent, ITerminalViewControl terminal);
+
+ void dispose();
+
+ /**
+ * Sets the font of a control created with {@link #createControl(Composite, ITerminalViewControl)}.
+ * @param font the new text font.
+ */
+ void setFont(Font font);
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ITerminalListener.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ITerminalListener.java
new file mode 100644
index 0000000..df8c6c5
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ITerminalListener.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.control;
+
+import com.google.eclipse.elt.emulator.provisional.api.TerminalState;
+
+/**
+ * Provided by a view implementation.
+ */
+public interface ITerminalListener {
+ /**
+ * Called when the state of the connection has changed.
+ * @param state the state of the connection.
+ */
+ void setState(TerminalState state);
+
+ /**
+ * Set the title of the terminal.
+ * @param title the new title.
+ */
+ void setTerminalTitle(String title);
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ITerminalViewControl.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ITerminalViewControl.java
new file mode 100644
index 0000000..65ddab2
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/control/ITerminalViewControl.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ ******************************************************************************/
+package com.google.eclipse.elt.emulator.control;
+
+import java.io.UnsupportedEncodingException;
+
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Control;
+
+import com.google.eclipse.elt.emulator.provisional.api.*;
+
+public interface ITerminalViewControl {
+ void setEncoding(String encoding) throws UnsupportedEncodingException;
+
+ String getEncoding();
+
+ boolean isEmpty();
+
+ void setFont(Font font);
+
+ void setInvertedColors(boolean invert);
+
+ Font getFont();
+
+ Control getControl();
+
+ Control getRootControl();
+
+ boolean isDisposed();
+
+ void selectAll();
+
+ void clearTerminal();
+
+ void copy();
+
+ void paste();
+
+ String getSelection();
+
+ TerminalState getState();
+
+ Clipboard getClipboard();
+
+ void connectTerminal();
+
+ void disconnectTerminal();
+
+ void disposeTerminal();
+
+ boolean isConnected();
+
+ String getSettingsSummary();
+
+ boolean setFocus();
+
+ ITerminalConnector getTerminalConnector();
+
+ void setConnector(ITerminalConnector connector);
+
+ ITerminalConnector[] getConnectors();
+
+ void sendKey(char arg0);
+
+ public boolean pasteString(String string);
+
+ void setCommandInputField(ICommandInputField inputField);
+
+ ICommandInputField getCommandInputField();
+
+ public int getBufferLineLimit();
+
+ public void setBufferLineLimit(int bufferLineLimit);
+
+ boolean isScrollLockOn();
+
+ void setScrollLockOn(boolean on);
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/EditActionAccelerators.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/EditActionAccelerators.java
new file mode 100644
index 0000000..f39c7e2
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/EditActionAccelerators.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.emulator.core;
+
+import static org.eclipse.jface.bindings.keys.SWTKeySupport.convertKeyStrokeToAccelerator;
+
+import java.util.*;
+
+import org.eclipse.jface.bindings.TriggerSequence;
+import org.eclipse.jface.bindings.keys.*;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.keys.IBindingService;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class EditActionAccelerators {
+ private static final String COPY_COMMAND_ID = "com.google.eclipse.terminal.local.copy";
+ private static final String PASTE_COMMAND_ID = "com.google.eclipse.terminal.local.paste";
+
+ private final Map<Integer, String> commandIdsByAccelerator = new HashMap<Integer, String>();
+
+ private void load() {
+ addAccelerator(COPY_COMMAND_ID);
+ addAccelerator(PASTE_COMMAND_ID);
+ }
+
+ private void addAccelerator(String commandId) {
+ KeySequence keySequence = bindingFor(commandId);
+ if (keySequence == null) {
+ return;
+ }
+ KeyStroke[] keyStrokes = keySequence.getKeyStrokes();
+ if (keyStrokes.length != 0) {
+ int accelerator = convertKeyStrokeToAccelerator(keyStrokes[0]);
+ commandIdsByAccelerator.put(new Integer(accelerator), commandId);
+ }
+ }
+
+ private static KeySequence bindingFor(String commandId) {
+ IBindingService bindingService = bindingService();
+ TriggerSequence binding = bindingService.getBestActiveBindingFor(commandId);
+ if (binding instanceof KeySequence) {
+ KeySequence keySequence = (KeySequence) binding;
+ return keySequence;
+ }
+ return null;
+ }
+
+ private static IBindingService bindingService() {
+ return (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class);
+ }
+
+ boolean isCopyAction(int accelerator) {
+ return isMatchingAction(accelerator, COPY_COMMAND_ID);
+ }
+
+ boolean isPasteAction(int accelerator) {
+ return isMatchingAction(accelerator, PASTE_COMMAND_ID);
+ }
+
+ private boolean isMatchingAction(int accelerator, String commandId) {
+ if (commandIdsByAccelerator.isEmpty()) {
+ load();
+ }
+ return commandId.equals(commandIdsByAccelerator.get(new Integer(accelerator)));
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/IVT100EmulatorBackend.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/IVT100EmulatorBackend.java
new file mode 100644
index 0000000..8519a39
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/IVT100EmulatorBackend.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.core;
+
+import java.util.List;
+
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+
+import com.google.eclipse.elt.emulator.model.Style;
+
+public interface IVT100EmulatorBackend {
+ /**
+ * Erases all text from the terminal view. Including the history.
+ */
+ void clearAll();
+
+ /**
+ * Sets the dimensions of the addressable scroll space of the screen. Keeps the cursor position relative to the bottom
+ * of the screen.
+ *
+ * @param lines the number of lines.
+ * @param columns the number of columns.
+ */
+ void setDimensions(int lines, int columns);
+
+ /**
+ * Makes room for N characters on the current line at the cursor position. Text under the cursor moves right
+ * without wrapping at the end of the line.
+ *
+ * @param charactersToInsert the number of characters to insert.
+ */
+ void insertCharacters(int charactersToInsert);
+
+ /**
+ * Erases from the cursor position (inclusive) to the end of screen. The cursor does not move.
+ */
+ void eraseToEndOfScreen();
+
+ /**
+ * Erases from the beginning of screen to the cursor position (inclusive). The cursor does not move.
+ */
+ void eraseToCursor();
+
+ /**
+ * Erases the complete display. All lines are erased and changed to single-width. The cursor does not move.
+ */
+ void eraseAll();
+
+ /**
+ * Erases the complete line.
+ */
+ void eraseLine();
+
+ /**
+ * Erases from the cursor position (inclusive) to the end of line.
+ */
+ void eraseLineToEnd();
+
+ /**
+ * Erases from beginning of line to the cursor position (inclusive).
+ */
+ void eraseLineToCursor();
+
+ /**
+ * Inserts n lines at line with cursor. Lines displayed below cursor are moved down. Lines moved past the bottom
+ * margin are lost. This sequence is ignored when cursor is outside scrolling region.
+ *
+ * @param lineCount the number of lines to insert.
+ */
+ void insertLines(int lineCount);
+
+ /**
+ * Deletes n characters, starting with the character at the cursor position. When a character is deleted, all
+ * characters to the right of cursor move left. This creates a space character at right margin. This character has
+ * same character attribute as the last character moved left.
+ *
+ * @param charCount the number of characters to delete.
+ */
+ void deleteCharacters(int charCount);
+
+ /**
+ * Deletes n lines, starting at line with cursor. As lines are deleted, the lines displayed below cursor move up.
+ * The lines added to bottom of screen have spaces with the same character attributes as the last line moved up.
+ * This sequence is ignored when cursor is outside scrolling region.
+ *
+ * @param lineCount the number of lines to delete.
+ */
+ void deleteLines(int lineCount);
+
+ Style getDefaultStyle();
+
+ void setDefaultStyle(Style defaultStyle);
+
+ Style getStyle();
+
+ /**
+ * Sets the style to use.
+ *
+ * @param style the new style.
+ */
+ void setStyle(Style style);
+
+ /**
+ * Displays a subset of the newly-received text in the terminal view, wrapping text at the right edge of the screen
+ * and overwriting text when the cursor is not at the very end of the screen's text.
+ *
+ * <p>
+ * There are never any ANSI control characters or escape sequences in the text being displayed by this method (this
+ * includes newlines, carriage returns, and tabs.)
+ * </p>
+ *
+ * @param buffer the text to append.
+ */
+ void appendString(String buffer);
+
+ /**
+ * Process a newline (Control-J) character. A newline (NL) character just moves the cursor to the same column on the
+ * next line, creating new lines when the cursor reaches the bottom edge of the terminal. This is counter-intuitive,
+ * especially to UNIX programmers who are taught that writing a single NL to a terminal is sufficient to move the
+ * cursor to the first column of the next line, as if a carriage return (CR) and a NL were written.
+ *
+ * <p>
+ * UNIX terminals typically display a NL character as a CR followed by a NL because the terminal device typically has
+ * the ONLCR attribute bit set (see the termios(4) man page for details), which causes the terminal device driver to
+ * translate NL to CR + NL on output. The terminal itself (i.e., a hardware terminal or a terminal emulator, like
+ * xterm or this code) _always_ interprets a CR to mean "move the cursor to the beginning of the current
+ * line" and a NL to mean "move the cursor to the same column on the next line".
+ * </p>
+ */
+ void processNewline();
+
+ /**
+ * Returns the relative line number of the line containing the cursor. The returned line number is relative to the
+ * top-most visible line, which has relative line number 0.
+ *
+ * @return the relative line number of the line containing the cursor.
+ */
+ int getCursorLine();
+
+ int getCursorColumn();
+
+ /**
+ * Moves the cursor to the specified line and column.
+ *
+ * @param targetLine is the line number of a screen line, so it has a minimum value of 0 (the topmost screen line) and
+ * a maximum value of heightInLines - 1 (the bottom-most screen line). A line does not have to contain any text to
+ * move the cursor to any column in that line.
+ * @param targetColumn the given column.
+ */
+ void setCursor(int targetLine, int targetColumn);
+
+ void setCursorColumn(int targetColumn);
+
+ void setCursorLine(int targetLine);
+
+ int getLines();
+
+ int getColumns();
+
+ List<IHyperlink> hyperlinksAt(int line);
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/LoggingOutputStream.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/LoggingOutputStream.java
new file mode 100644
index 0000000..1b4c9e4
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/LoggingOutputStream.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.core;
+
+import java.io.*;
+
+import com.google.eclipse.elt.emulator.provisional.api.Logger;
+
+public class LoggingOutputStream extends FilterOutputStream {
+ public LoggingOutputStream(OutputStream out) {
+ super(out);
+ }
+
+ @Override public void write(byte[] b, int off, int len) throws IOException {
+ if (Logger.isLogEnabled()) {
+ Logger.log("Received " + len + " bytes: '" + Logger.encode(new String(b, 0, len)) + "'");
+ }
+ if ((off | len | (b.length - (len + off)) | (off + len)) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ for (int i = 0; i < len; i++) {
+ super.write(b[off + i]);
+ }
+ }
+
+ @Override public void write(int b) throws IOException {
+ if (Logger.isLogEnabled()) {
+ Logger.log("Received " + 1 + " bytes: '" + Logger.encode(new String(new byte[] { (byte) b }, 0, 1)) + "'");
+ }
+ super.write(b);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100BackendTraceDecorator.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100BackendTraceDecorator.java
new file mode 100644
index 0000000..3e1d689
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100BackendTraceDecorator.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.core;
+
+import java.io.PrintStream;
+import java.util.List;
+
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+
+import com.google.eclipse.elt.emulator.model.Style;
+
+public class VT100BackendTraceDecorator implements IVT100EmulatorBackend {
+ final IVT100EmulatorBackend backend;
+ final PrintStream out;
+
+ public VT100BackendTraceDecorator(IVT100EmulatorBackend backend, PrintStream out) {
+ this.backend = backend;
+ this.out = out;
+ }
+
+ @Override public void appendString(String buffer) {
+ out.println("appendString(\"" + buffer + "\")");
+ backend.appendString(buffer);
+ }
+
+ @Override public void clearAll() {
+ out.println("clearAll()");
+ backend.clearAll();
+ }
+
+ @Override public void deleteCharacters(int n) {
+ out.println("deleteCharacters(" + n + ")");
+ backend.deleteCharacters(n);
+ }
+
+ @Override public void deleteLines(int n) {
+ out.println("deleteLines(" + n + ")");
+ backend.deleteLines(n);
+ }
+
+ @Override public void eraseAll() {
+ out.println("eraseAll()");
+ backend.eraseAll();
+ }
+
+ @Override public void eraseLine() {
+ out.println("eraseLine()");
+ backend.eraseLine();
+ }
+
+ @Override public void eraseLineToCursor() {
+ out.println("eraseLineToCursor()");
+ backend.eraseLineToCursor();
+ }
+
+ @Override public void eraseLineToEnd() {
+ out.println("eraseLineToEnd()");
+ backend.eraseLineToEnd();
+ }
+
+ @Override public void eraseToCursor() {
+ out.println("eraseToCursor()");
+ backend.eraseToCursor();
+ }
+
+ @Override public void eraseToEndOfScreen() {
+ out.println("eraseToEndOfScreen()");
+ backend.eraseToEndOfScreen();
+ }
+
+ @Override public int getColumns() {
+ return backend.getColumns();
+ }
+
+ @Override public int getCursorColumn() {
+ return backend.getCursorColumn();
+ }
+
+ @Override public int getCursorLine() {
+ return backend.getCursorLine();
+ }
+
+ @Override public Style getDefaultStyle() {
+ return backend.getDefaultStyle();
+ }
+
+ @Override public int getLines() {
+ return backend.getLines();
+ }
+
+ @Override public Style getStyle() {
+ return backend.getStyle();
+ }
+
+ @Override public void insertCharacters(int charactersToInsert) {
+ out.println("insertCharacters(" + charactersToInsert + ")");
+ backend.insertCharacters(charactersToInsert);
+ }
+
+ @Override public void insertLines(int n) {
+ out.println("insertLines(" + n + ")");
+ backend.insertLines(n);
+ }
+
+ @Override public void processNewline() {
+ out.println("processNewline()");
+ backend.processNewline();
+ }
+
+ @Override public void setCursor(int targetLine, int targetColumn) {
+ out.println("setCursor(" + targetLine + ", " + targetColumn + ")");
+ backend.setCursor(targetLine, targetColumn);
+ }
+
+ @Override public void setCursorColumn(int targetColumn) {
+ out.println("setCursorColumn(" + targetColumn + ")");
+ backend.setCursorColumn(targetColumn);
+ }
+
+ @Override public void setCursorLine(int targetLine) {
+ out.println("setCursorLine(" + targetLine + ")");
+ backend.setCursorLine(targetLine);
+ }
+
+ @Override public void setDefaultStyle(Style defaultStyle) {
+ out.println("setDefaultStyle(" + defaultStyle + ")");
+ backend.setDefaultStyle(defaultStyle);
+ }
+
+ @Override public void setDimensions(int lines, int cols) {
+ out.println("setDimensions(" + lines + "," + cols + ")");
+ backend.setDimensions(lines, cols);
+ }
+
+ @Override public void setStyle(Style style) {
+ out.println("setStyle(" + style + ")");
+ backend.setStyle(style);
+ }
+
+ @Override public List<IHyperlink> hyperlinksAt(int line) {
+ return backend.hyperlinksAt(line);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100Emulator.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100Emulator.java
new file mode 100644
index 0000000..f6625f8
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100Emulator.java
@@ -0,0 +1,834 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.core;
+
+import java.io.*;
+import java.util.List;
+
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.swt.events.*;
+
+import com.google.eclipse.elt.emulator.impl.*;
+import com.google.eclipse.elt.emulator.model.*;
+import com.google.eclipse.elt.emulator.provisional.api.*;
+
+/**
+ * This class processes character data received from the remote host and displays it to the user using the Terminal
+ * view's StyledText widget. This class processes ANSI control characters, including NUL, backspace, carriage return,
+ * line-feed, and a subset of ANSI escape sequences sufficient to allow use of screen-oriented applications, such as vi,
+ * Emacs, and any GNU readline-enabled application (bash, bc, ncftp, etc.).
+ * <p>
+ */
+public class VT100Emulator implements ControlListener {
+ // This is a character processing state: Initial state.
+ private static final int ANSISTATE_INITIAL = 0;
+
+ // This is a character processing state: We've seen an escape character.
+ private static final int ANSISTATE_ESCAPE = 1;
+
+ // Character processing state: We've seen a '[' after an escape character. Expecting a parameter character or a
+ // command character next.
+ private static final int ANSISTATE_EXPECTING_PARAMETER_OR_COMMAND = 2;
+
+ // Character processing state: We've seen a ']' after an escape character. We are now expecting an operating system
+ // command that reprograms an intelligent terminal.
+ private static final int ANSISTATE_EXPECTING_OS_COMMAND = 3;
+
+ /**
+ * This field holds the current state of the Finite Terminal State Automaton (FSA) that recognizes ANSI escape
+ * sequences.
+ *
+ * @see #processNewText()
+ */
+ private int ansiState = ANSISTATE_INITIAL;
+
+ // Holds a reference to the {@link TerminalControl} object that instantiates this class.
+ private final ITerminalControlForText terminal;
+
+ // Holds a reference to the StyledText widget that is used to display text to the user.
+ private final IVT100EmulatorBackend text;
+
+ // Holds the saved absolute line number of the cursor when processing the "ESC 7" and "ESC 8" command sequences.
+ private int savedCursorLine = 0;
+
+ // Holds the saved column number of the cursor when processing the "ESC 7" and "ESC 8" command sequences.
+ private int savedCursorColumn = 0;
+
+ // Holds an array of StringBuffer objects, each of which is one parameter from the current ANSI escape
+ // sequence. For example, when parsing the escape sequence "\e[20;10H", this array holds the strings "20" and "10".
+ private final StringBuffer[] parameters = new StringBuffer[16];
+
+ // Holds the OS-specific command found in an escape sequence of the form "\e]...\u0007".
+ private final StringBuffer ansiOsCommand = new StringBuffer(128);
+
+ // Holds the index of the next unused element of the array stored in field 'parameters'.
+ private int nextAnsiParameter = 0;
+
+ private int nextChar = -1;
+
+ private Reader reader;
+
+ private boolean crAfterNewLine;
+
+ /**
+ * The constructor.
+ */
+ public VT100Emulator(ITerminalTextData data, ITerminalControlForText terminal, Reader reader) {
+ super();
+ this.terminal = terminal;
+ for (int i = 0; i < parameters.length; ++i) {
+ parameters[i] = new StringBuffer();
+ }
+ setInputStreamReader(reader);
+ if (TerminalPlugin.isOptionEnabled("com.google.eclipse.tm.terminal/debug/log/VT100Backend")) {
+ text = new VT100BackendTraceDecorator(new VT100EmulatorBackend(data), System.out);
+ } else {
+ text = new VT100EmulatorBackend(data);
+ }
+ }
+
+ /**
+ * Set the reader that this terminal gets its input from.
+ *
+ * The reader can be changed while the Terminal is running, but a change of the reader likely loses some characters
+ * which have not yet been fully read. Changing the reader can be done in order to change the selected Encoding,
+ * though. This is typically done when the Terminal is constructed, i.e. before it really starts operation; or, when
+ * the user manually selects a different encoding and thus doesn't care about losing old characters.
+ *
+ * @param reader the new reader.
+ */
+ public void setInputStreamReader(Reader reader) {
+ this.reader = reader;
+ }
+
+ public void setDimensions(int lines, int cols) {
+ text.setDimensions(lines, cols);
+ ITerminalConnector telnetConnection = getConnector();
+ if (telnetConnection != null) {
+ telnetConnection.setTerminalSize(text.getColumns(), text.getLines());
+ }
+ }
+
+ public void dispose() {
+ }
+
+ @Override public void controlMoved(ControlEvent event) {
+ }
+
+ @Override public void controlResized(ControlEvent event) {
+ adjustTerminalDimensions();
+ }
+
+ public void clearTerminal() {
+ text.clearAll();
+ }
+
+ public void fontChanged() {
+ if (text != null) {
+ adjustTerminalDimensions();
+ }
+ }
+
+ public void processText() {
+ try {
+ // Find the width and height of the terminal, and resize it to display an integral number of lines and columns.
+ adjustTerminalDimensions();
+ // Restore the caret offset, process and display the new text, then save
+ // the caret offset. See the documentation for field caretOffset for
+ // details.
+ // ISSUE: Is this causing the scroll-to-bottom-on-output behavior?
+ try {
+ processNewText();
+ } catch (IOException e) {
+ Logger.logException(e);
+ }
+ } catch (RuntimeException e) {
+ Logger.logException(e);
+ }
+ }
+
+ /**
+ * This method scans the newly received text, processing ANSI control characters and escape sequences and displaying
+ * normal text.
+ *
+ * @throws IOException if something goes wrong.
+ */
+ private void processNewText() throws IOException {
+ // Scan the newly received text.
+ while (hasNextChar()) {
+ char character = getNextChar();
+ switch (ansiState) {
+ case ANSISTATE_INITIAL:
+ switch (character) {
+ case '\u0000':
+ break; // NUL character. Ignore it.
+ case '\u0007':
+ processBEL(); // BEL (Control-G)
+ break;
+ case '\b':
+ processBackspace(); // Backspace
+ break;
+ case '\t':
+ processTab(); // Tab.
+ break;
+ case '\n':
+ processNewline(); // Newline (Control-J)
+ if (crAfterNewLine) {
+ processCarriageReturn(); // Carriage Return (Control-M)
+ }
+ break;
+ case '\r':
+ processCarriageReturn(); // Carriage Return (Control-M)
+ break;
+ case '\u001b':
+ ansiState = ANSISTATE_ESCAPE; // Escape.
+ break;
+ default:
+ processNonControlCharacters(character);
+ break;
+ }
+ break;
+ case ANSISTATE_ESCAPE:
+ // We've seen an escape character. Here, we process the character immediately following the escape.
+ switch (character) {
+ case '[':
+ ansiState = ANSISTATE_EXPECTING_PARAMETER_OR_COMMAND;
+ nextAnsiParameter = 0;
+ // Erase the parameter strings in preparation for optional parameter characters.
+ for (int i = 0; i < parameters.length; ++i) {
+ parameters[i].delete(0, parameters[i].length());
+ }
+ break;
+ case ']':
+ ansiState = ANSISTATE_EXPECTING_OS_COMMAND;
+ ansiOsCommand.delete(0, ansiOsCommand.length());
+ break;
+ case '7':
+ // Save cursor position and character attributes.
+ ansiState = ANSISTATE_INITIAL;
+ savedCursorLine = relativeCursorLine();
+ savedCursorColumn = getCursorColumn();
+ break;
+ case '8':
+ // Restore cursor and attributes to previously saved position.
+ ansiState = ANSISTATE_INITIAL;
+ moveCursor(savedCursorLine, savedCursorColumn);
+ break;
+ case 'c':
+ // Reset the terminal.
+ ansiState = ANSISTATE_INITIAL;
+ resetTerminal();
+ break;
+ default:
+ Logger.log("Unsupported escape sequence: escape '" + character + "'");
+ ansiState = ANSISTATE_INITIAL;
+ break;
+ }
+ break;
+ case ANSISTATE_EXPECTING_PARAMETER_OR_COMMAND:
+ // Parameters can appear after the '[' in an escape sequence, but they are optional.
+ if (character == '@' || (character >= 'A' && character <= 'Z') || (character >= 'a' && character <= 'z')) {
+ ansiState = ANSISTATE_INITIAL;
+ processAnsiCommandCharacter(character);
+ } else {
+ processAnsiParameterCharacter(character);
+ }
+ break;
+ case ANSISTATE_EXPECTING_OS_COMMAND:
+ // A BEL (\u0007) character marks the end of the OSC sequence.
+ if (character == '\u0007') {
+ ansiState = ANSISTATE_INITIAL;
+ processAnsiOsCommand();
+ } else {
+ ansiOsCommand.append(character);
+ }
+ break;
+ default:
+ // This should never happen! If it does happen, it means there is a bug in the FSA. For robustness, we return to
+ // the initial state.
+ Logger.log("INVALID ANSI FSA STATE: " + ansiState);
+ ansiState = ANSISTATE_INITIAL;
+ break;
+ }
+ }
+ }
+
+ private void resetTerminal() {
+ text.eraseAll();
+ text.setCursor(0, 0);
+ text.setStyle(text.getDefaultStyle());
+ }
+
+ // This method is called when we have parsed an OS Command escape sequence. The only one we support is
+ // "\e]0;...\u0007", which sets the terminal title.
+ private void processAnsiOsCommand() {
+ if (ansiOsCommand.charAt(0) != '0' || ansiOsCommand.charAt(1) != ';') {
+ Logger.log("Ignoring unsupported ANSI OSC sequence: '" + ansiOsCommand + "'");
+ return;
+ }
+ terminal.setTerminalTitle(ansiOsCommand.substring(2));
+ }
+
+ // Dispatches control to various processing methods based on the command character found in the most recently received
+ // ANSI escape sequence. This method only handles command characters that follow the ANSI standard Control Sequence
+ // Introducer (CSI), which is "\e[...", where "..." is an optional ';'-separated sequence of numeric parameters.
+ private void processAnsiCommandCharacter(char ansiCommandCharacter) {
+ // If the width or height of the terminal is ridiculously small (one line or column or less), don't even try to
+ // process the escape sequence. This avoids throwing an exception (SPR 107450). The display will be messed up, but
+ // what did you user expect by making the terminal so small?
+ switch (ansiCommandCharacter) {
+ case '@':
+ // Insert character(s).
+ processAnsiCommand_atsign();
+ break;
+ case 'A':
+ // Move cursor up N lines (default 1).
+ processAnsiCommand_A();
+ break;
+ case 'B':
+ // Move cursor down N lines (default 1).
+ processAnsiCommand_B();
+ break;
+ case 'C':
+ // Move cursor forward N columns (default 1).
+ processAnsiCommand_C();
+ break;
+ case 'D':
+ // Move cursor backward N columns (default 1).
+ processAnsiCommand_D();
+ break;
+ case 'E':
+ // Move cursor to first column of Nth next line (default 1).
+ processAnsiCommand_E();
+ break;
+ case 'F':
+ // Move cursor to first column of Nth previous line (default 1).
+ processAnsiCommand_F();
+ break;
+ case 'G':
+ // Move to column N of current line (default 1).
+ processAnsiCommand_G();
+ break;
+ case 'H':
+ // Set cursor Position.
+ processAnsiCommand_H();
+ break;
+ case 'J':
+ // Erase part or all of display. Cursor does not move.
+ processAnsiCommand_J();
+ break;
+ case 'K':
+ // Erase in line (cursor does not move).
+ processAnsiCommand_K();
+ break;
+ case 'L':
+ // Insert line(s) (current line moves down).
+ processAnsiCommand_L();
+ break;
+ case 'M':
+ // Delete line(s).
+ processAnsiCommand_M();
+ break;
+ case 'm':
+ // Set Graphics Rendition (SGR).
+ processAnsiCommand_m();
+ break;
+ case 'n':
+ // Device Status Report (DSR).
+ processAnsiCommand_n();
+ break;
+ case 'P':
+ // Delete character(s).
+ processAnsiCommand_P();
+ break;
+ case 'S':
+ // Scroll up.
+ // Emacs, vi, and GNU readline don't seem to use this command, so we ignore it for now.
+ break;
+ case 'T':
+ // Scroll down.
+ // Emacs, vi, and GNU readline don't seem to use this command, so we ignore it for now.
+ break;
+ case 'X':
+ // Erase character.
+ // Emacs, vi, and GNU readline don't seem to use this command, so we ignore it for now.
+ break;
+ case 'Z':
+ // Cursor back tab.
+ // Emacs, vi, and GNU readline don't seem to use this command, so we ignore it for now.
+ break;
+
+ default:
+ Logger.log("Ignoring unsupported ANSI command character: '" + ansiCommandCharacter + "'");
+ break;
+ }
+ }
+
+ // Makes room for N characters on the current line at the cursor position. Text under the cursor moves right without
+ // wrapping at the end of the line.
+ private void processAnsiCommand_atsign() {
+ int charactersToInsert = getAnsiParameter(0);
+ text.insertCharacters(charactersToInsert);
+ }
+
+ // Moves the cursor up by the number of lines specified by the escape sequence parameter (default 1).
+ private void processAnsiCommand_A() {
+ moveCursorUp(getAnsiParameter(0));
+ }
+
+ // Moves the cursor down by the number of lines specified by the escape sequence parameter (default 1).
+ private void processAnsiCommand_B() {
+ moveCursorDown(getAnsiParameter(0));
+ }
+
+ // Moves the cursor forward by the number of columns specified by the escape sequence parameter (default 1).
+ private void processAnsiCommand_C() {
+ moveCursorForward(getAnsiParameter(0));
+ }
+
+ // Moves the cursor backward by the number of columns specified by the escape sequence parameter (default 1).
+ private void processAnsiCommand_D() {
+ moveCursorBackward(getAnsiParameter(0));
+ }
+
+ // Moves the cursor to the first column of the Nth next line, where N is specified by the ANSI parameter (default 1).
+ private void processAnsiCommand_E() {
+ int linesToMove = getAnsiParameter(0);
+ moveCursor(relativeCursorLine() + linesToMove, 0);
+ }
+
+ // Moves the cursor to the first column of the Nth previous line, where N is specified by the ANSI parameter
+ // (default 1).
+ private void processAnsiCommand_F() {
+ int linesToMove = getAnsiParameter(0);
+ moveCursor(relativeCursorLine() - linesToMove, 0);
+ }
+
+ // Moves the cursor within the current line to the column specified by the ANSI parameter (default is column 1).
+ private void processAnsiCommand_G() {
+ int targetColumn = 1;
+ if (parameters[0].length() > 0) {
+ targetColumn = getAnsiParameter(0) - 1;
+ }
+ moveCursor(relativeCursorLine(), targetColumn);
+ }
+
+ // Sets the cursor to a position specified by the escape sequence parameters (default is the upper left corner of the
+ // screen).
+ private void processAnsiCommand_H() {
+ moveCursor(getAnsiParameter(0) - 1, getAnsiParameter(1) - 1);
+ }
+
+ // Deletes some (or all) of the text on the screen without moving the cursor.
+ private void processAnsiCommand_J() {
+ int parameter;
+ if (parameters[0].length() == 0) {
+ parameter = 0;
+ } else {
+ parameter = getAnsiParameter(0);
+ }
+ switch (parameter) {
+ case 0:
+ text.eraseToEndOfScreen();
+ break;
+ case 1:
+ // Erase from beginning to current position (inclusive).
+ text.eraseToCursor();
+ break;
+ case 2:
+ // Erase entire display.
+ text.eraseAll();
+ break;
+ default:
+ Logger.log("Unexpected J-command parameter: " + parameter);
+ break;
+ }
+ }
+
+ // Deletes some (or all) of the text in the current line without moving the cursor.
+ private void processAnsiCommand_K() {
+ int parameter = getAnsiParameter(0);
+ switch (parameter) {
+ case 0:
+ // Erase from beginning to current position (inclusive).
+ text.eraseLineToCursor();
+ break;
+ case 1:
+ // Erase from current position to end (inclusive).
+ text.eraseLineToEnd();
+ break;
+ case 2:
+ // Erase entire line.
+ text.eraseLine();
+ break;
+ default:
+ Logger.log("Unexpected K-command parameter: " + parameter);
+ break;
+ }
+ }
+
+ // Inserts one or more blank lines. The current line of text moves down. Text that falls off the bottom of the screen
+ // is deleted.
+ private void processAnsiCommand_L() {
+ text.insertLines(getAnsiParameter(0));
+ }
+
+ // Deletes one or more lines of text. Any lines below the deleted lines move up, which we implement by appending
+ // new lines to the end of the text.
+ private void processAnsiCommand_M() {
+ text.deleteLines(getAnsiParameter(0));
+ }
+
+ // Sets a new graphics rendition mode, such as foreground/background color, bold/normal text, and reverse video.
+ private void processAnsiCommand_m() {
+ if (parameters[0].length() == 0) {
+ // This a special case: when no ANSI parameter is specified, act like a single parameter equal to 0 was specified.
+ parameters[0].append('0');
+ }
+ Style style = text.getStyle();
+ // There are a non-zero number of ANSI parameters. Process each one in order.
+ int parameterCount = parameters.length;
+ int parameterIndex = 0;
+ while (parameterIndex < parameterCount && parameters[parameterIndex].length() > 0) {
+ int parameter = getAnsiParameter(parameterIndex);
+ switch (parameter) {
+ case 0:
+ // Reset all graphics modes.
+ style = text.getDefaultStyle();
+ break;
+ case 1:
+ style = style.setBold(true);
+ break;
+ case 4:
+ style = style.setUnderline(true);
+ break;
+ case 5:
+ style = style.setBlink(true);
+ break;
+ case 7:
+ style = style.setReverse(true);
+ break;
+ case 10: // Set primary font. Ignored.
+ break;
+ case 21:
+ case 22:
+ style = style.setBold(false);
+ break;
+ case 24:
+ style = style.setUnderline(false);
+ break;
+ case 25:
+ style = style.setBlink(false);
+ break;
+ case 27:
+ style = style.setReverse(false);
+ break;
+ case 30:
+ style = style.setForground("BLACK");
+ break;
+ case 31:
+ style = style.setForground("RED");
+ break;
+ case 32:
+ style = style.setForground("GREEN");
+ break;
+ case 33:
+ style = style.setForground("YELLOW");
+ break;
+ case 34:
+ style = style.setForground("BLUE");
+ break;
+ case 35:
+ style = style.setForground("MAGENTA");
+ break;
+ case 36:
+ style = style.setForground("CYAN");
+ break;
+ case 37:
+ style = style.setForground("WHITE_FOREGROUND");
+ break;
+ case 40:
+ style = style.setBackground("BLACK");
+ break;
+ case 41:
+ style = style.setBackground("RED");
+ break;
+ case 42:
+ style = style.setBackground("GREEN");
+ break;
+ case 43:
+ style = style.setBackground("YELLOW");
+ break;
+ case 44:
+ style = style.setBackground("BLUE");
+ break;
+ case 45:
+ style = style.setBackground("MAGENTA");
+ break;
+ case 46:
+ style = style.setBackground("CYAN");
+ break;
+ case 47:
+ style = style.setBackground("WHITE");
+ break;
+ default:
+ Logger.log("Unsupported graphics rendition parameter: " + parameter);
+ break;
+ }
+ ++parameterIndex;
+ }
+ text.setStyle(style);
+ }
+
+ // Responds to an ANSI Device Status Report (DSR) command from the remote endpoint requesting the cursor position.
+ // Requests for other kinds of status are ignored.
+ private void processAnsiCommand_n() {
+ // Do nothing if the numeric parameter was not 6 (which means report cursor position).
+ if (getAnsiParameter(0) != 6) {
+ return;
+ }
+ // Send the ANSI cursor position (which is 1-based) to the remote endpoint.
+ String positionReport = "\u001b[" + (relativeCursorLine() + 1) + ";" + (getCursorColumn() + 1) + "R";
+ try {
+ // TODO(alruiz): use same encoding as terminal.
+ OutputStreamWriter streamWriter = new OutputStreamWriter(terminal.getOutputStream(), "ISO-8859-1");
+ streamWriter.write(positionReport, 0, positionReport.length());
+ streamWriter.flush();
+ } catch (IOException ex) {
+ Logger.log("Caught IOException!");
+ }
+ }
+
+ // Deletes one or more characters starting at the current cursor position. Characters on the same line and to the
+ // right of the deleted characters move left. If there are no characters on the current line at or to the right of the
+ // cursor column, no text is deleted.
+ private void processAnsiCommand_P() {
+ text.deleteCharacters(getAnsiParameter(0));
+ }
+
+ // Returns one of the numeric ANSI parameters received in the most recent escape sequence.
+ private int getAnsiParameter(int parameterIndex) {
+ if (parameterIndex < 0 || parameterIndex >= parameters.length) {
+ // This should never happen.
+ return -1;
+ }
+ String parameter = parameters[parameterIndex].toString();
+ if (parameter.length() == 0) {
+ return 1;
+ }
+ int parameterValue = 1;
+ // Don't trust the remote endpoint to send well formed numeric parameters.
+ try {
+ parameterValue = Integer.parseInt(parameter);
+ } catch (NumberFormatException ex) {
+ parameterValue = 1;
+ }
+ return parameterValue;
+ }
+
+ // Processes a single parameter character in an ANSI escape sequence. Parameters are the (optional) characters
+ // between the leading "\e[" and the command character in an escape sequence (e.g., in the escape sequence
+ // "\e[20;10H", the parameter characters are "20;10"). Parameters are integers separated by one or more ';'s.
+ private void processAnsiParameterCharacter(char ch) {
+ if (ch == ';') {
+ ++nextAnsiParameter;
+ } else {
+ if (nextAnsiParameter < parameters.length) {
+ parameters[nextAnsiParameter].append(ch);
+ }
+ }
+ }
+
+ // Processes a contiguous sequence of non-control characters. This is a performance optimization, so that we don't
+ // have to insert or append each non-control character individually to the StyledText widget. A non-control character
+ // is any character that passes the condition in the below while loop.
+ private void processNonControlCharacters(char character) throws IOException {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(character);
+ // Identify a contiguous sequence of non-control characters, starting at firstNonControlCharacterIndex in newText.
+ while (hasNextChar()) {
+ character = getNextChar();
+ if (character == '\u0000' || character == '\b' || character == '\t' || character == '\u0007' || character == '\n'
+ || character == '\r' || character == '\u001b') {
+ pushBackChar(character);
+ break;
+ }
+ buffer.append(character);
+ }
+ // Now insert the sequence of non-control characters in the StyledText widget
+ // at the location of the cursor.
+ displayNewText(buffer.toString());
+ }
+
+ // Displays a subset of the newly-received text in the Terminal view, wrapping text at the right edge of the screen
+ // and overwriting text when the cursor is not at the very end of the screen's text.
+ // There are never any ANSI control characters or escape sequences in the text being displayed by this method (this
+ // includes newlines, carriage returns, and tabs).
+ private void displayNewText(String buffer) {
+ text.appendString(buffer);
+ }
+
+ // Processes a BEL (Control-G) character.
+ private void processBEL() {
+ // TODO
+ // Display.getDefault().beep();
+ }
+
+ // Processes a backspace (Control-H) character.
+ private void processBackspace() {
+ moveCursorBackward(1);
+ }
+
+ // Processes a tab (Control-I) character. We don't insert a tab character into the StyledText widget. Instead, we move
+ // the cursor forward to the next tab stop, without altering any of the text. Tab stops are every 8 columns. The
+ // cursor will never move past the rightmost column.
+ private void processTab() {
+ moveCursorForward(8 - (getCursorColumn() % 8));
+ }
+
+ // Processes a newline (Control-J) character. A newline (NL) character just moves the cursor to the same column on the
+ // next line, creating new lines when the cursor reaches the bottom edge of the terminal. This is counter-intuitive,
+ // especially to UNIX programmers who are taught that writing a single NL to a terminal is sufficient to move the
+ // cursor to the first column of the next line, as if a carriage return (CR) and a NL were written.
+ //
+ // UNIX terminals typically display a NL character as a CR followed by a NL because the terminal device typically has
+ // the ONLCR attribute bit set (see the termios(4) man page for details), which causes the terminal device driver to
+ // translate NL to CR + NL on output. The terminal itself (i.e., a hardware terminal or a terminal emulator, like
+ // xterm or this code) _always_ interprets a CR to mean "move the cursor to the beginning of the current
+ // line" and a NL to mean "move the cursor to the same column on the next line".
+ private void processNewline() {
+ text.processNewline();
+ }
+
+ // Processes a Carriage Return (Control-M).
+ private void processCarriageReturn() {
+ text.setCursorColumn(0);
+ }
+
+ // Computes the width of the terminal in columns and its height in lines, then adjusts the width and height of the
+ // view's StyledText widget so that it displays an integral number of lines and columns of text. The adjustment is
+ // always to shrink the widget vertically or horizontally, because if the control were to grow, it would be clipped by
+ // the edges of the view window (i.e., the view window does not become larger to accommodate its contents becoming
+ // larger).
+ //
+ // This method must be called immediately before each time text is written to the terminal so that we can properly
+ // line wrap text. Because it is called so frequently, it must be fast when there is no resizing to be done.
+ private void adjustTerminalDimensions() {
+ // Compute how many pixels we need to shrink the StyledText control vertically to make it display an integral number
+ // of lines of text.
+ //
+ // TODO
+ // if(text.getColumns()!=80 && text.getLines()!=80) text.setDimensions(24, 80);
+ ITerminalConnector connector = getConnector();
+ // TODO MSA: send only if dimensions have really changed!
+ if (connector != null) {
+ connector.setTerminalSize(text.getColumns(), text.getLines());
+ }
+ }
+
+ private ITerminalConnector getConnector() {
+ if (terminal.getTerminalConnector() != null) {
+ return terminal.getTerminalConnector();
+ }
+ return null;
+ }
+
+ // Returns the relative line number of the line containing the cursor. The returned line number is relative to the
+ // top-most visible line, which has relative line number 0.
+ private int relativeCursorLine() {
+ return text.getCursorLine();
+ }
+
+ // Moves the cursor to the specified line and column. Parameter <i>targetLine</i> is the line number of a screen line,
+ // so it has a minimum value of 0 (the topmost screen line) and a maximum value of heightInLines - 1 (the
+ // bottom-most screen line). A line does not have to contain any text to move the cursor to any column in that line.
+ private void moveCursor(int targetLine, int targetColumn) {
+ text.setCursor(targetLine, targetColumn);
+ }
+
+ // Moves the cursor down n lines, but won't move the cursor past the bottom of the screen. This method does not cause
+ // any scrolling.
+ private void moveCursorDown(int lines) {
+ moveCursor(relativeCursorLine() + lines, getCursorColumn());
+ }
+
+ // Moves the cursor up n lines, but won't move the cursor past the top of the screen. This method does not cause any
+ // scrolling.
+ private void moveCursorUp(int lines) {
+ moveCursor(relativeCursorLine() - lines, getCursorColumn());
+ }
+
+ // Method moves the cursor forward n columns, but won't move the cursor past the right edge of the screen, nor will
+ // it move the cursor onto the next line. This method does not cause any scrolling.
+ private void moveCursorForward(int columnsToMove) {
+ moveCursor(relativeCursorLine(), getCursorColumn() + columnsToMove);
+ }
+
+ // Moves the cursor backward n columns, but won't move the cursor past the left edge of the screen, nor will it move
+ // the cursor onto the previous line. This method does not cause any scrolling.
+ private void moveCursorBackward(int columnsToMove) {
+ moveCursor(relativeCursorLine(), getCursorColumn() - columnsToMove);
+ }
+
+ /**
+ * Resets the state of the terminal text (foreground color, background color, font style and other internal state). It
+ * essentially makes it ready for new input.
+ */
+ public void resetState() {
+ ansiState = ANSISTATE_INITIAL;
+ text.setStyle(text.getDefaultStyle());
+ }
+
+ private char getNextChar() throws IOException {
+ int c = -1;
+ if (nextChar != -1) {
+ c = nextChar;
+ nextChar = -1;
+ } else {
+ c = reader.read();
+ }
+ // TODO: better end of file handling
+ if (c == -1) {
+ c = 0;
+ }
+ return (char) c;
+ }
+
+ private boolean hasNextChar() throws IOException {
+ if (nextChar >= 0) {
+ return true;
+ }
+ return reader.ready();
+ }
+
+ /**
+ * Put back one character to the stream. This method can push back exactly one character. The character is the next
+ * character returned by {@link #getNextChar}.
+ *
+ * @param c the character to be pushed back.
+ */
+ private void pushBackChar(char c) {
+ nextChar = c;
+ }
+
+ private int getCursorColumn() {
+ return text.getCursorColumn();
+ }
+
+ public boolean isCrAfterNewLine() {
+ return crAfterNewLine;
+ }
+
+ public void setCrAfterNewLine(boolean crAfterNewLine) {
+ this.crAfterNewLine = crAfterNewLine;
+ }
+
+ public List<IHyperlink> hyperlinksAt(int line) {
+ return text.hyperlinksAt(line);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100EmulatorBackend.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100EmulatorBackend.java
new file mode 100644
index 0000000..4d56c59
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100EmulatorBackend.java
@@ -0,0 +1,368 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.core;
+
+import static java.util.Collections.emptyList;
+
+import java.util.*;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+
+import com.google.eclipse.elt.emulator.hyperlink.*;
+import com.google.eclipse.elt.emulator.model.*;
+
+public class VT100EmulatorBackend implements IVT100EmulatorBackend {
+ // This field holds the number of the column in which the cursor is logically positioned. The left-most column on the
+ // screen is column 0, and column numbers increase to the right. The maximum value of this field is
+ // widthInColumns - 1. We track the cursor column using this field to avoid having to recompute it repeatly using
+ // StyledText method calls.
+ //
+ // The StyledText widget that displays text has a vertical bar (called the "caret") that appears _between_ character
+ // cells, but ANSI terminals have the concept of a cursor that appears _in_ a character cell, so we need a convention
+ // for which character cell the cursor logically occupies when the caret is physically between two cells. The
+ // convention used in this class is that the cursor is logically in column N when the caret is physically positioned
+ // immediately to the _left_ of column N.
+ //
+ // When cursorColumn is N, the next character output to the terminal appears in column N. When a character is output
+ // to the rightmost column on a given line (column widthInColumns - 1), the cursor moves to column 0 on the next line
+ // after the character is drawn (this is how line wrapping is implemented). If the cursor is in the bottom-most line
+ // when line wrapping occurs, the topmost visible line is scrolled off the top edge of the screen.
+ private int cursorColumn;
+
+ private int cursorLine;
+ private Style defaultStyle;
+ private Style style;
+ private int lines;
+ private int columns;
+
+ private final ITerminalTextData terminal;
+
+ private final IHyperlinkFactory httpHyperlinkFactory = new HttpHyperlinkFactory();
+ private final Map<Integer, List<IHyperlink>> hyperlinks = new HashMap<Integer, List<IHyperlink>>();
+
+ public VT100EmulatorBackend(ITerminalTextData terminal) {
+ this.terminal = terminal;
+ }
+
+ @Override public void clearAll() {
+ synchronized (terminal) {
+ // clear the history
+ int n = terminal.getHeight();
+ for (int line = 0; line < n; line++) {
+ terminal.cleanLine(line);
+ }
+ terminal.setDimensions(lines, terminal.getWidth());
+ setStyle(getDefaultStyle());
+ setCursor(0, 0);
+ }
+ }
+
+ @Override public void setDimensions(int lines, int columns) {
+ synchronized (terminal) {
+ if (lines == this.lines && columns == this.columns) {
+ return; // nothing to do
+ }
+ // relative cursor line
+ int cursorLine = getCursorLine();
+ int cursorColumn = getCursorColumn();
+ int height = terminal.getHeight();
+ // absolute cursor line
+ int absoluteCursorLine = cursorLine + height - this.lines;
+ int newLines = Math.max(lines, height);
+ if (lines < this.lines) {
+ if (height == this.lines) {
+ // if the terminal has no history, then resize by setting the size to the new size.
+ // TODO We are assuming that cursor line points at end of text.
+ newLines = Math.max(lines, cursorLine + 1);
+ }
+ }
+ this.lines = lines;
+ this.columns = columns;
+ // Make the terminal at least as high as we need lines.
+ terminal.setDimensions(newLines, this.columns);
+ // Compute relative cursor line.
+ cursorLine = absoluteCursorLine - (newLines - this.lines);
+ setCursor(cursorLine, cursorColumn);
+ }
+ }
+
+ int toAbsoluteLine(int line) {
+ synchronized (terminal) {
+ return terminal.getHeight() - this.lines + line;
+ }
+ }
+
+ @Override public void insertCharacters(int charactersToInsert) {
+ synchronized (terminal) {
+ int line = toAbsoluteLine(cursorLine);
+ int n = charactersToInsert;
+ for (int column = columns - 1; column >= cursorColumn + n; column--) {
+ char c = terminal.getChar(line, column - n);
+ Style style = terminal.getStyle(line, column - n);
+ terminal.setChar(line, column, c, style);
+ }
+ int last = Math.min(cursorColumn + n, columns);
+ for (int col = cursorColumn; col < last; col++) {
+ terminal.setChar(line, col, '\000', null);
+ }
+ }
+ }
+
+ @Override public void eraseToEndOfScreen() {
+ synchronized (terminal) {
+ eraseLineToEnd();
+ for (int line = toAbsoluteLine(cursorLine + 1); line < toAbsoluteLine(lines); line++) {
+ terminal.cleanLine(line);
+ }
+ }
+ }
+
+ @Override public void eraseToCursor() {
+ synchronized (terminal) {
+ for (int line = toAbsoluteLine(0); line < toAbsoluteLine(cursorLine); line++) {
+ terminal.cleanLine(line);
+ }
+ eraseLineToCursor();
+ }
+ }
+
+ @Override public void eraseAll() {
+ synchronized (terminal) {
+ for (int line = toAbsoluteLine(0); line < toAbsoluteLine(lines); line++) {
+ terminal.cleanLine(line);
+ }
+ }
+ }
+
+ @Override public void eraseLine() {
+ synchronized (terminal) {
+ terminal.cleanLine(toAbsoluteLine(cursorLine));
+ }
+ }
+
+ @Override public void eraseLineToEnd() {
+ synchronized (terminal) {
+ int line = toAbsoluteLine(cursorLine);
+ for (int col = cursorColumn; col < columns; col++) {
+ terminal.setChar(line, col, '\000', null);
+ }
+ }
+ }
+
+ @Override public void eraseLineToCursor() {
+ synchronized (terminal) {
+ int line = toAbsoluteLine(cursorLine);
+ for (int col = 0; col <= cursorColumn; col++) {
+ terminal.setChar(line, col, '\000', null);
+ }
+ }
+ }
+
+ @Override public void insertLines(int lineCount) {
+ synchronized (terminal) {
+ if (!isCursorInScrollingRegion()) {
+ return;
+ }
+ Assert.isTrue(lineCount > 0);
+ int line = toAbsoluteLine(cursorLine);
+ int nLines = terminal.getHeight() - line;
+ terminal.scroll(line, nLines, lineCount);
+ }
+ }
+
+ @Override public void deleteCharacters(int charCount) {
+ synchronized (terminal) {
+ int line = toAbsoluteLine(cursorLine);
+ for (int col = cursorColumn + charCount; col < columns; col++) {
+ char c = terminal.getChar(line, col);
+ Style style = terminal.getStyle(line, col);
+ terminal.setChar(line, col - charCount, c, style);
+ }
+ int first = Math.max(cursorColumn, columns - charCount);
+ for (int col = first; col < columns; col++) {
+ terminal.setChar(line, col, '\000', null);
+ }
+ }
+ }
+
+ @Override public void deleteLines(int lineCount) {
+ synchronized (terminal) {
+ if (!isCursorInScrollingRegion()) {
+ return;
+ }
+ Assert.isTrue(lineCount > 0);
+ int line = toAbsoluteLine(cursorLine);
+ int currentLineCount = terminal.getHeight() - line;
+ terminal.scroll(line, currentLineCount, -lineCount);
+ }
+ }
+
+ private boolean isCursorInScrollingRegion() {
+ return true;
+ }
+
+ @Override public Style getDefaultStyle() {
+ synchronized (terminal) {
+ return defaultStyle;
+ }
+ }
+
+ @Override public void setDefaultStyle(Style defaultStyle) {
+ synchronized (terminal) {
+ this.defaultStyle = defaultStyle;
+ }
+ }
+
+ @Override public Style getStyle() {
+ synchronized (terminal) {
+ if (style == null) {
+ return defaultStyle;
+ }
+ return style;
+ }
+ }
+
+ @Override public void setStyle(Style style) {
+ synchronized (terminal) {
+ this.style = style;
+ }
+ }
+
+ @Override public void appendString(String buffer) {
+ synchronized (terminal) {
+ char[] chars = buffer.toCharArray();
+ int line = toAbsoluteLine(cursorLine);
+ int originalLine = line;
+ List<IHyperlink> found = emptyList();
+ if (buffer != null) {
+ found = httpHyperlinkFactory.hyperlinksIn(cursorColumn, buffer);
+ hyperlinks.put(new Integer(line), found);
+ }
+ int i = 0;
+ while (i < chars.length) {
+ int n = Math.min(columns - cursorColumn, chars.length - i);
+ terminal.setChars(line, cursorColumn, chars, i, n, style);
+ int col = cursorColumn + n;
+ i += n;
+ // wrap needed?
+ if (col >= columns) {
+ doNewline();
+ line = toAbsoluteLine(cursorLine);
+ setCursorColumn(0);
+ } else {
+ setCursorColumn(col);
+ }
+ }
+ drawHyperlinks(found, originalLine);
+ }
+ }
+
+ private void drawHyperlinks(List<IHyperlink> hyperlinks, int line) {
+ for (IHyperlink hyperlink : hyperlinks) {
+ IRegion region = hyperlink.getHyperlinkRegion();
+ int start = region.getOffset();
+ int end = start + region.getLength();
+ for (int column = start; column < end; column++) {
+ Style style = terminal.getStyle(line, column);
+ if (style != null) {
+ style = style.setUnderline(true);
+ terminal.setChar(line, column, terminal.getChar(line, column), style);
+ }
+ }
+ }
+ }
+
+ // MUST be called from a synchronized block!
+ private void doNewline() {
+ if (cursorLine + 1 >= lines) {
+ int h = terminal.getHeight();
+ terminal.addLine();
+ if (h != terminal.getHeight()) {
+ setCursorLine(cursorLine + 1);
+ }
+ } else {
+ setCursorLine(cursorLine + 1);
+ }
+ }
+
+ @Override public void processNewline() {
+ synchronized (terminal) {
+ doNewline();
+ }
+ }
+
+ @Override public int getCursorLine() {
+ synchronized (terminal) {
+ return cursorLine;
+ }
+ }
+
+ @Override public int getCursorColumn() {
+ synchronized (terminal) {
+ return cursorColumn;
+ }
+ }
+
+ @Override public void setCursor(int targetLine, int targetColumn) {
+ synchronized (terminal) {
+ setCursorLine(targetLine);
+ setCursorColumn(targetColumn);
+ }
+ }
+
+ @Override public void setCursorColumn(int targetColumn) {
+ synchronized (terminal) {
+ if (targetColumn < 0) {
+ targetColumn = 0;
+ } else if (targetColumn >= columns) {
+ targetColumn = columns - 1;
+ }
+ cursorColumn = targetColumn;
+ // We make the assumption that nobody is changing the
+ // terminal cursor except this class!
+ // This assumption gives a huge performance improvement
+ terminal.setCursorColumn(targetColumn);
+ }
+ }
+
+ @Override public void setCursorLine(int targetLine) {
+ synchronized (terminal) {
+ if (targetLine < 0) {
+ targetLine = 0;
+ } else if (targetLine >= lines) {
+ targetLine = lines - 1;
+ }
+ cursorLine = targetLine;
+ // We make the assumption that nobody is changing the terminal cursor except this class!
+ // This assumption gives a huge performance improvement.
+ terminal.setCursorLine(toAbsoluteLine(targetLine));
+ }
+ }
+
+ @Override public int getLines() {
+ synchronized (terminal) {
+ return lines;
+ }
+ }
+
+ @Override public int getColumns() {
+ synchronized (terminal) {
+ return columns;
+ }
+ }
+
+ @Override public List<IHyperlink> hyperlinksAt(int line) {
+ List<IHyperlink> found = hyperlinks.get(new Integer(line));
+ if (found == null) {
+ return emptyList();
+ }
+ return found;
+ }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100TerminalControl.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100TerminalControl.java
new file mode 100644
index 0000000..5fa669f
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/core/VT100TerminalControl.java
@@ -0,0 +1,843 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.core;
+
+import static org.eclipse.core.runtime.Status.OK_STATUS;
+import static org.eclipse.jface.bindings.keys.SWTKeySupport.convertEventToUnmodifiedAccelerator;
+
+import static com.google.eclipse.elt.emulator.impl.TerminalPlugin.isOptionEnabled;
+
+import java.io.*;
+import java.net.SocketException;
+import java.util.List;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.contexts.*;
+import org.eclipse.ui.keys.IBindingService;
+
+import com.google.eclipse.elt.emulator.control.*;
+import com.google.eclipse.elt.emulator.impl.*;
+import com.google.eclipse.elt.emulator.model.*;
+import com.google.eclipse.elt.emulator.provisional.api.*;
+import com.google.eclipse.elt.emulator.textcanvas.*;
+import com.google.eclipse.elt.emulator.textcanvas.PipedInputStream;
+
+public class VT100TerminalControl implements ITerminalControlForText, ITerminalControl, ITerminalViewControl {
+ protected static final String[] LINE_DELIMITERS = { "\n" };
+
+ private static final String DEFAULT_ENCODING =
+ new InputStreamReader(new ByteArrayInputStream(new byte[0])).getEncoding();
+
+ // This field holds a reference to a TerminalText object that performs all ANSI text processing on data received from
+ // the remote host and controls how text is displayed using the view's StyledText widget.
+ private final VT100Emulator terminalText;
+
+ private Display display;
+ private TextCanvas textControl;
+ private Composite rootControl;
+ private Clipboard clipboard;
+ private KeyListener keyHandler;
+ private final ITerminalListener terminalListener;
+ private String message = "";
+ private FocusListener fFocusListener;
+ private ITerminalConnector connector;
+ private final ITerminalConnector[] connectors;
+ private final PipedInputStream inputStream;
+ private String encoding = DEFAULT_ENCODING;
+ private InputStreamReader inputStreamReader;
+ private ICommandInputField commandInputField;
+ private volatile TerminalState state;
+ private final ITerminalTextData terminalModel;
+
+ volatile private Job job;
+
+ private final EditActionAccelerators editActionAccelerators = new EditActionAccelerators();
+
+ public VT100TerminalControl(ITerminalListener target, Composite wndParent, ITerminalConnector[] connectors) {
+ this.connectors = connectors;
+ terminalListener = target;
+ terminalModel = TerminalTextDataFactory.makeTerminalTextData();
+ terminalModel.setMaxHeight(1000);
+ inputStream = new PipedInputStream(8 * 1024);
+ terminalText = new VT100Emulator(terminalModel, this, null);
+ try {
+ // Use default Encoding as start, until setEncoding() is called.
+ setEncoding(null);
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ // Fall back to local platform default encoding
+ encoding = DEFAULT_ENCODING;
+ inputStreamReader = new InputStreamReader(inputStream);
+ terminalText.setInputStreamReader(inputStreamReader);
+ }
+ setUpTerminal(wndParent);
+ }
+
+ @Override public void setEncoding(String encoding) throws UnsupportedEncodingException {
+ if (encoding == null) {
+ encoding = "ISO-8859-1";
+ }
+ inputStreamReader = new InputStreamReader(inputStream, encoding);
+ // remember encoding if above didn't throw an exception
+ this.encoding = encoding;
+ terminalText.setInputStreamReader(inputStreamReader);
+ }
+
+ @Override public String getEncoding() {
+ return encoding;
+ }
+
+ @Override public ITerminalConnector[] getConnectors() {
+ return connectors;
+ }
+
+ @Override public void copy() {
+ copy(DND.CLIPBOARD);
+ }
+
+ private void copy(int clipboardType) {
+ String selection = getSelection();
+ if (selection == null || selection.isEmpty()) {
+ return;
+ }
+ Object[] data = new Object[] { selection };
+ Transfer[] types = new Transfer[] { TextTransfer.getInstance() };
+ clipboard.setContents(data, types, clipboardType);
+ }
+
+ @Override public void paste() {
+ paste(DND.CLIPBOARD);
+ }
+
+ private void paste(int clipboardType) {
+ TextTransfer textTransfer = TextTransfer.getInstance();
+ String strText = (String) clipboard.getContents(textTransfer, clipboardType);
+ pasteString(strText);
+ }
+
+ @Override public boolean pasteString(String text) {
+ if (!isConnected()) {
+ return false;
+ }
+ if (text == null) {
+ return false;
+ }
+ if (!encoding.equals(DEFAULT_ENCODING)) {
+ sendString(text);
+ } else {
+ // TODO I do not understand why pasteString would do this here...
+ for (int i = 0; i < text.length(); i++) {
+ sendChar(text.charAt(i), false);
+ }
+ }
+ return true;
+ }
+
+ @Override public void selectAll() {
+ getTextControl().selectAll();
+ }
+
+ @Override public void sendKey(char character) {
+ Event event;
+ KeyEvent keyEvent;
+ event = new Event();
+ event.widget = getTextControl();
+ event.character = character;
+ event.keyCode = 0;
+ event.stateMask = 0;
+ event.doit = true;
+ keyEvent = new KeyEvent(event);
+ keyHandler.keyPressed(keyEvent);
+ }
+
+ @Override public void clearTerminal() {
+ getTerminalText().clearTerminal();
+ }
+
+ @Override public Clipboard getClipboard() {
+ return clipboard;
+ }
+
+ @Override public String getSelection() {
+ String text = textControl.getSelectionText();
+ return text == null ? "" : text;
+ }
+
+ @Override public boolean setFocus() {
+ return getTextControl().setFocus();
+ }
+
+ @Override public boolean isEmpty() {
+ return getTextControl().isEmpty();
+ }
+
+ @Override public boolean isDisposed() {
+ return getTextControl().isDisposed();
+ }
+
+ @Override public boolean isConnected() {
+ return state == TerminalState.CONNECTED;
+ }
+
+ @Override public void disposeTerminal() {
+ disconnectTerminal();
+ clipboard.dispose();
+ getTerminalText().dispose();
+ }
+
+ @Override public void connectTerminal() {
+ if (getTerminalConnector() == null) {
+ return;
+ }
+ terminalText.resetState();
+ if (connector.getInitializationErrorMessage() != null) {
+ showErrorMessage(NLS.bind(TerminalMessages.cannotConnectTo, connector.getName(),
+ connector.getInitializationErrorMessage()));
+ return;
+ }
+ getTerminalConnector().connect(this);
+ // clean the error message
+ setErrorMessage("");
+ waitForConnect();
+ }
+
+ @Override public ITerminalConnector getTerminalConnector() {
+ return connector;
+ }
+
+ @Override public void disconnectTerminal() {
+ Logger.log("entered.");
+ // Disconnect the remote side first.
+ if (getState() != TerminalState.CLOSED && getTerminalConnector() != null) {
+ getTerminalConnector().disconnect();
+ }
+ // Ensure that a new Job can be started; then clean up old Job.
+ Job newJob;
+ synchronized (this) {
+ newJob = job;
+ job = null;
+ }
+ if (newJob != null) {
+ newJob.cancel();
+ // Join job to avoid leaving job running after workbench shutdown (333613).
+ // Interrupt to be fast enough; cannot close fInputStream since it is re-used (bug 348700).
+ Thread t = newJob.getThread();
+ if (t != null) {
+ t.interrupt();
+ }
+ try {
+ newJob.join();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ private void waitForConnect() {
+ // TODO
+ // Eliminate this code
+ while (getState() == TerminalState.CONNECTING) {
+ if (display.readAndDispatch()) {
+ continue;
+ }
+ display.sleep();
+ }
+ if (getTextControl().isDisposed()) {
+ disconnectTerminal();
+ return;
+ }
+ if (!getMsg().isEmpty()) {
+ showErrorMessage(getMsg());
+ disconnectTerminal();
+ return;
+ }
+ getTextControl().setFocus();
+ startReaderJob();
+ }
+
+ private synchronized void startReaderJob() {
+ if (job == null) {
+ initializeJob();
+ job.setSystem(true);
+ job.schedule();
+ }
+ }
+
+ private void initializeJob() {
+ job = new Job("Terminal data reader") {
+ @Override protected IStatus run(IProgressMonitor monitor) {
+ IStatus status = OK_STATUS;
+ try {
+ while (true) {
+ while (inputStream.available() == 0 && !monitor.isCanceled()) {
+ try {
+ inputStream.waitForAvailable(500);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ if (monitor.isCanceled()) {
+ // Do not disconnect terminal here because another reader job may already be running.
+ status = Status.CANCEL_STATUS;
+ break;
+ }
+ try {
+ // TODO: should block when no text is available!
+ terminalText.processText();
+ } catch (Exception e) {
+ disconnectTerminal();
+ status = new Status(IStatus.ERROR, TerminalPlugin.PLUGIN_ID, e.getLocalizedMessage(), e);
+ break;
+ }
+ }
+ } finally {
+ // Clean the job: start a new one when the connection gets restarted.
+ // Bug 208145: make sure we do not clean an other job that's already started (since it would become a zombie)
+ synchronized (VT100TerminalControl.this) {
+ if (job == this) {
+ job = null;
+ }
+ }
+ }
+ return status;
+ }
+ };
+ }
+
+ private void showErrorMessage(String message) {
+ String title = TerminalMessages.terminalError;
+ MessageBox mb = new MessageBox(getShell(), SWT.ICON_ERROR | SWT.OK);
+ mb.setText(title);
+ mb.setMessage(message);
+ mb.open();
+ }
+
+ protected void sendString(String string) {
+ try {
+ // Send the string after converting it to an array of bytes using the platform's default character encoding.
+ //
+ // TODO: Find a way to force this to use the ISO Latin-1 encoding.
+ // TODO: handle Encoding Errors in a better way
+ getOutputStream().write(string.getBytes(encoding));
+ getOutputStream().flush();
+ } catch (SocketException socketException) {
+ displayTextInTerminal(socketException.getMessage());
+ String strMsg = TerminalMessages.socketError + "!\n" + socketException.getMessage();
+ showErrorMessage(strMsg);
+ Logger.logException(socketException);
+ disconnectTerminal();
+ } catch (IOException ioException) {
+ showErrorMessage(TerminalMessages.ioError + "!\n" + ioException.getMessage());
+ Logger.logException(ioException);
+ disconnectTerminal();
+ }
+ }
+
+ @Override public Shell getShell() {
+ return getTextControl().getShell();
+ }
+
+ protected void sendChar(char chKey, boolean altKeyPressed) {
+ try {
+ String text = Character.toString(chKey);
+ byte[] bytes = text.getBytes(getEncoding());
+ OutputStream os = getOutputStream();
+ if (os == null) {
+ // Bug 207785: NPE when trying to send char while no longer connected
+ Logger.log("NOT sending '" + text + "' because no longer connected");
+ } else {
+ if (altKeyPressed) {
+ // When the ALT key is pressed at the same time that a character is typed, translate it into an ESCAPE
+ // followed by the character. The alternative in this case is to set the high bit of the character being
+ // transmitted, but that will cause input such as ALT-f to be seen as the ISO Latin-1 character '�', which
+ // can be confusing to European users running Emacs, for whom Alt-f should move forward a word instead of
+ // inserting the '�' character.
+ //
+ // TODO: Make the ESCAPE-vs-highbit behavior user configurable.
+ Logger.log("sending ESC + '" + text + "'");
+ getOutputStream().write('\u001b');
+ getOutputStream().write(bytes);
+ } else {
+ Logger.log("sending '" + text + "'");
+ getOutputStream().write(bytes);
+ }
+ getOutputStream().flush();
+ }
+ } catch (SocketException socketException) {
+ Logger.logException(socketException);
+ displayTextInTerminal(socketException.getMessage());
+ String message = TerminalMessages.socketError + "!\n" + socketException.getMessage();
+ showErrorMessage(message);
+ Logger.logException(socketException);
+ disconnectTerminal();
+ } catch (IOException ioException) {
+ Logger.logException(ioException);
+ displayTextInTerminal(ioException.getMessage());
+ String message = TerminalMessages.ioError + "!\n" + ioException.getMessage();
+ showErrorMessage(message);
+ Logger.logException(ioException);
+ disconnectTerminal();
+ }
+ }
+
+ @Override public void setUpTerminal(Composite parent) {
+ Assert.isNotNull(parent);
+ state = TerminalState.CLOSED;
+ setUpControls(parent);
+ setupListeners();
+ }
+
+ @Override public void setFont(Font font) {
+ getTextControl().setFont(font);
+ if (commandInputField != null) {
+ commandInputField.setFont(font);
+ }
+ // Tell the TerminalControl singleton that the font has changed.
+ textControl.onFontChange();
+ getTerminalText().fontChanged();
+ }
+
+ @Override public Font getFont() {
+ return getTextControl().getFont();
+ }
+
+ @Override public Control getControl() {
+ return textControl;
+ }
+
+ @Override public Control getRootControl() {
+ return rootControl;
+ }
+
+ protected void setUpControls(Composite parent) {
+ // The Terminal view now aims to be an ANSI-conforming terminal emulator, so it can't have a horizontal scroll bar
+ // (but a vertical one is ok). Also, do _not_ make the TextViewer read-only, because that prevents it from seeing a
+ // TAB character when the user presses TAB (instead, the TAB causes focus to switch to another Workbench control).
+ // We prevent local keyboard input from modifying the text in method TerminalVerifyKeyListener.verifyKey().
+ rootControl = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ layout.verticalSpacing = 0;
+ rootControl.setLayout(layout);
+ ITerminalTextDataSnapshot snapshot = terminalModel.makeSnapshot();
+ // TODO how to get the initial size correctly!
+ snapshot.updateSnapshot(false);
+ ITextCanvasModel canvasModel = new PollingTextCanvasModel(snapshot);
+ textControl = new TextCanvas(rootControl, canvasModel, SWT.NONE, new TextLineRenderer(textControl, canvasModel));
+ textControl.addMouseListener(new MouseAdapter() {
+ @Override public void mouseUp(MouseEvent e) {
+ IHyperlink hyperlink = hyperlinkAt(e);
+ if (hyperlink != null) {
+ hyperlink.open();
+ }
+ }
+ });
+ textControl.addMouseMoveListener(new MouseMoveListener() {
+ @Override public void mouseMove(MouseEvent e) {
+ IHyperlink hyperlink = hyperlinkAt(e);
+ int cursorId = (hyperlink == null) ? SWT.CURSOR_IBEAM : SWT.CURSOR_HAND;
+ Cursor newCursor = textControl.getDisplay().getSystemCursor(cursorId);
+ if (!newCursor.equals(textControl.getCursor())) {
+ textControl.setCursor(newCursor);
+ }
+ }
+ });
+ textControl.setLayoutData(new GridData(GridData.FILL_BOTH));
+ textControl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ textControl.addResizeHandler(new TextCanvas.ResizeListener() {
+ @Override public void sizeChanged(int lines, int columns) {
+ terminalText.setDimensions(lines, columns);
+ }
+ });
+ textControl.addMouseListener(new MouseAdapter() {
+ @Override public void mouseUp(MouseEvent e) {
+ // update selection used by middle mouse button paste
+ if (e.button == 1 && getSelection().length() > 0) {
+ copy(DND.SELECTION_CLIPBOARD);
+ }
+ }
+ });
+ display = getTextControl().getDisplay();
+ clipboard = new Clipboard(display);
+ setFont(JFaceResources.getTextFont());
+ }
+
+ private IHyperlink hyperlinkAt(MouseEvent e) {
+ Point p = textControl.screenPointToCell(e.x, e.y);
+ if (p != null) {
+ List<IHyperlink> hyperlinks = terminalText.hyperlinksAt(p.y);
+ for (IHyperlink hyperlink : hyperlinks) {
+ IRegion region = hyperlink.getHyperlinkRegion();
+ int start = region.getOffset();
+ int end = start + region.getLength() - 1;
+ if (p.x >= start && p.x <= end) {
+ return hyperlink;
+ }
+ }
+ }
+ return null;
+ }
+
+ protected void setupListeners() {
+ keyHandler = new TerminalKeyHandler();
+ fFocusListener = new TerminalFocusListener();
+ getTextControl().addKeyListener(keyHandler);
+ getTextControl().addFocusListener(fFocusListener);
+ }
+
+ @Override public void displayTextInTerminal(String text) {
+ writeToTerminal("\r\n" + text + "\r\n");
+ }
+
+ private void writeToTerminal(String text) {
+ try {
+ getRemoteToTerminalOutputStream().write(text.getBytes(encoding));
+ } catch (UnsupportedEncodingException e) {
+ // should never happen!
+ e.printStackTrace();
+ } catch (IOException e) {
+ // should never happen!
+ e.printStackTrace();
+ }
+ }
+
+ @Override public OutputStream getRemoteToTerminalOutputStream() {
+ if (Logger.isLogEnabled()) {
+ return new LoggingOutputStream(inputStream.getOutputStream());
+ }
+ return inputStream.getOutputStream();
+ }
+
+ protected boolean isLogCharEnabled() {
+ return isOptionEnabled(Logger.TRACE_DEBUG_LOG_CHAR);
+ }
+
+ protected boolean isLogBufferSizeEnabled() {
+ return isOptionEnabled(Logger.TRACE_DEBUG_LOG_BUFFER_SIZE);
+ }
+
+ @Override public OutputStream getOutputStream() {
+ if (getTerminalConnector() != null) {
+ return getTerminalConnector().getTerminalToRemoteStream();
+ }
+ return null;
+ }
+
+ @Override public void setErrorMessage(String message) {
+ this.message = message;
+ }
+
+ public String getMsg() {
+ return message;
+ }
+
+ protected TextCanvas getTextControl() {
+ return textControl;
+ }
+
+ public VT100Emulator getTerminalText() {
+ return terminalText;
+ }
+
+ protected class TerminalFocusListener implements FocusListener {
+ private IContextActivation contextActivation = null;
+
+ @Override public void focusGained(FocusEvent event) {
+ // Disable all keyboard accelerators (e.g., Control-B) so the Terminal view can see every key stroke. Without
+ // this, Emacs, vi, and Bash are unusable in the terminal view.
+ IBindingService bindingService = (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class);
+ bindingService.setKeyFilterEnabled(false);
+ // The above code fails to cause Eclipse to disable menu-activation accelerators (e.g., Alt-F for the File menu),
+ // so we set the command context to be the Terminal view's command context. This enables us to override
+ // menu-activation accelerators with no-op commands in our plugin.xml file, which enables the terminal view to see
+ // absolutly _all_ key-presses.
+ IContextService contextService = (IContextService) PlatformUI.getWorkbench().getAdapter(IContextService.class);
+ contextActivation = contextService.activateContext("com.google.eclipse.elt.emulator.TerminalContext");
+ }
+
+ @Override public void focusLost(FocusEvent event) {
+ // Enable all key bindings.
+ IBindingService bindingService = (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class);
+ bindingService.setKeyFilterEnabled(true);
+ // Restore the command context to its previous value.
+ IContextService contextService = (IContextService) PlatformUI.getWorkbench().getAdapter(IContextService.class);
+ contextService.deactivateContext(contextActivation);
+ }
+ }
+
+ protected class TerminalKeyHandler extends KeyAdapter {
+ @Override public void keyPressed(KeyEvent event) {
+ if (getState() == TerminalState.CONNECTING) {
+ return;
+ }
+ int accelerator = convertEventToUnmodifiedAccelerator(event);
+ if (editActionAccelerators.isCopyAction(accelerator)) {
+ copy();
+ return;
+ }
+ if (editActionAccelerators.isPasteAction(accelerator)) {
+ paste();
+ return;
+ }
+ // We set the event.doit to false to prevent any further processing of this key event. The only reason this is
+ // here is because I was seeing the F10 key both send an escape sequence (due to this method) and switch focus to
+ // the Workbench File menu (forcing the user to click in the terminal view again to continue entering text). This
+ // fixes that.
+ event.doit = false;
+ char character = event.character;
+ if (state == TerminalState.CLOSED) {
+ // Pressing ENTER while not connected causes us to connect.
+ if (character == '\r') {
+ connectTerminal();
+ return;
+ }
+ // Ignore all other keyboard input when not connected.
+ // Allow other key handlers (such as Ctrl+F1) do their work.
+ event.doit = true;
+ return;
+ }
+ // Manage the Del key
+ if (event.keyCode == 0x000007f) {
+ sendString("\u001b[3~");
+ return;
+ }
+ // If the event character is NUL ('\u0000'), then a special key was pressed (e.g., PageUp, PageDown, an arrow key,
+ // a function key, Shift, Alt, Control, etc.). The one exception is when the user presses Control-@, which sends a
+ // NUL character, in which case we must send the NUL to the remote endpoint. This is necessary so that Emacs will
+ // work correctly, because Control-@ (i.e., NUL) invokes Emacs' set-mark-command when Emacs is running on a
+ // terminal. When the user presses Control-@, the keyCode is 50.
+ if (character == '\u0000' && event.keyCode != 50) {
+ // A special key was pressed. Figure out which one it was and send the appropriate ANSI escape sequence.
+ //
+ // IMPORTANT: Control will not enter this method for these special keys unless certain <keybinding> tags are
+ // present in the plugin.xml file for the Terminal view. Do not delete those tags.
+ switch (event.keyCode) {
+ case 0x1000001: // Up arrow.
+ sendString("\u001b[A");
+ break;
+ case 0x1000002: // Down arrow.
+ sendString("\u001b[B");
+ break;
+ case 0x1000003: // Left arrow.
+ sendString("\u001b[D");
+ break;
+ case 0x1000004: // Right arrow.
+ sendString("\u001b[C");
+ break;
+ case 0x1000005: // PgUp key.
+ sendString("\u001b[5~");
+ break;
+ case 0x1000006: // PgDn key.
+ sendString("\u001b[6~");
+ break;
+ case 0x1000007: // Home key.
+ sendString("\u001b[H");
+ break;
+ case 0x1000008: // End key.
+ sendString("\u001b[F");
+ break;
+ case 0x1000009: // Insert.
+ sendString("\u001b[2~");
+ break;
+ case 0x100000a: // F1 key.
+ if ((event.stateMask & SWT.CTRL) != 0) {
+ // Allow Ctrl+F1 to act locally as well as on the remote, because it is typically non-intrusive
+ event.doit = true;
+ }
+ sendString("\u001b[M");
+ break;
+ case 0x100000b: // F2 key.
+ sendString("\u001b[N");
+ break;
+ case 0x100000c: // F3 key.
+ sendString("\u001b[O");
+ break;
+ case 0x100000d: // F4 key.
+ sendString("\u001b[P");
+ break;
+ case 0x100000e: // F5 key.
+ sendString("\u001b[Q");
+ break;
+ case 0x100000f: // F6 key.
+ sendString("\u001b[R");
+ break;
+ case 0x1000010: // F7 key.
+ sendString("\u001b[S");
+ break;
+ case 0x1000011: // F8 key.
+ sendString("\u001b[T");
+ break;
+ case 0x1000012: // F9 key.
+ sendString("\u001b[U");
+ break;
+ case 0x1000013: // F10 key.
+ sendString("\u001b[V");
+ break;
+ case 0x1000014: // F11 key.
+ sendString("\u001b[W");
+ break;
+ case 0x1000015: // F12 key.
+ sendString("\u001b[X");
+ break;
+ default:
+ // Ignore other special keys. Control flows through this case when the user presses SHIFT, CONTROL, ALT, and
+ // any other key not handled by the above cases.
+ break;
+ }
+ // It's ok to return here, because we never locally echo special keys.
+ return;
+ }
+ // To fix SPR 110341, we consider the Alt key to be pressed only when the Control key is _not_ also pressed. This
+ // works around a bug in SWT where, on European keyboards, the AltGr key being pressed appears to us as Control
+ // + Alt being pressed simultaneously.
+ Logger.log("stateMask = " + event.stateMask);
+ boolean altKeyPressed = (((event.stateMask & SWT.ALT) != 0) && ((event.stateMask & SWT.CTRL) == 0));
+ if (!altKeyPressed && (event.stateMask & SWT.CTRL) != 0 && character == ' ') {
+ // Send a NUL character -- many terminal emulators send NUL when Control-Space is pressed. This is used to set
+ // the mark in Emacs.
+ character = '\u0000';
+ }
+ sendChar(character, altKeyPressed);
+ // Special case: When we are in a TCP connection and echoing characters locally, send a LF after sending a CR.
+ // ISSUE: Is this absolutely required?
+ if (character == '\r' && getTerminalConnector() != null && isConnected() && getTerminalConnector().isLocalEcho()) {
+ sendChar('\n', false);
+ }
+ // Now decide if we should locally echo the character we just sent. We do _not_ locally echo the character if any
+ // of these conditions are true:
+ //
+ // * This is a serial connection.
+ // * This is a TCP connection (i.e., m_telnetConnection is not null) and the remote endpoint is not a TELNET
+ // server.
+ // * The ALT (or META) key is pressed.
+ // * The character is any of the first 32 ISO Latin-1 characters except Control-I or Control-M.
+ // * The character is the DELETE character.
+ if (getTerminalConnector() == null || getTerminalConnector().isLocalEcho() == false || altKeyPressed
+ || (character >= '\u0001' && character < '\t') || (character > '\t' && character < '\r')
+ || (character > '\r' && character <= '\u001f') || character == '\u007f') {
+ // No local echoing.
+ return;
+ }
+ // Locally echo the character.
+ StringBuilder charBuffer = new StringBuilder();
+ charBuffer.append(character);
+ // If the character is a carriage return, we locally echo it as a CR + LF combination.
+ if (character == '\r') {
+ charBuffer.append('\n');
+ }
+ writeToTerminal(charBuffer.toString());
+ }
+ }
+
+ @Override public void setTerminalTitle(String title) {
+ terminalListener.setTerminalTitle(title);
+ }
+
+ @Override public TerminalState getState() {
+ return state;
+ }
+
+ @Override public void setState(TerminalState state) {
+ this.state = state;
+ terminalListener.setState(state);
+ // enable the (blinking) cursor if the terminal is connected
+ runAsyncInDisplayThread(new Runnable() {
+ @Override public void run() {
+ if (textControl != null && !textControl.isDisposed()) {
+ textControl.setCursorEnabled(isConnected());
+ }
+ }
+ });
+ }
+
+ private void runAsyncInDisplayThread(Runnable runnable) {
+ if (Display.findDisplay(Thread.currentThread()) != null) {
+ runnable.run();
+ } else if (PlatformUI.isWorkbenchRunning()) {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(runnable);
+ // else should not happen and we ignore it...
+ }
+ }
+
+ @Override public String getSettingsSummary() {
+ if (getTerminalConnector() != null) {
+ return getTerminalConnector().getSettingsSummary();
+ }
+ return "";
+ }
+
+ @Override public void setConnector(ITerminalConnector connector) {
+ this.connector = connector;
+ }
+
+ @Override public ICommandInputField getCommandInputField() {
+ return commandInputField;
+ }
+
+ @Override public void setCommandInputField(ICommandInputField inputField) {
+ if (commandInputField != null) {
+ commandInputField.dispose();
+ }
+ commandInputField = inputField;
+ if (commandInputField != null) {
+ commandInputField.createControl(rootControl, this);
+ }
+ if (rootControl.isVisible()) {
+ rootControl.layout(true);
+ }
+ }
+
+ @Override public int getBufferLineLimit() {
+ return terminalModel.getMaxHeight();
+ }
+
+ @Override public void setBufferLineLimit(int bufferLineLimit) {
+ if (bufferLineLimit <= 0) {
+ return;
+ }
+ synchronized (terminalModel) {
+ if (terminalModel.getHeight() > bufferLineLimit) {
+ terminalModel.setDimensions(bufferLineLimit, terminalModel.getWidth());
+ }
+ terminalModel.setMaxHeight(bufferLineLimit);
+ }
+ }
+
+ @Override public boolean isScrollLockOn() {
+ return textControl.isScrollLockOn();
+ }
+
+ @Override public void setScrollLockOn(boolean on) {
+ textControl.setScrollLockOn(on);
+ }
+
+ @Override public void setInvertedColors(boolean invert) {
+ textControl.setInvertedColors(invert);
+ }
+
+ public void setColors(RGB background, RGB foreground) {
+ textControl.setColors(background, foreground);
+ }
+
+ public void setBlinkingCursor(boolean useBlinkingCursor) {
+ textControl.setBlinkingCursor(useBlinkingCursor);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/HttpHyperlinkFactory.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/HttpHyperlinkFactory.java
new file mode 100644
index 0000000..8b2e4d0
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/HttpHyperlinkFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.emulator.hyperlink;
+
+import java.util.*;
+import java.util.regex.*;
+
+import org.eclipse.jface.text.*;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class HttpHyperlinkFactory implements IHyperlinkFactory {
+ private static Pattern URL_PATTERN =
+ Pattern.compile("http(s)?:\\/{2}[\\d\\w-]+(\\.[\\d\\w-]+)*(?:(?:\\/[^\\s/]*))*(\\:[\\d]+)?");
+
+ @Override public List<IHyperlink> hyperlinksIn(int column, String text) {
+ if (text == null) {
+ return NO_HYPERLINKS;
+ }
+ List<IHyperlink> hyperlinks = new ArrayList<IHyperlink>();
+ Matcher matcher = URL_PATTERN.matcher(text);
+ int start = 0;
+ while (matcher.find(start)) {
+ String url = matcher.group().trim();
+ if (url.isEmpty()) {
+ continue;
+ }
+ IRegion region = new Region(column + matcher.start(), url.length());
+ hyperlinks.add(new UrlHyperlink(region, url));
+ start = matcher.end();
+ }
+ return hyperlinks.isEmpty() ? NO_HYPERLINKS : hyperlinks;
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/IHyperlinkFactory.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/IHyperlinkFactory.java
new file mode 100644
index 0000000..f58919a
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/IHyperlinkFactory.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.emulator.hyperlink;
+
+import static java.util.Collections.emptyList;
+
+import java.util.List;
+
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public interface IHyperlinkFactory {
+ List<IHyperlink> NO_HYPERLINKS = emptyList();
+
+ List<IHyperlink> hyperlinksIn(int column, String text);
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/UrlHyperlink.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/UrlHyperlink.java
new file mode 100644
index 0000000..bec695b
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/hyperlink/UrlHyperlink.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.emulator.hyperlink;
+
+import java.net.*;
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.hyperlink.URLHyperlink;
+import org.eclipse.ui.*;
+import org.eclipse.ui.browser.*;
+
+import com.google.eclipse.elt.emulator.impl.TerminalPlugin;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class UrlHyperlink extends URLHyperlink {
+ public UrlHyperlink(IRegion region, String urlString) {
+ super(region, urlString);
+ }
+
+ @Override public void open() {
+ // Create the browser
+ IWorkbenchBrowserSupport support= PlatformUI.getWorkbench().getBrowserSupport();
+ IWebBrowser browser;
+ try {
+ browser= support.createBrowser(null);
+ } catch (PartInitException e) {
+ logErrorOpeningUrl(e);
+ return;
+ }
+ URL url = null;
+ try {
+ url = new URL(getURLString());
+ browser.openURL(url);
+ } catch (PartInitException e) {
+ openInExternalBrowser(url);
+ } catch (MalformedURLException e) {
+ logErrorOpeningUrl(e);
+ }
+ }
+
+ private void openInExternalBrowser(URL url) {
+ try {
+ PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser().openURL(url);
+ } catch (Exception e) {
+ logErrorOpeningUrl(e);
+ }
+ }
+
+ private void logErrorOpeningUrl(Exception e) {
+ e.printStackTrace();
+ String format = "Unable to open URL '%s'";
+ TerminalPlugin.log(String.format(format, getURLString()), e);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/ITerminalControlForText.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/ITerminalControlForText.java
new file mode 100644
index 0000000..37d7bd1
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/ITerminalControlForText.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.impl;
+
+import java.io.OutputStream;
+
+import com.google.eclipse.elt.emulator.provisional.api.*;
+
+public interface ITerminalControlForText {
+
+ TerminalState getState();
+
+ void setState(TerminalState state);
+
+ void setTerminalTitle(String title);
+
+ ITerminalConnector getTerminalConnector();
+
+ OutputStream getOutputStream();
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalInputStream.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalInputStream.java
new file mode 100644
index 0000000..a41a0e0
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalInputStream.java
@@ -0,0 +1,232 @@
+/*******************************************************************************
+ * Copyright (c) 1996, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Michael Scharf (Wind River)
+ * Douglas Lea (Addison Wesley) - [cq:1552] BoundedBufferWithStateTracking adapted to BoundedByteBuffer
+ *******************************************************************************/
+
+package com.google.eclipse.elt.emulator.impl;
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.swt.widgets.Display;
+
+import com.google.eclipse.elt.emulator.util.BoundedByteBuffer;
+
+/**
+ * The main purpose of this class is to start a {@code Runnable} in the display thread when data is available and to
+ * pretend no data is available after a given amount of time the {@code Runnable} is running.
+ */
+public class TerminalInputStream extends InputStream {
+ // The maximum time in milliseconds the 'notifyChange' runs until 'ready()' returns false.
+ private final int uiTimeout;
+
+ // The output stream used by the terminal backend to write to the terminal.
+ protected final OutputStream outputStream;
+
+ // This runnable is called every time some characters are available from.
+ private final Runnable notifyChange;
+
+ // A shared timer for all terminals. This times is used to limit the time used in the display thread.
+ static Timer timer = new Timer(false);
+
+ // A blocking byte queue.
+ private final BoundedByteBuffer queue;
+
+ // The maximum amount of data read and written in one shot. The timer cannot interrupt reading this amount of data.
+ // This is used as optimization, because reading single characters can be very inefficient, because each call is
+ // synchronized.
+ // Block size must be smaller than the Queue capacity!
+ final int BLOCK_SIZE = 64;
+
+ // The runnable that is scheduled in the display tread. Takes care of the timeout management. It calls the
+ // 'notifyChange'.
+ // Synchronized with 'queue'.
+ private Runnable runnable;
+
+ // Used as flag to indicate that the current runnable has used enough time in the display thread. This variable is set
+ // by a timer thread after the Runnable starts to run in the Display thread after 'uiTimeout'.
+ // Synchronized with 'queue'.
+ private boolean enoughDisplayTime;
+
+ /**
+ * Constructor.
+ *
+ * @param bufferSize the size of the buffer of the output stream
+ * @param uiTimeout the maximum time the {@code notifyChange} {@code Runnable} runs. It will be rescheduled after
+ * {@code uiTimeout} if input data is still available.
+ * @param notifyChange a {@code Runnable} that is posted to the display thread via {@link Display#asyncExec}. The
+ * {@code Runnable} is posted several times!
+ */
+ public TerminalInputStream(int bufferSize, int uiTimeout, Runnable notifyChange) {
+ outputStream = new TerminalOutputStream();
+ this.notifyChange = notifyChange;
+ int capacity = bufferSize;
+ if (capacity < BLOCK_SIZE) {
+ capacity = 2 * BLOCK_SIZE;
+ }
+ queue = new BoundedByteBuffer(capacity);
+ this.uiTimeout = uiTimeout;
+ }
+
+ /**
+ * Posts the {@code Runnable} {@link #notifyChange} to the display thread, unless the {@code Runnable} is already
+ * scheduled. It will make {@link #ready} return {@code false} after {@link #uiTimeout} milliseconds.
+ */
+ void bytesAreAvailable() {
+ // synchronize on the queue to reduce the locks
+ synchronized (queue) {
+ if (runnable == null) {
+ runnable = new Runnable() {
+ @Override public void run() {
+ synchronized (queue) {
+ runnable = null;
+ }
+ startTimer(uiTimeout);
+ notifyChange.run();
+ }
+ };
+ // TODO: Make sure we don't create a display if the display is disposed.
+ Display.getDefault().asyncExec(runnable);
+ }
+ }
+ }
+
+ /**
+ * Starts a timer that sets {@link #enoughDisplayTime} to {@code true} after the given milliseconds.
+ *
+ * @param milliseconds the time after which {@code enoughDisplayTime} is set to {@code true}.
+ */
+ void startTimer(int milliseconds) {
+ synchronized (queue) {
+ enoughDisplayTime = false;
+ }
+ timer.schedule(new TimerTask() {
+ @Override public void run() {
+ synchronized (queue) {
+ enoughDisplayTime = true;
+ // there is some data available
+ if (queue.size() > 0) {
+ // schedule a new runnable to do the work
+ bytesAreAvailable();
+ }
+ }
+ }
+ }, milliseconds);
+ }
+
+ /**
+ * Returns the output stream used by the back-end to write to the terminal.
+ *
+ * @return the output stream used by the back-end to write to the terminal.
+ */
+ public OutputStream getOutputStream() {
+ return outputStream;
+ }
+
+ /**
+ * Indicate whether a character is available for the terminal to show. Must be called in the display thread.
+ * @return {@code true} if a character is available for the terminal to show, {@code false} otherwise.
+ */
+ @Override public int available() {
+ int available;
+ synchronized (queue) {
+ if (enoughDisplayTime) {
+ return 0;
+ }
+ available = queue.size();
+ }
+ // Limit the available amount of data. Otherwise our trick of limiting the time spend reading might not work.
+ if (available > BLOCK_SIZE) {
+ available = BLOCK_SIZE;
+ }
+ return available;
+ }
+
+ /**
+ * Returns the next available byte. Checks with {@link #available} if characters are available.
+ *
+ * @return the next available byte.
+ */
+ @Override public int read() throws IOException {
+ try {
+ return queue.read();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return -1;
+ }
+ }
+
+ /**
+ * Closing a {@code ByteArrayInputStream} has no effect. The methods in this class can be called after the stream has
+ * been closed without generating an {@code IOException}.
+ */
+ @Override public void close() {}
+
+ @Override public int read(byte[] b, int off, int len) throws IOException {
+ int n = 0;
+ // Read as much as we can using a single synchronized statement.
+ synchronized (queue) {
+ try {
+ // Make sure that not more than BLOCK_SIZE is read in one call.
+ while (queue.size() > 0 && n < len && n < BLOCK_SIZE) {
+ b[off + n] = queue.read();
+ n++;
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ return n;
+ }
+
+ /**
+ * An output stream that calls {@link TerminalInputStream#textAvailable} every time data is written to the stream. The
+ * data is written to {@link TerminalInputStream#queue}.
+ *
+ */
+ class TerminalOutputStream extends OutputStream {
+ @Override public void write(byte[] b, int off, int len) throws IOException {
+ try {
+ // optimization to avoid many synchronized sections: put the data in junks into the queue.
+ int newOff = off;
+ int end = off + len;
+ while (newOff < end) {
+ int n = newOff + BLOCK_SIZE;
+ if (n > end) {
+ n = end;
+ }
+ // now block the queue for the time we need to
+ // add some characters
+ synchronized (queue) {
+ for (int i = newOff; i < n; i++) {
+ queue.write(b[i]);
+ }
+ bytesAreAvailable();
+ }
+ newOff += BLOCK_SIZE;
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ @Override public void write(int b) throws IOException {
+ try {
+ // A kind of optimization, because both calls use the queue lock.
+ synchronized (queue) {
+ queue.write((byte) b);
+ bytesAreAvailable();
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalMessages.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalMessages.java
new file mode 100644
index 0000000..fa1755e
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalMessages.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.impl;
+
+import org.eclipse.osgi.util.NLS;
+
+public class TerminalMessages extends NLS {
+ public static String terminalError;
+ public static String socketError;
+ public static String ioError;
+ public static String cannotConnectTo;
+ public static String notInitialized;
+
+ static {
+ Class<?> clazz = TerminalMessages.class;
+ NLS.initializeMessages(clazz.getName(), clazz);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalMessages.properties b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalMessages.properties
new file mode 100644
index 0000000..1fecdb3
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalMessages.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2003, 2009 Wind River Systems, Inc. and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+# NLS_MESSAGEFORMAT_VAR
+
+terminalError = Terminal Error
+socketError = Socket Error
+ioError = IO Error
+cannotConnectTo = Cannot initialize {0}:\n{1}
+notInitialized = Not Initialized
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalPlugin.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalPlugin.java
new file mode 100644
index 0000000..1a9fe5b
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/impl/TerminalPlugin.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.impl;
+
+import static org.eclipse.core.runtime.IStatus.ERROR;
+
+import static com.google.eclipse.elt.emulator.actions.Images.*;
+
+import java.net.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.resource.*;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+public class TerminalPlugin extends AbstractUIPlugin {
+ public static final String PLUGIN_ID = "com.google.eclipse.elt.emulator";
+
+ protected static TerminalPlugin plugin;
+
+ public TerminalPlugin() {
+ plugin = this;
+ }
+
+ public static TerminalPlugin getDefault() {
+ return plugin;
+ }
+
+ public static boolean isOptionEnabled(String option) {
+ String enabled = Platform.getDebugOption(option);
+ if (enabled == null) {
+ return false;
+ }
+ return Boolean.parseBoolean(enabled);
+ }
+
+ @Override protected void initializeImageRegistry(ImageRegistry imageRegistry) {
+ try {
+ // Local tool-bars
+ putImageInRegistry(imageRegistry, IMAGE_CLCL_CLEAR_ALL, IMAGE_DIR_LOCALTOOL + "clear_co.gif");
+ // Enabled local tool-bars
+ putImageInRegistry(imageRegistry, IMAGE_ELCL_CLEAR_ALL, IMAGE_DIR_ELCL + "clear_co.gif");
+ // Disabled local tool-bars
+ putImageInRegistry(imageRegistry, IMAGE_DLCL_CLEAR_ALL, IMAGE_DIR_DLCL + "clear_co.gif");
+ } catch (MalformedURLException malformedURLException) {
+ malformedURLException.printStackTrace();
+ }
+ }
+
+ protected void putImageInRegistry(
+ ImageRegistry imageRegistry, String strKey, String relativePath) throws MalformedURLException {
+ URL url = TerminalPlugin.getDefault().getBundle().getEntry(relativePath);
+ ImageDescriptor imageDescriptor = ImageDescriptor.createFromURL(url);
+ imageRegistry.put(strKey, imageDescriptor);
+ }
+
+ public static void log(String message, Exception error) {
+ getDefault().getLog().log(new Status(ERROR, PLUGIN_ID, message, error));
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/ISnapshotChanges.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/ISnapshotChanges.java
new file mode 100644
index 0000000..b4f0fe0
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/ISnapshotChanges.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import com.google.eclipse.elt.emulator.model.ITerminalTextData;
+
+public interface ISnapshotChanges {
+ void markLineChanged(int line);
+
+ void markLinesChanged(int line, int size);
+
+ /**
+ * Marks all lines within the scrolling region changed and resets the scrolling information.
+ */
+ void convertScrollingIntoChanges();
+
+ boolean hasChanged();
+
+ void scroll(int startLine, int size, int shift);
+
+ void setAllChanged(int height);
+
+ int getFirstChangedLine();
+
+ int getLastChangedLine();
+
+ int getScrollWindowStartLine();
+
+ int getScrollWindowSize();
+
+ int getScrollWindowShift();
+
+ boolean hasLineChanged(int line);
+
+ void markDimensionsChanged();
+
+ boolean hasDimensionsChanged();
+
+ void markCursorChanged();
+
+ boolean hasTerminalChanged();
+
+ void setTerminalChanged();
+
+ void copyChangedLines(ITerminalTextData destination, ITerminalTextData source);
+
+ void setInterestWindow(int startLine, int size);
+
+ int getInterestWindowStartLine();
+
+ int getInterestWindowSize();
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/SnapshotChanges.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/SnapshotChanges.java
new file mode 100644
index 0000000..fc43d0d
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/SnapshotChanges.java
@@ -0,0 +1,333 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import org.eclipse.core.runtime.Assert;
+
+import com.google.eclipse.elt.emulator.model.ITerminalTextData;
+
+/**
+ * Collects the changes of the {@link ITerminalTextData}.
+ */
+public class SnapshotChanges implements ISnapshotChanges {
+ private int firstChangedLine;
+ private int lastChangedLine;
+ private int scrollWindowStartLine;
+ private int scrollWindowSize;
+ private int scrollWindowShift;
+ private boolean dontTrackScrolling;
+ private boolean[] changedLines;
+ private int interestWindowSize;
+ private int interestWindowStartLine;
+ private boolean fDimensionsChanged;
+ private boolean fTerminalHasChanged;
+ private boolean cursorHasChanged;
+
+ public SnapshotChanges(int windowStart, int windowSize) {
+ this(windowStart + windowSize);
+ interestWindowStartLine = windowStart;
+ interestWindowSize = windowSize;
+ }
+
+ public SnapshotChanges(int changedLineCount) {
+ setChangedLineCount(changedLineCount);
+ firstChangedLine = Integer.MAX_VALUE;
+ lastChangedLine = -1;
+ }
+
+ // Indicates whether the range overlaps with the interest window.
+ private boolean isInInterestWindow(int line, int size) {
+ if (interestWindowSize <= 0) {
+ return true;
+ }
+ if (line + size <= interestWindowStartLine || line >= interestWindowStartLine + interestWindowSize) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isInInterestWindow(int line) {
+ if (interestWindowSize <= 0) {
+ return true;
+ }
+ if (line < interestWindowStartLine || line >= interestWindowStartLine + interestWindowSize) {
+ return false;
+ }
+ return true;
+ }
+
+ private int fitLineToWindow(int line) {
+ if (interestWindowSize <= 0) {
+ return line;
+ }
+ if (line < interestWindowStartLine) {
+ return interestWindowStartLine;
+ }
+ return line;
+ }
+
+ /**
+ * The result is only defined if {@link #isInInterestWindow(int, int)} returns {@code true}.
+ * <p>
+ * Note:{@link #fitLineToWindow(int)} has to be called on the line to move the window correctly.
+ * </p>
+ * @param line the line <b>before</b> {@link #fitLineToWindow(int)} has been called.
+ * @param size
+ * @return the adjusted size.
+ */
+ private int fitSizeToWindow(int line, int size) {
+ if (interestWindowSize <= 0) {
+ return size;
+ }
+ if (line < interestWindowStartLine) {
+ size -= interestWindowStartLine - line;
+ line = interestWindowStartLine;
+ }
+ if (line + size > interestWindowStartLine + interestWindowSize) {
+ size = interestWindowStartLine + interestWindowSize - line;
+ }
+ return size;
+ }
+
+ @Override public void markLineChanged(int line) {
+ if (!isInInterestWindow(line)) {
+ return;
+ }
+ line = fitLineToWindow(line);
+ if (line < firstChangedLine) {
+ firstChangedLine = line;
+ }
+ if (line > lastChangedLine) {
+ lastChangedLine = line;
+ }
+ // In case the terminal got resized we expand don't remember the changed line because there is nothing to copy.
+ if (line < getChangedLineCount()) {
+ setChangedLine(line, true);
+ }
+ }
+
+ @Override public void markLinesChanged(int line, int size) {
+ if (size <= 0 || !isInInterestWindow(line, size)) {
+ return;
+ }
+ // Do not exceed the bounds of changedLines. The terminal might have been resized and we can only keep changes for
+ // the size of the previous terminal.
+ size = fitSizeToWindow(line, size);
+ line = fitLineToWindow(line);
+ int m = Math.min(line + size, getChangedLineCount());
+ for (int i = line; i < m; i++) {
+ setChangedLine(i, true);
+ }
+ // this sets firstChangedLine as well
+ markLineChanged(line);
+ // this sets lastChangedLine as well
+ markLineChanged(line + size - 1);
+ }
+
+ @Override public void markCursorChanged() {
+ cursorHasChanged = true;
+ }
+
+ @Override public void convertScrollingIntoChanges() {
+ markLinesChanged(scrollWindowStartLine, scrollWindowSize);
+ scrollWindowStartLine = 0;
+ scrollWindowSize = 0;
+ scrollWindowShift = 0;
+ }
+
+ @Override public boolean hasChanged() {
+ if (firstChangedLine != Integer.MAX_VALUE || lastChangedLine > 0 || scrollWindowShift != 0 || fDimensionsChanged
+ || cursorHasChanged) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override public void markDimensionsChanged() {
+ fDimensionsChanged = true;
+ }
+
+ @Override public boolean hasDimensionsChanged() {
+ return fDimensionsChanged;
+ }
+
+ @Override public boolean hasTerminalChanged() {
+ return fTerminalHasChanged;
+ }
+
+ @Override public void setTerminalChanged() {
+ fTerminalHasChanged = true;
+ }
+
+ @Override public void scroll(int startLine, int size, int shift) {
+ size = fitSizeToWindow(startLine, size);
+ startLine = fitLineToWindow(startLine);
+ // Let's track only negative shifts.
+ if (dontTrackScrolling) {
+ // We are in a state where we cannot track scrolling so let's simply mark the scrolled lines as changed.
+ markLinesChanged(startLine, size);
+ } else if (shift >= 0) {
+ // We cannot handle positive scroll forget about clever caching of scroll events.
+ doNotTrackScrollingAnymore();
+ // Mark all lines inside the scroll region as changed.
+ markLinesChanged(startLine, size);
+ } else {
+ // We have already scrolled.
+ if (scrollWindowShift < 0) {
+ // We have already scrolled.
+ if (scrollWindowStartLine == startLine && scrollWindowSize == size) {
+ // We are scrolling the same region again?
+ scrollWindowShift += shift;
+ scrollChangesLinesWithNegativeShift(startLine, size, shift);
+ } else {
+ // Mark all lines in the old scroll region as changed.
+ doNotTrackScrollingAnymore();
+ markLinesChanged(startLine, size);
+ }
+ } else {
+ // First scroll in this change -- we just notify it
+ scrollWindowStartLine = startLine;
+ scrollWindowSize = size;
+ scrollWindowShift = shift;
+ scrollChangesLinesWithNegativeShift(startLine, size, shift);
+ }
+ }
+ }
+
+ // Some incompatible scrolling occurred. We cannot do the scroll optimization anymore.
+ private void doNotTrackScrollingAnymore() {
+ if (scrollWindowSize > 0) {
+ // Convert the current scrolling into changes.
+ markLinesChanged(scrollWindowStartLine, scrollWindowSize);
+ scrollWindowStartLine = 0;
+ scrollWindowSize = 0;
+ scrollWindowShift = 0;
+ }
+ // Don't be clever on scrolling anymore.
+ dontTrackScrolling = true;
+ }
+
+ private void scrollChangesLinesWithNegativeShift(int line, int n, int shift) {
+ Assert.isTrue(shift < 0);
+ // Scroll the region. Don't run out of bounds!
+ int m = Math.min(line + n + shift, getChangedLineCount() + shift);
+ for (int i = line; i < m; i++) {
+ setChangedLine(i, hasLineChanged(i - shift));
+ // Move the first changed line up. We don't have to move the maximum down, because with a shift scroll, the max is
+ // moved by the next loop in this method
+ if (i < firstChangedLine && hasLineChanged(i)) {
+ firstChangedLine = i;
+ }
+ }
+ // Mark the "opened" lines as changed.
+ for (int i = Math.max(0, line + n + shift); i < line + n; i++) {
+ markLineChanged(i);
+ }
+ }
+
+ @Override public void setAllChanged(int height) {
+ scrollWindowStartLine = 0;
+ scrollWindowSize = 0;
+ scrollWindowShift = 0;
+ firstChangedLine = fitLineToWindow(0);
+ lastChangedLine = firstChangedLine + fitSizeToWindow(0, height) - 1;
+ // No need to keep an array of changes anymore.
+ setChangedLineCount(0);
+ }
+
+ @Override public int getFirstChangedLine() {
+ return firstChangedLine;
+ }
+
+ @Override public int getLastChangedLine() {
+ return lastChangedLine;
+ }
+
+ @Override public int getScrollWindowStartLine() {
+ return scrollWindowStartLine;
+ }
+
+ @Override public int getScrollWindowSize() {
+ return scrollWindowSize;
+ }
+
+ @Override public int getScrollWindowShift() {
+ return scrollWindowShift;
+ }
+
+ @Override public void copyChangedLines(ITerminalTextData destination, ITerminalTextData source) {
+ int n = Math.min(lastChangedLine + 1, source.getHeight());
+ for (int i = firstChangedLine; i < n; i++) {
+ if (hasLineChanged(i)) {
+ destination.copyLine(source, i, i);
+ }
+ }
+ }
+
+ @Override public int getInterestWindowSize() {
+ return interestWindowSize;
+ }
+
+ @Override public int getInterestWindowStartLine() {
+ return interestWindowStartLine;
+ }
+
+ @Override public void setInterestWindow(int startLine, int size) {
+ int oldStartLine = interestWindowStartLine;
+ int oldSize = interestWindowSize;
+ interestWindowStartLine = startLine;
+ interestWindowSize = size;
+ if (oldSize > 0) {
+ int shift = oldStartLine - startLine;
+ if (shift == 0) {
+ if (size > oldSize) {
+ // add lines to the end
+ markLinesChanged(oldStartLine + oldSize, size - oldSize);
+ }
+ // else no lines within the window have changed
+
+ } else if (Math.abs(shift) < size) {
+ if (shift < 0) {
+ // we can scroll
+ scroll(startLine, oldSize, shift);
+ // mark the lines at the end as new
+ for (int i = oldStartLine + oldSize; i < startLine + size; i++) {
+ markLineChanged(i);
+ }
+ } else {
+ // we cannot shift positive -- mark all changed
+ markLinesChanged(startLine, size);
+ }
+ } else {
+ // no scrolling possible
+ markLinesChanged(startLine, size);
+ }
+ }
+ }
+
+ @Override public boolean hasLineChanged(int line) {
+ if (line < getChangedLineCount()) {
+ return changedLines[line];
+ }
+ // since the height of the terminal could have changed but we have tracked only changes of the previous terminal
+ // height, any line outside the the range of the previous height has changed
+ return isInInterestWindow(line);
+ }
+
+ private int getChangedLineCount() {
+ return changedLines.length;
+ }
+
+ private void setChangedLine(int line, boolean changed) {
+ changedLines[line] = changed;
+ }
+
+ void setChangedLineCount(int length) {
+ changedLines = new boolean[length];
+ }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/SynchronizedTerminalTextData.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/SynchronizedTerminalTextData.java
new file mode 100644
index 0000000..74f7fc6
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/SynchronizedTerminalTextData.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse public synchronized License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Michael Scharf (Wind River) - initial API and implementation
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+/**
+ * This is a decorator to make all access to an {@link ITerminalTextData} synchronized.
+ */
+public class SynchronizedTerminalTextData implements ITerminalTextData {
+ private final ITerminalTextData data;
+
+ public SynchronizedTerminalTextData(ITerminalTextData data) {
+ this.data = data;
+ }
+
+ @Override public synchronized void addLine() {
+ data.addLine();
+ }
+
+ @Override public synchronized void cleanLine(int line) {
+ data.cleanLine(line);
+ }
+
+ @Override public synchronized void copy(ITerminalTextData source) {
+ data.copy(source);
+ }
+
+ @Override public synchronized void copyLine(ITerminalTextData source, int sourceLine, int destinationLine) {
+ data.copyLine(source, sourceLine, destinationLine);
+ }
+
+ @Override public synchronized void copyRange(
+ ITerminalTextData source, int sourceStartLine, int destinationStartLine, int length) {
+ data.copyRange(source, sourceStartLine, destinationStartLine, length);
+ }
+
+ @Override public synchronized char getChar(int line, int column) {
+ return data.getChar(line, column);
+ }
+
+ @Override public synchronized char[] getChars(int line) {
+ return data.getChars(line);
+ }
+
+ @Override public synchronized int getCursorColumn() {
+ return data.getCursorColumn();
+ }
+
+ @Override public synchronized int getCursorLine() {
+ return data.getCursorLine();
+ }
+
+ @Override public synchronized int getHeight() {
+ return data.getHeight();
+ }
+
+ @Override public synchronized LineSegment[] getLineSegments(int line, int startColumn, int columnCount) {
+ return data.getLineSegments(line, startColumn, columnCount);
+ }
+
+ @Override public synchronized int getMaxHeight() {
+ return data.getMaxHeight();
+ }
+
+ @Override public synchronized Style getStyle(int line, int column) {
+ return data.getStyle(line, column);
+ }
+
+ @Override public synchronized Style[] getStyles(int line) {
+ return data.getStyles(line);
+ }
+
+ @Override public synchronized int getWidth() {
+ return data.getWidth();
+ }
+
+ @Override public synchronized ITerminalTextDataSnapshot makeSnapshot() {
+ return data.makeSnapshot();
+ }
+
+ @Override public synchronized void scroll(int startLine, int size, int shift) {
+ data.scroll(startLine, size, shift);
+ }
+
+ @Override public synchronized void setChar(int line, int column, char c, Style style) {
+ data.setChar(line, column, c, style);
+ }
+
+ @Override public synchronized void setChars(int line, int column, char[] chars, int start, int length, Style style) {
+ data.setChars(line, column, chars, start, length, style);
+ }
+
+ @Override public synchronized void setChars(int line, int column, char[] chars, Style style) {
+ data.setChars(line, column, chars, style);
+ }
+
+ @Override public synchronized void setCursorColumn(int column) {
+ data.setCursorColumn(column);
+ }
+
+ @Override public synchronized void setCursorLine(int line) {
+ data.setCursorLine(line);
+ }
+
+ @Override public synchronized void setDimensions(int height, int width) {
+ data.setDimensions(height, width);
+ }
+
+ @Override public synchronized void setMaxHeight(int height) {
+ data.setMaxHeight(height);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextData.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextData.java
new file mode 100644
index 0000000..2805e46
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextData.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import java.util.*;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+public class TerminalTextData implements ITerminalTextData {
+ private final ITerminalTextData data;
+ private final List<TerminalTextDataSnapshot> snapshots = new ArrayList<TerminalTextDataSnapshot>();
+ private int cursorLine;
+ private int cursorColumn;
+
+ public TerminalTextData() {
+ this(new TerminalTextDataFastScroll());
+ }
+
+ public TerminalTextData(ITerminalTextData data) {
+ this.data = data;
+ }
+
+ @Override public int getWidth() {
+ return data.getWidth();
+ }
+
+ @Override public int getHeight() {
+ return data.getHeight();
+ }
+
+ @Override public void setDimensions(int height, int width) {
+ int currentHeight = getHeight();
+ int currentWidth = getWidth();
+ if (currentWidth == width && currentHeight == height) {
+ return;
+ }
+ data.setDimensions(height, width);
+ sendDimensionsChanged(currentHeight, currentWidth, height, width);
+ }
+
+ private void sendDimensionsChanged(int oldHeight, int oldWidth, int newHeight, int newWidth) {
+ // determine what has changed
+ if (oldWidth == newWidth) {
+ if (oldHeight < newHeight) {
+ sendLinesChangedToSnapshot(oldHeight, newHeight - oldHeight);
+ } else {
+ sendLinesChangedToSnapshot(newHeight, oldHeight - newHeight);
+ }
+ } else {
+ sendLinesChangedToSnapshot(0, oldHeight);
+ }
+ sendDimensionsChanged();
+ }
+
+ @Override public LineSegment[] getLineSegments(int line, int column, int len) {
+ return data.getLineSegments(line, column, len);
+ }
+
+ @Override public char getChar(int line, int column) {
+ return data.getChar(line, column);
+ }
+
+ @Override public Style getStyle(int line, int column) {
+ return data.getStyle(line, column);
+ }
+
+ @Override public void setChar(int line, int column, char c, Style style) {
+ data.setChar(line, column, c, style);
+ sendLineChangedToSnapshots(line);
+ }
+
+ @Override public void setChars(int line, int column, char[] chars, Style style) {
+ data.setChars(line, column, chars, style);
+ sendLineChangedToSnapshots(line);
+ }
+
+ @Override public void setChars(int line, int column, char[] chars, int start, int length, Style style) {
+ data.setChars(line, column, chars, start, length, style);
+ sendLineChangedToSnapshots(line);
+ }
+
+ @Override public void scroll(int startLine, int size, int shift) {
+ data.scroll(startLine, size, shift);
+ sendScrolledToSnapshots(startLine, size, shift);
+ }
+
+ @Override public String toString() {
+ return data.toString();
+ }
+
+ private void sendDimensionsChanged() {
+ for (TerminalTextDataSnapshot snapshot : snapshots) {
+ snapshot.markDimensionsChanged();
+ }
+ }
+
+ protected void sendLineChangedToSnapshots(int line) {
+ for (TerminalTextDataSnapshot snapshot : snapshots) {
+ snapshot.markLineChanged(line);
+ }
+ }
+
+ protected void sendLinesChangedToSnapshot(int startingLine, int lineCount) {
+ for (TerminalTextDataSnapshot snapshot : snapshots) {
+ snapshot.markLinesChanged(startingLine, lineCount);
+ }
+ }
+
+ /**
+ * Notifies snapshot that a region was scrolled.
+ * @param startingLine first line of scrolled region.
+ * @param lineCount size of scrolled region (number of lines.)
+ * @param shift delta by which the region is scrolled.
+ */
+ protected void sendScrolledToSnapshots(int startingLine, int lineCount, int shift) {
+ for (TerminalTextDataSnapshot snapshot : snapshots) {
+ snapshot.scroll(startingLine, lineCount, shift);
+ }
+ }
+
+ protected void sendCursorChanged() {
+ for (TerminalTextDataSnapshot snapshot : snapshots) {
+ snapshot.markCursorChanged();
+ }
+ }
+
+ /**
+ * Removes the snapshot from the observer list.
+ * @param snapshot a snapshot of a terminal model.
+ */
+ protected void removeSnapshot(TerminalTextDataSnapshot snapshot) {
+ snapshots.remove(snapshot);
+ }
+
+ @Override public ITerminalTextDataSnapshot makeSnapshot() {
+ TerminalTextDataSnapshot snapshot = new TerminalTextDataSnapshot(this);
+ snapshot.markDimensionsChanged();
+ snapshots.add(snapshot);
+ return snapshot;
+ }
+
+ @Override public void addLine() {
+ int oldHeight = getHeight();
+ data.addLine();
+ // was is an append or a scroll?
+ int newHeight = getHeight();
+ if (newHeight > oldHeight) {
+ // the line was appended
+ sendLinesChangedToSnapshot(oldHeight, 1);
+ int width = getWidth();
+ sendDimensionsChanged(oldHeight, width, newHeight, width);
+ } else {
+ // the line was scrolled
+ sendScrolledToSnapshots(0, oldHeight, -1);
+ }
+ }
+
+ @Override public void copy(ITerminalTextData source) {
+ data.copy(source);
+ cursorLine = source.getCursorLine();
+ cursorColumn = source.getCursorColumn();
+ }
+
+ @Override public void copyLine(ITerminalTextData source, int sourceLine, int destinationLine) {
+ data.copyLine(source, sourceLine, destinationLine);
+ }
+
+ @Override public void copyRange(ITerminalTextData source, int sourceStartLine, int destinationStartLine, int length) {
+ data.copyRange(source, sourceStartLine, destinationStartLine, length);
+ }
+
+ @Override public char[] getChars(int line) {
+ return data.getChars(line);
+ }
+
+ @Override public Style[] getStyles(int line) {
+ return data.getStyles(line);
+ }
+
+ @Override public int getMaxHeight() {
+ return data.getMaxHeight();
+ }
+
+ @Override public void setMaxHeight(int height) {
+ data.setMaxHeight(height);
+ }
+
+ @Override public void cleanLine(int line) {
+ data.cleanLine(line);
+ sendLineChangedToSnapshots(line);
+ }
+
+ @Override public int getCursorColumn() {
+ return cursorColumn;
+ }
+
+ @Override public int getCursorLine() {
+ return cursorLine;
+ }
+
+ @Override public void setCursorColumn(int column) {
+ cursorColumn = column;
+ sendCursorChanged();
+ }
+
+ @Override public void setCursorLine(int line) {
+ cursorLine = line;
+ sendCursorChanged();
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataFastScroll.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataFastScroll.java
new file mode 100644
index 0000000..66c4b21
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataFastScroll.java
@@ -0,0 +1,227 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import org.eclipse.core.runtime.Assert;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+/**
+ * This class is optimized for scrolling the entire {@link #getHeight()}. The scrolling is done by moving an offset into
+ * the data and using the modulo operator.
+ */
+public class TerminalTextDataFastScroll implements ITerminalTextData {
+ private final ITerminalTextData data;
+ private int height;
+ private int maxHeight;
+ private int offset;
+
+ public TerminalTextDataFastScroll(ITerminalTextData data, int maxHeight) {
+ this.maxHeight = maxHeight;
+ this.data = data;
+ this.data.setDimensions(this.maxHeight, this.data.getWidth());
+ if (this.maxHeight > 2) {
+ moveOffset(-2);
+ }
+ }
+
+ public TerminalTextDataFastScroll(int maxHeight) {
+ this(new TerminalTextDataStore(), maxHeight);
+ }
+
+ public TerminalTextDataFastScroll() {
+ this(new TerminalTextDataStore(), 1);
+ }
+
+ private int getPositionOfLine(int line) {
+ return (line + offset) % maxHeight;
+ }
+
+ private void moveOffset(int delta) {
+ Assert.isTrue(Math.abs(delta) < maxHeight);
+ offset = (maxHeight + offset + delta) % maxHeight;
+ }
+
+ @Override public void addLine() {
+ if (getHeight() < maxHeight) {
+ setDimensions(getHeight() + 1, getWidth());
+ } else {
+ scroll(0, getHeight(), -1);
+ }
+ }
+
+ @Override public void cleanLine(int line) {
+ data.cleanLine(getPositionOfLine(line));
+ }
+
+ @Override public void copy(ITerminalTextData source) {
+ int height = source.getHeight();
+ setDimensions(source.getHeight(), source.getWidth());
+ for (int i = 0; i < height; i++) {
+ data.copyLine(source, i, getPositionOfLine(i));
+ }
+ }
+
+ @Override public void copyLine(ITerminalTextData source, int sourceLine, int destinationLine) {
+ data.copyLine(source, sourceLine, getPositionOfLine(destinationLine));
+ }
+
+ @Override public void copyRange(ITerminalTextData source, int sourceStartLine, int destinationStartLine, int length) {
+ Assert.isTrue(destinationStartLine >= 0 && destinationStartLine + length <= height);
+ for (int i = 0; i < length; i++) {
+ data.copyLine(source, i + sourceStartLine, getPositionOfLine(i + destinationStartLine));
+ }
+ }
+
+ @Override public char getChar(int line, int column) {
+ Assert.isTrue(line >= 0 && line < height);
+ return data.getChar(getPositionOfLine(line), column);
+ }
+
+ @Override public char[] getChars(int line) {
+ Assert.isTrue(line >= 0 && line < height);
+ return data.getChars(getPositionOfLine(line));
+ }
+
+ @Override public int getHeight() {
+ return height;
+ }
+
+ @Override public LineSegment[] getLineSegments(int line, int startCol, int numberOfCols) {
+ Assert.isTrue(line >= 0 && line < height);
+ return data.getLineSegments(getPositionOfLine(line), startCol, numberOfCols);
+ }
+
+ @Override public int getMaxHeight() {
+ return maxHeight;
+ }
+
+ @Override public Style getStyle(int line, int column) {
+ Assert.isTrue(line >= 0 && line < height);
+ return data.getStyle(getPositionOfLine(line), column);
+ }
+
+ @Override public Style[] getStyles(int line) {
+ Assert.isTrue(line >= 0 && line < height);
+ return data.getStyles(getPositionOfLine(line));
+ }
+
+ @Override public int getWidth() {
+ return data.getWidth();
+ }
+
+ @Override public ITerminalTextDataSnapshot makeSnapshot() {
+ return data.makeSnapshot();
+ }
+
+ private void cleanLines(int line, int len) {
+ for (int i = line; i < line + len; i++) {
+ data.cleanLine(getPositionOfLine(i));
+ }
+ }
+
+ @Override public void scroll(int startLine, int size, int shift) {
+ Assert.isTrue(startLine >= 0 && startLine + size <= height);
+ if (shift >= maxHeight || -shift >= maxHeight) {
+ cleanLines(startLine, maxHeight - startLine);
+ return;
+ }
+ if (size == height) {
+ // This is the case this class is optimized for!
+ moveOffset(-shift);
+ // We only have to clean the lines that appear by the move.
+ if (shift < 0) {
+ cleanLines(Math.max(startLine, startLine + size + shift), Math.min(-shift, getHeight() - startLine));
+ } else {
+ cleanLines(startLine, Math.min(shift, getHeight() - startLine));
+ }
+ } else {
+ // we have to copy the lines.
+ if (shift < 0) {
+ // move the region up
+ for (int i = startLine; i < startLine + size + shift; i++) {
+ data.copyLine(data, getPositionOfLine(i - shift), getPositionOfLine(i));
+ }
+ // then clean the opened lines
+ cleanLines(Math.max(0, startLine + size + shift), Math.min(-shift, getHeight() - startLine));
+ } else {
+ for (int i = startLine + size - 1; i >= startLine && i - shift >= 0; i--) {
+ data.copyLine(data, getPositionOfLine(i - shift), getPositionOfLine(i));
+ }
+ cleanLines(startLine, Math.min(shift, getHeight() - startLine));
+ }
+ }
+ }
+
+ @Override public void setChar(int line, int column, char c, Style style) {
+ Assert.isTrue(line >= 0 && line < height);
+ data.setChar(getPositionOfLine(line), column, c, style);
+ }
+
+ @Override public void setChars(int line, int column, char[] chars, int start, int len, Style style) {
+ Assert.isTrue(line >= 0 && line < height);
+ data.setChars(getPositionOfLine(line), column, chars, start, len, style);
+ }
+
+ @Override public void setChars(int line, int column, char[] chars, Style style) {
+ Assert.isTrue(line >= 0 && line < height);
+ data.setChars(getPositionOfLine(line), column, chars, style);
+ }
+
+ @Override public void setDimensions(int height, int width) {
+ Assert.isTrue(height >= 0 && width >= 0);
+ if (height > maxHeight) {
+ setMaxHeight(height);
+ }
+ this.height = height;
+ if (width != data.getWidth()) {
+ data.setDimensions(maxHeight, width);
+ }
+ }
+
+ @Override public void setMaxHeight(int maxHeight) {
+ Assert.isTrue(maxHeight >= height);
+ // move everything to offset0
+ int start = getPositionOfLine(0);
+ if (start != 0) {
+ // invent a more efficient algorithm....
+ ITerminalTextData buffer = new TerminalTextDataStore();
+ // create a buffer with the expected height
+ buffer.setDimensions(maxHeight, getWidth());
+ int n = Math.min(maxHeight - start, maxHeight);
+ // copy the first part
+ buffer.copyRange(data, start, 0, n);
+ // copy the second part
+ if (n < maxHeight) {
+ buffer.copyRange(data, 0, n, Math.min(maxHeight - n, maxHeight - n));
+ }
+ // copy the buffer back to our data
+ data.copy(buffer);
+ moveOffset(-start);
+ } else {
+ data.setDimensions(maxHeight, data.getWidth());
+ }
+ this.maxHeight = maxHeight;
+ }
+
+ @Override public int getCursorColumn() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override public int getCursorLine() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override public void setCursorColumn(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override public void setCursorLine(int line) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataSnapshot.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataSnapshot.java
new file mode 100644
index 0000000..f4ae9ba
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataSnapshot.java
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import java.util.*;
+
+import org.eclipse.core.runtime.Assert;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+class TerminalTextDataSnapshot implements ITerminalTextDataSnapshot {
+ // The changes of the current snapshot relative to the previous snapshot.
+ private volatile ISnapshotChanges currentChanges;
+
+ // Keeps track of changes that happened since the current snapshot has been made.
+ private ISnapshotChanges futureChanges;
+
+ // Is used as lock and is the reference to the terminal we take snapshots from.
+ private final TerminalTextData terminal;
+
+ // A snapshot copy of terminal. It does not need internal synchronization.
+ private final TerminalTextDataWindow snapshot;
+
+ private final List<SnapshotOutOfDateListener> listeners = new ArrayList<SnapshotOutOfDateListener>();
+
+ private boolean listenersNeedNotify;
+ private int interestWindowSize;
+ private int interestWindowStartLine;
+
+ TerminalTextDataSnapshot(TerminalTextData terminal) {
+ snapshot = new TerminalTextDataWindow();
+ this.terminal = terminal;
+ currentChanges = new SnapshotChanges(this.terminal.getHeight());
+ currentChanges.setTerminalChanged();
+ futureChanges = new SnapshotChanges(this.terminal.getHeight());
+ futureChanges.markLinesChanged(0, this.terminal.getHeight());
+ listenersNeedNotify = true;
+ interestWindowSize = -1;
+ }
+
+ @Override public void detach() {
+ terminal.removeSnapshot(this);
+ }
+
+ @Override public boolean isOutOfDate() {
+ // This is called from terminal, therefore we lock on terminal
+ synchronized (terminal) {
+ return futureChanges.hasChanged();
+ }
+ }
+
+ @Override public void updateSnapshot(boolean detectScrolling) {
+ // Make sure terminal does not change while we make the snapshot.
+ synchronized (terminal) {
+ // let's make the future changes current
+ currentChanges = futureChanges;
+ futureChanges = new SnapshotChanges(terminal.getHeight());
+ futureChanges.setInterestWindow(interestWindowStartLine, interestWindowSize);
+ // and update the snapshot
+ if (snapshot.getHeight() != terminal.getHeight() || snapshot.getWidth() != terminal.getWidth()) {
+ if (interestWindowSize == -1) {
+ snapshot.setWindow(0, terminal.getHeight());
+ }
+ // if the dimensions have changed, we need a full copy.
+ snapshot.copy(terminal);
+ // and we mark all lines as changed.
+ currentChanges.setAllChanged(terminal.getHeight());
+ } else {
+ // first we do the scroll on the copy
+ int start = currentChanges.getScrollWindowStartLine();
+ int lines = Math.min(currentChanges.getScrollWindowSize(), snapshot.getHeight() - start);
+ snapshot.scroll(start, lines, currentChanges.getScrollWindowShift());
+ // and then create the snapshot of the changed lines.
+ currentChanges.copyChangedLines(snapshot, terminal);
+ }
+ listenersNeedNotify = true;
+ snapshot.setCursorLine(terminal.getCursorLine());
+ snapshot.setCursorColumn(terminal.getCursorColumn());
+ }
+ if (!detectScrolling) {
+ // let's pretend there was no scrolling and convert the scrolling into line changes
+ currentChanges.convertScrollingIntoChanges();
+ }
+ }
+
+ @Override public char getChar(int line, int column) {
+ return snapshot.getChar(line, column);
+ }
+
+ @Override public int getHeight() {
+ return snapshot.getHeight();
+ }
+
+ @Override public LineSegment[] getLineSegments(int line, int column, int len) {
+ return snapshot.getLineSegments(line, column, len);
+ }
+
+ @Override public Style getStyle(int line, int column) {
+ return snapshot.getStyle(line, column);
+ }
+
+ @Override public int getWidth() {
+ return snapshot.getWidth();
+ }
+
+ @Override public int getFirstChangedLine() {
+ return currentChanges.getFirstChangedLine();
+ }
+
+ @Override public int getLastChangedLine() {
+ return currentChanges.getLastChangedLine();
+ }
+
+ @Override public boolean hasLineChanged(int line) {
+ return currentChanges.hasLineChanged(line);
+ }
+
+ @Override public boolean hasDimensionsChanged() {
+ return currentChanges.hasDimensionsChanged();
+ }
+
+ @Override public boolean hasTerminalChanged() {
+ return currentChanges.hasTerminalChanged();
+ }
+
+ @Override public int getScrollWindowStartLine() {
+ return currentChanges.getScrollWindowStartLine();
+ }
+
+ @Override public int getScrollWindowSize() {
+ return currentChanges.getScrollWindowSize();
+ }
+
+ @Override public int getScrollWindowShift() {
+ return currentChanges.getScrollWindowShift();
+ }
+
+ void markLineChanged(int line) {
+ futureChanges.markLineChanged(line);
+ futureChanges.setTerminalChanged();
+ notifyListers();
+ }
+
+ void markLinesChanged(int line, int size) {
+ futureChanges.markLinesChanged(line, size);
+ futureChanges.setTerminalChanged();
+ notifyListers();
+ }
+
+ void markDimensionsChanged() {
+ futureChanges.markDimensionsChanged();
+ futureChanges.setTerminalChanged();
+ notifyListers();
+ }
+
+ void markCursorChanged() {
+ futureChanges.markCursorChanged();
+ futureChanges.setTerminalChanged();
+ notifyListers();
+ }
+
+ void scroll(int startLine, int size, int shift) {
+ futureChanges.scroll(startLine, size, shift);
+ futureChanges.setTerminalChanged();
+ notifyListers();
+ }
+
+ private void notifyListers() {
+ synchronized (terminal) {
+ if (listenersNeedNotify) {
+ for (SnapshotOutOfDateListener listener : listeners) {
+ listener.snapshotOutOfDate(this);
+ }
+ listenersNeedNotify = false;
+ }
+ }
+ }
+
+ @Override public ITerminalTextDataSnapshot makeSnapshot() {
+ return snapshot.makeSnapshot();
+ }
+
+ @Override synchronized public void addListener(SnapshotOutOfDateListener listener) {
+ listeners.add(listener);
+ }
+
+ @Override synchronized public void removeListener(SnapshotOutOfDateListener listener) {
+ listeners.remove(listener);
+ }
+
+ @Override public String toString() {
+ return snapshot.toString();
+ }
+
+ @Override public int getInterestWindowSize() {
+ return interestWindowSize;
+ }
+
+ @Override public int getInterestWindowStartLine() {
+ return interestWindowStartLine;
+ }
+
+ @Override public void setInterestWindow(int startLine, int size) {
+ Assert.isTrue(startLine >= 0 && size >= 0);
+ interestWindowStartLine = startLine;
+ interestWindowSize = size;
+ snapshot.setWindow(startLine, size);
+ futureChanges.setInterestWindow(startLine, size);
+ notifyListers();
+ }
+
+ @Override public char[] getChars(int line) {
+ return snapshot.getChars(line);
+ }
+
+ @Override public Style[] getStyles(int line) {
+ return snapshot.getStyles(line);
+ }
+
+ @Override public int getCursorColumn() {
+ return snapshot.getCursorColumn();
+ }
+
+ @Override public int getCursorLine() {
+ return snapshot.getCursorLine();
+ }
+
+ @Override public ITerminalTextData getTerminalTextData() {
+ return terminal;
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataStore.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataStore.java
new file mode 100644
index 0000000..dc7855f
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataStore.java
@@ -0,0 +1,295 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import static java.util.Arrays.copyOf;
+
+import java.lang.reflect.Array;
+import java.util.*;
+
+import org.eclipse.core.runtime.Assert;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+public class TerminalTextDataStore implements ITerminalTextData {
+ private char[][] chars;
+ private Style[][] styles;
+ private int width;
+ private int height;
+ private int maxHeight;
+ private int cursorColumn;
+ private int cursorLine;
+
+ public TerminalTextDataStore() {
+ chars = new char[0][];
+ styles = new Style[0][];
+ width = 0;
+ }
+
+ @Override public int getWidth() {
+ return width;
+ }
+
+ @Override public int getHeight() {
+ return height;
+ }
+
+ @Override public void setDimensions(int height, int width) {
+ Assert.isTrue(height >= 0 && width >= 0);
+ // Just extend the region.
+ if (height > chars.length) {
+ int h = 4 * height / 3;
+ if (maxHeight > 0 && h > maxHeight) {
+ h = maxHeight;
+ }
+ styles = (Style[][]) resizeArray(styles, height);
+ chars = (char[][]) resizeArray(chars, height);
+ }
+ // Clean the new lines
+ if (height > this.height) {
+ for (int i = this.height; i < height; i++) {
+ styles[i] = null;
+ chars[i] = null;
+ }
+ }
+ // Set dimensions after successful resize.
+ this.width = width;
+ this.height = height;
+ }
+
+ /**
+ * Reallocates an array with a new size, and copies the contents of the old array to the new array.
+ *
+ * @param originalArray the old array, to be reallocated.
+ * @param newSize the new array size.
+ * @return A new array with the same contents (chopped off if needed or filled with 0 or null).
+ */
+ private Object resizeArray(Object originalArray, int newSize) {
+ int oldSize = Array.getLength(originalArray);
+ if (oldSize == newSize) {
+ return originalArray;
+ }
+ Class<?> elementType = originalArray.getClass().getComponentType();
+ Object newArray = Array.newInstance(elementType, newSize);
+ int preserveLength = Math.min(oldSize, newSize);
+ if (preserveLength > 0) {
+ System.arraycopy(originalArray, 0, newArray, 0, preserveLength);
+ }
+ return newArray;
+ }
+
+ @Override public LineSegment[] getLineSegments(int line, int startingColumn, int columnCount) {
+ // get the styles and chars for this line
+ Style[] styles = this.styles[line];
+ char[] chars = this.chars[line];
+ int column = startingColumn;
+ int size = startingColumn + columnCount;
+ // Expand the line if needed
+ if (styles == null) {
+ styles = new Style[size];
+ } else if (styles.length < size) {
+ styles = (Style[]) resizeArray(styles, size);
+ }
+ if (chars == null) {
+ chars = new char[size];
+ } else if (chars.length < size) {
+ chars = (char[]) resizeArray(chars, size);
+ }
+ // Create the line segments
+ Style style = styles[startingColumn];
+ List<LineSegment> segments = new ArrayList<LineSegment>();
+ for (int i = startingColumn; i < size; i++) {
+ if (styles[i] != style) {
+ segments.add(new LineSegment(column, new String(chars, column, i - column), style));
+ style = styles[i];
+ column = i;
+ }
+ }
+ if (column < size) {
+ segments.add(new LineSegment(column, new String(chars, column, size - column), style));
+ }
+ return segments.toArray(new LineSegment[segments.size()]);
+ }
+
+ @Override public char getChar(int line, int column) {
+ Assert.isTrue(column < width);
+ if (chars[line] == null || column >= chars[line].length) {
+ return 0;
+ }
+ return chars[line][column];
+ }
+
+ @Override public Style getStyle(int line, int column) {
+ Assert.isTrue(column < width);
+ if (styles[line] == null || column >= styles[line].length) {
+ return null;
+ }
+ return styles[line][column];
+ }
+
+ void ensureLineLength(int iLine, int length) {
+ if (length > width) {
+ throw new RuntimeException();
+ }
+ if (chars[iLine] == null) {
+ chars[iLine] = new char[length];
+ } else if (chars[iLine].length < length) {
+ chars[iLine] = (char[]) resizeArray(chars[iLine], length);
+ }
+ if (styles[iLine] == null) {
+ styles[iLine] = new Style[length];
+ } else if (styles[iLine].length < length) {
+ styles[iLine] = (Style[]) resizeArray(styles[iLine], length);
+ }
+ }
+
+ @Override public void setChar(int line, int column, char c, Style style) {
+ ensureLineLength(line, column + 1);
+ chars[line][column] = c;
+ styles[line][column] = style;
+ }
+
+ @Override public void setChars(int line, int column, char[] chars, Style style) {
+ setChars(line, column, chars, 0, chars.length, style);
+ }
+
+ @Override public void setChars(int line, int column, char[] chars, int start, int len, Style style) {
+ ensureLineLength(line, column + len);
+ for (int i = 0; i < len; i++) {
+ this.chars[line][column + i] = chars[i + start];
+ this.styles[line][column + i] = style;
+ }
+ }
+
+ @Override public void scroll(int startLine, int size, int shift) {
+ Assert.isTrue(startLine + size <= getHeight());
+ if (shift < 0) {
+ // move the region up
+ for (int i = startLine; i < startLine + size + shift; i++) {
+ chars[i] = chars[i - shift];
+ styles[i] = styles[i - shift];
+ }
+ // then clean the opened lines
+ cleanLines(Math.max(startLine, startLine + size + shift), Math.min(-shift, getHeight() - startLine));
+ } else {
+ for (int i = startLine + size - 1; i >= startLine && i - shift >= 0; i--) {
+ chars[i] = chars[i - shift];
+ styles[i] = styles[i - shift];
+ }
+ cleanLines(startLine, Math.min(shift, getHeight() - startLine));
+ }
+ }
+
+ private void cleanLines(int line, int len) {
+ for (int i = line; i < line + len; i++) {
+ chars[i] = null;
+ styles[i] = null;
+ }
+ }
+
+ @Override public String toString() {
+ StringBuffer buff = new StringBuffer();
+ for (int line = 0; line < getHeight(); line++) {
+ if (line > 0) {
+ buff.append("\n");
+ }
+ for (int column = 0; column < width; column++) {
+ buff.append(getChar(line, column));
+ }
+ }
+ return buff.toString();
+ }
+
+ @Override public ITerminalTextDataSnapshot makeSnapshot() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override public void addLine() {
+ if (maxHeight > 0 && getHeight() < maxHeight) {
+ setDimensions(getHeight() + 1, getWidth());
+ } else {
+ scroll(0, getHeight(), -1);
+ }
+ }
+
+ @Override public void copy(ITerminalTextData source) {
+ width = source.getWidth();
+ int newHeight = source.getHeight();
+ if (getHeight() != newHeight) {
+ chars = new char[newHeight][];
+ styles = new Style[newHeight][];
+ }
+ for (int i = 0; i < newHeight; i++) {
+ chars[i] = source.getChars(i);
+ styles[i] = source.getStyles(i);
+ }
+ height = newHeight;
+ cursorLine = source.getCursorLine();
+ cursorColumn = source.getCursorColumn();
+ }
+
+ @Override public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine, int length) {
+ for (int i = 0; i < length; i++) {
+ chars[i + destStartLine] = source.getChars(i + sourceStartLine);
+ styles[i + destStartLine] = source.getStyles(i + sourceStartLine);
+ }
+ }
+
+ @Override public void copyLine(ITerminalTextData source, int sourceLine, int destLine) {
+ chars[destLine] = source.getChars(sourceLine);
+ styles[destLine] = source.getStyles(sourceLine);
+ }
+
+ @Override public char[] getChars(int line) {
+ if (chars[line] == null) {
+ return null;
+ }
+ return chars[line].clone();
+ }
+
+ @Override public Style[] getStyles(int line) {
+ if (styles[line] == null) {
+ return null;
+ }
+ return styles[line].clone();
+ }
+
+ public void setLine(int line, char[] chars, Style[] styles) {
+ this.chars[line] = copyOf(chars, chars.length);
+ this.styles[line] = copyOf(styles, styles.length);
+ }
+
+ @Override public void setMaxHeight(int height) {
+ maxHeight = height;
+ }
+
+ @Override public int getMaxHeight() {
+ return maxHeight;
+ }
+
+ @Override public void cleanLine(int line) {
+ chars[line] = null;
+ styles[line] = null;
+ }
+
+ @Override public int getCursorColumn() {
+ return cursorColumn;
+ }
+
+ @Override public int getCursorLine() {
+ return cursorLine;
+ }
+
+ @Override public void setCursorColumn(int column) {
+ cursorColumn = column;
+ }
+
+ @Override public void setCursorLine(int line) {
+ cursorLine = line;
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataWindow.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataWindow.java
new file mode 100644
index 0000000..54c6285
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/internal/model/TerminalTextDataWindow.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.internal.model;
+
+import org.eclipse.core.runtime.Assert;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+/**
+ * This class stores the data only within a window {@link #setWindow(int, int)} and {@link #getWindowStartLine()} and
+ * {@link #getWindowSize()}. Everything outside the is {@code char=='\000'} and {@code style=null}.
+ */
+public class TerminalTextDataWindow implements ITerminalTextData {
+ private final ITerminalTextData data;
+
+ private int windowStartLine;
+ private int windowSize;
+ private int height;
+ private int maxHeight;
+
+ public TerminalTextDataWindow(ITerminalTextData data) {
+ this.data = data;
+ }
+
+ public TerminalTextDataWindow() {
+ this(new TerminalTextDataStore());
+ }
+
+ private boolean isInWindow(int line) {
+ return line >= windowStartLine && line < windowStartLine + windowSize;
+ }
+
+ @Override public char getChar(int line, int column) {
+ if (!isInWindow(line)) {
+ return 0;
+ }
+ return data.getChar(line - windowStartLine, column);
+ }
+
+ @Override public char[] getChars(int line) {
+ if (!isInWindow(line)) {
+ return null;
+ }
+ return data.getChars(line - windowStartLine);
+ }
+
+ @Override public int getHeight() {
+ return height;
+ }
+
+ @Override public LineSegment[] getLineSegments(int line, int startingColumn, int columnCount) {
+ if (!isInWindow(line)) {
+ return new LineSegment[] { new LineSegment(startingColumn, new String(new char[columnCount]), null) };
+ }
+ return data.getLineSegments(line - windowStartLine, startingColumn, columnCount);
+ }
+
+ @Override public int getMaxHeight() {
+ return maxHeight;
+ }
+
+ @Override public Style getStyle(int line, int column) {
+ if (!isInWindow(line)) {
+ return null;
+ }
+ return data.getStyle(line - windowStartLine, column);
+ }
+
+ @Override public Style[] getStyles(int line) {
+ if (!isInWindow(line)) {
+ return null;
+ }
+ return data.getStyles(line - windowStartLine);
+ }
+
+ @Override public int getWidth() {
+ return data.getWidth();
+ }
+
+ @Override public ITerminalTextDataSnapshot makeSnapshot() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override public void addLine() {
+ if (maxHeight > 0 && getHeight() < maxHeight) {
+ setDimensions(getHeight() + 1, getWidth());
+ } else {
+ scroll(0, getHeight(), -1);
+ }
+ }
+
+ @Override public void copy(ITerminalTextData source) {
+ // We inherit the dimensions of the source.
+ setDimensions(source.getHeight(), source.getWidth());
+ int n = Math.min(windowSize, source.getHeight() - windowStartLine);
+ if (n > 0) {
+ data.copyRange(source, windowStartLine, 0, n);
+ }
+ }
+
+ @Override public void copyRange(ITerminalTextData source, int sourceStartLine, int destinationStartLine, int length) {
+ int newLength = length;
+ int destinationStart = destinationStartLine - windowStartLine;
+ int sourceStart = sourceStartLine;
+ // If start outside our range, cut the length to copy.
+ if (destinationStart < 0) {
+ newLength += destinationStart;
+ sourceStart -= destinationStart;
+ destinationStart = 0;
+ }
+ // Do not exceed the window size.
+ newLength = Math.min(newLength, windowSize);
+ if (newLength > 0) {
+ data.copyRange(source, sourceStart, destinationStart, newLength);
+ }
+ }
+
+ @Override public void copyLine(ITerminalTextData source, int sourceLine, int destLine) {
+ if (isInWindow(destLine)) {
+ data.copyLine(source, sourceLine, destLine - windowStartLine);
+ }
+ }
+
+ @Override public void scroll(int startLine, int lineCount, int shift) {
+ Assert.isTrue(startLine >= 0 && startLine + lineCount <= height);
+ int length = lineCount;
+ int start = startLine - windowStartLine;
+ // If start outside our range, cut the length to copy.
+ if (start < 0) {
+ length += start;
+ start = 0;
+ }
+ length = Math.min(length, windowSize - start);
+ // do not exceed the window size
+ if (length > 0) {
+ data.scroll(start, length, shift);
+ }
+ }
+
+ @Override public void setChar(int line, int column, char c, Style style) {
+ if (!isInWindow(line)) {
+ return;
+ }
+ data.setChar(line - windowStartLine, column, c, style);
+ }
+
+ @Override public void setChars(int line, int column, char[] chars, int start, int len, Style style) {
+ if (!isInWindow(line)) {
+ return;
+ }
+ data.setChars(line - windowStartLine, column, chars, start, len, style);
+ }
+
+ @Override public void setChars(int line, int column, char[] chars, Style style) {
+ if (!isInWindow(line)) {
+ return;
+ }
+ data.setChars(line - windowStartLine, column, chars, style);
+ }
+
+ @Override public void setDimensions(int height, int width) {
+ Assert.isTrue(height >= 0);
+ data.setDimensions(windowSize, width);
+ setHeight(height);
+ }
+
+ @Override public void setMaxHeight(int maxHeight) {
+ this.maxHeight = maxHeight;
+ }
+
+ public void setWindow(int startLine, int size) {
+ windowStartLine = startLine;
+ windowSize = size;
+ data.setDimensions(windowSize, getWidth());
+ }
+
+ public int getWindowStartLine() {
+ return windowStartLine;
+ }
+
+ public int getWindowSize() {
+ return windowSize;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ @Override public void cleanLine(int line) {
+ if (isInWindow(line)) {
+ data.cleanLine(line - windowStartLine);
+ }
+ }
+
+ @Override public int getCursorColumn() {
+ return data.getCursorColumn();
+ }
+
+ @Override public int getCursorLine() {
+ return data.getCursorLine();
+ }
+
+ @Override public void setCursorColumn(int column) {
+ data.setCursorColumn(column);
+ }
+
+ @Override public void setCursorLine(int line) {
+ data.setCursorLine(line);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextData.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextData.java
new file mode 100644
index 0000000..4c3b351
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextData.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+/**
+ * A writable matrix of characters and {@link Style}. This is intended to be the low level representation of the text of
+ * a Terminal. Higher layers are responsible to fill the text and styles into this representation.
+ */
+public interface ITerminalTextData extends ITerminalTextDataReadOnly {
+
+ void setDimensions(int height, int width);
+
+ void setMaxHeight(int height);
+
+ int getMaxHeight();
+
+ void setChar(int line, int column, char c, Style style);
+
+ void setChars(int line, int column, char[] chars, Style style);
+
+ void setChars(int line, int column, char[] chars, int start, int len, Style style);
+
+ void cleanLine(int line);
+
+ /**
+ * Shifts some lines up or down. The "empty" space is filled with {@code '\000'} chars and {@code null} {@link Style}.
+ * <p>
+ * To illustrate shift, here is some sample data:
+ * <pre>
+ * 0 aaaa
+ * 1 bbbb
+ * 2 cccc
+ * 3 dddd
+ * 4 eeee
+ * </pre>
+ *
+ * Shift a region of 3 lines <b>up</b> by one line {@code shift(1,3,-1)}
+ * <pre>
+ * 0 aaaa
+ * 1 cccc
+ * 2 dddd
+ * 3
+ * 4 eeee
+ * </pre>
+ *
+ * Shift a region of 3 lines <b>down</b> by one line {@code shift(1,3,1)}
+ * <pre>
+ * 0 aaaa
+ * 1
+ * 2 bbbb
+ * 3 cccc
+ * 4 eeee
+ * </pre>
+ *
+ * @param startLine the start line of the shift.
+ * @param size the number of lines to shift.
+ * @param shift how much scrolling is done. New scrolled area is filled with {@code '\000'}. A negative number means
+ * scroll down, positive scroll up (see example above).
+ */
+ void scroll(int startLine, int size, int shift);
+
+ /**
+ * Adds a new line to the terminal. If maxHeigth is reached, the entire terminal will be scrolled. Else a line will be
+ * added.
+ */
+ void addLine();
+
+ void copy(ITerminalTextData source);
+
+ void copyLine(ITerminalTextData source, int sourceLine, int destinationLine);
+
+ void copyRange(ITerminalTextData source, int sourceStartLine, int destinationStartLine, int length);
+
+ void setCursorLine(int line);
+
+ void setCursorColumn(int column);
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextDataReadOnly.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextDataReadOnly.java
new file mode 100644
index 0000000..aa98f42
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextDataReadOnly.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+public interface ITerminalTextDataReadOnly {
+ int getWidth();
+
+ int getHeight();
+
+ LineSegment[] getLineSegments(int line, int startCol, int numberOfCols);
+
+ char getChar(int line, int column);
+
+ Style getStyle(int line, int column);
+
+ /**
+ * Creates a new instance of {@link ITerminalTextDataSnapshot} that can be used to track changes. Make sure to call
+ * {@link ITerminalTextDataSnapshot#detach()} if you don't need the snapshots anymore.
+ * <p>
+ * <b>Note: </b>A new snapshot is empty and needs a call to {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)}
+ * to get its initial values. You might want to setup the snapshot to your needs by calling
+ * {@link ITerminalTextDataSnapshot#setInterestWindow(int, int)}.
+ * </p>
+ *
+ * @return a new instance of {@link ITerminalTextDataSnapshot} that "listens" to changes of this one.
+ */
+ public ITerminalTextDataSnapshot makeSnapshot();
+
+ char[] getChars(int line);
+
+ Style[] getStyles(int line);
+
+ int getCursorLine();
+
+ int getCursorColumn();
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextDataSnapshot.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextDataSnapshot.java
new file mode 100644
index 0000000..746e638
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/ITerminalTextDataSnapshot.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Michael Scharf (Wind River) - initial API and implementation
+ * Martin Oberhuber (Wind River) - [261486][api][cleanup] Mark @noimplement interfaces as @noextend
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+/**
+ * Maintains a snapshot of an instance of {@link ITerminalTextData}. While the {@link ITerminalTextData} continues
+ * changing, the snapshot remains unchanged until the next snapshot is taken by calling
+ * {@link #updateSnapshot(boolean)}. This is important, because the {@link ITerminalTextData} might get modified by
+ * another thread.
+ */
+public interface ITerminalTextDataSnapshot extends ITerminalTextDataReadOnly {
+ /**
+ * This listener gets called when the current snapshot is out of date. Calling
+ * {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} will have an effect. Once the
+ * {@link #snapshotOutOfDate(ITerminalTextDataSnapshot)} method is called, it will not be called until
+ * {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} is called and a new snapshot needs to be updated again.
+ */
+ interface SnapshotOutOfDateListener {
+ /**
+ * Gets called when the snapshot is out of date. To get the snapshot up to date, call
+ * {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)}.
+ *
+ * @param snapshot The snapshot that is out of date.
+ */
+ void snapshotOutOfDate(ITerminalTextDataSnapshot snapshot);
+ }
+
+ void addListener(SnapshotOutOfDateListener listener);
+
+ void removeListener(SnapshotOutOfDateListener listener);
+
+ /**
+ * Ends the listening to the {@link ITerminalTextData}. After this has been called no new snapshot data is collected.
+ */
+ void detach();
+
+ boolean isOutOfDate();
+
+ /**
+ * The window of interest is the region the snapshot should track. Changes outside this region are ignored. The change
+ * takes effect after an update.
+ * @param startLine -1 means track the end of the data.
+ * @param size number of lines to track. A size of -1 means track all.
+ */
+ void setInterestWindow(int startLine, int size);
+
+ int getInterestWindowStartLine();
+
+ int getInterestWindowSize();
+
+ /**
+ * Create a new snapshot of the {@link ITerminalTextData}. It will efficiently copy the data of the
+ * {@link ITerminalTextData} into an internal representation. The snapshot also keeps track of the changes since the
+ * previous snapshot.
+ * <p>
+ * With the methods {@link #getFirstChangedLine()}, {@link #getLastChangedLine()} and {@link #hasLineChanged(int)} you
+ * can find out what has changed in the current snapshot since the previous snapshot.
+ * </p>
+ * <p>
+ * If {@code detectScrolling} is {@code true} the information about scrolling can be retrieved using the following methods:
+ * {@link #getScrollWindowStartLine()}, {@link #getScrollWindowSize()} and {@link #getScrollWindowShift()}.
+ * </p>
+ * <p>
+ * <b>Note:</b> The method {@link #hasLineChanged(int)} returns changes <b>after</b> the scrolling has been applied.
+ * </p>
+ *
+ * @param detectScrolling indicates whether the snapshot should try to identify scroll changes since the last
+ * snapshot.
+ */
+ void updateSnapshot(boolean detectScrolling);
+
+ /**
+ * Returns the first line changed in this snapshot compared to the previous snapshot.
+ * <p>
+ * <b>Note:</b> If no line has changed, this returns {@link Integer#MAX_VALUE}.
+ * <p>
+ * <b>Note:</b> if {@link #updateSnapshot(boolean)} has been called with <code>true</code>, then this does not include
+ * lines that only have been scrolled. This is the first line that has changed <b>after</b> the scroll has been
+ * applied.
+ *
+ * @return the first line changed in this snapshot compared to the previous snapshot.
+ */
+ int getFirstChangedLine();
+
+ /**
+ * Returns the last line changed in this snapshot compared to the previous snapshot. If the height has changed since
+ * the last update of the snapshot, then the returned value is within the new dimensions.
+ * <p>
+ * <b>Note:</b> If no line has changed, this returns <code>-1</code>
+ * <p>
+ * <b>Note:</b> if {@link #updateSnapshot(boolean)} has been called with <code>true</code>, then this does not include
+ * lines that only have been scrolled. This is the last line that has changed <b>after</b> the scroll has been
+ * applied.
+ * <p>
+ * A typical for loop using this method would look like this (note the <code><=</code> in the for loop):
+ * <pre>
+ * for (int line = snap.{@link #getFirstChangedLine()}; line <b><=</b> snap.getLastChangedLine(); line++)
+ * if(snap.{@link #hasLineChanged(int) hasLineChanged(line)})
+ * doSomething(line);
+ * </pre>
+ * @return the last line changed in this snapshot compared to the previous snapshot.
+ */
+ int getLastChangedLine();
+
+ boolean hasLineChanged(int line);
+
+ boolean hasDimensionsChanged();
+
+ boolean hasTerminalChanged();
+
+ /**
+ * If {@link #updateSnapshot(boolean)} was called with {@code true}, then this method returns the top of the scroll
+ * region.
+ *
+ * @return The first line scrolled in this snapshot compared to the previous snapshot.
+ *
+ * @see ITerminalTextData#scroll(int, int, int)
+ */
+ int getScrollWindowStartLine();
+
+ /**
+ * If {@link #updateSnapshot(boolean)} was called with {@code true}, then this method returns the size of the scroll
+ * region. If nothing has changed, 0 is returned.
+ *
+ * @return The number of lines scrolled in this snapshot compared to the previous snapshot.
+ *
+ * @see ITerminalTextData#scroll(int, int, int)
+ */
+ int getScrollWindowSize();
+
+ /**
+ * If {@link #updateSnapshot(boolean)} was called with {@code true}, then this method returns number of lines moved by
+ * the scroll region.
+ *
+ * @return The the scroll shift of this snapshot compared to the previous snapshot.
+ *
+ * @see ITerminalTextData#scroll(int, int, int)
+ */
+ int getScrollWindowShift();
+
+ ITerminalTextData getTerminalTextData();
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/LineSegment.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/LineSegment.java
new file mode 100644
index 0000000..4c61f7f
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/LineSegment.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Michael Scharf (Wind River) - initial API and implementation
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+public class LineSegment {
+ private final int column;
+ private final String text;
+ private final Style style;
+
+ public LineSegment(int column, String text, Style style) {
+ this.column = column;
+ this.text = text;
+ this.style = style;
+ }
+
+ public int getColumn() {
+ return column;
+ }
+
+ public Style getStyle() {
+ return style;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ @Override public String toString() {
+ return "LineSegment(" + column + ", \"" + text + "\"," + style + ")";
+ }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/Style.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/Style.java
new file mode 100644
index 0000000..3c0bb95
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/Style.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+import java.util.*;
+
+// TODO add an Object for user data, use weak map to keep track of styles with associated user data.
+public class Style {
+ private final StyleColor foreground;
+ private final StyleColor background;
+ private final boolean bold;
+ private final boolean blink;
+ private final boolean underline;
+ private final boolean reverse;
+
+ private static final Map<Style, Style> STYLES = new HashMap<Style, Style>();
+
+ private Style(StyleColor forground, StyleColor background, boolean bold, boolean blink, boolean underline,
+ boolean reverse) {
+ this.foreground = forground;
+ this.background = background;
+ this.bold = bold;
+ this.blink = blink;
+ this.underline = underline;
+ this.reverse = reverse;
+ }
+
+ public static Style getStyle(StyleColor forground, StyleColor background, boolean bold, boolean blink,
+ boolean underline, boolean reverse) {
+ Style style = new Style(forground, background, bold, blink, underline, reverse);
+ Style cached;
+ synchronized (STYLES) {
+ cached = STYLES.get(style);
+ if (cached == null) {
+ cached = style;
+ STYLES.put(cached, cached);
+ }
+ }
+ return cached;
+ }
+
+ public static Style getStyle(String forground, String background) {
+ return getStyle(
+ StyleColor.getStyleColor(forground), StyleColor.getStyleColor(background), false, false, false, false);
+ }
+
+ public static Style getStyle(StyleColor forground, StyleColor background) {
+ return getStyle(forground, background, false, false, false, false);
+ }
+
+ public Style setForground(StyleColor forground) {
+ return getStyle(forground, background, bold, blink, underline, reverse);
+ }
+
+ public Style setBackground(StyleColor background) {
+ return getStyle(foreground, background, bold, blink, underline, reverse);
+ }
+
+ public Style setForground(String colorName) {
+ return getStyle(StyleColor.getStyleColor(colorName), background, bold, blink, underline, reverse);
+ }
+
+ public Style setBackground(String colorName) {
+ return getStyle(foreground, StyleColor.getStyleColor(colorName), bold, blink, underline, reverse);
+ }
+
+ public Style setBold(boolean bold) {
+ return getStyle(foreground, background, bold, blink, underline, reverse);
+ }
+
+ public Style setBlink(boolean blink) {
+ return getStyle(foreground, background, bold, blink, underline, reverse);
+ }
+
+ public Style setUnderline(boolean underline) {
+ return getStyle(foreground, background, bold, blink, underline, reverse);
+ }
+
+ public Style setReverse(boolean reverse) {
+ return getStyle(foreground, background, bold, blink, underline, reverse);
+ }
+
+ public StyleColor getBackground() {
+ return background;
+ }
+
+ public boolean isBlink() {
+ return blink;
+ }
+
+ public boolean isBold() {
+ return bold;
+ }
+
+ public StyleColor getForeground() {
+ return foreground;
+ }
+
+ public boolean isReverse() {
+ return reverse;
+ }
+
+ public boolean isUnderline() {
+ return underline;
+ }
+
+ @Override public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((background == null) ? 0 : background.hashCode());
+ result = prime * result + (blink ? 1231 : 1237);
+ result = prime * result + (bold ? 1231 : 1237);
+ result = prime * result + ((foreground == null) ? 0 : foreground.hashCode());
+ result = prime * result + (reverse ? 1231 : 1237);
+ result = prime * result + (underline ? 1231 : 1237);
+ return result;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final Style other = (Style) obj;
+ if (background != other.background) {
+ return false;
+ }
+ if (blink != other.blink) {
+ return false;
+ }
+ if (bold != other.bold) {
+ return false;
+ }
+ if (foreground != other.foreground) {
+ return false;
+ }
+ if (reverse != other.reverse) {
+ return false;
+ }
+ return underline == other.underline;
+ }
+
+ @Override public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Style [foreground=");
+ builder.append(foreground);
+ builder.append(", background=");
+ builder.append(background);
+ builder.append(", bold=");
+ builder.append(bold);
+ builder.append(", blink=");
+ builder.append(blink);
+ builder.append(", underline=");
+ builder.append(underline);
+ builder.append(", reverse=");
+ builder.append(reverse);
+ builder.append("]");
+ return builder.toString();
+ }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/StyleColor.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/StyleColor.java
new file mode 100644
index 0000000..5748b28
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/StyleColor.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+import java.util.concurrent.*;
+
+public class StyleColor {
+ private static final ConcurrentMap<String, StyleColor> STYLE_COLORS = new ConcurrentHashMap<String, StyleColor>();
+
+ private final String name;
+
+ public static StyleColor getStyleColor(String name) {
+ StyleColor result = new StyleColor(name);
+ return STYLE_COLORS.putIfAbsent(name, result);
+ }
+
+ private StyleColor(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override public String toString() {
+ return name;
+ }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/TerminalTextDataFactory.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/TerminalTextDataFactory.java
new file mode 100644
index 0000000..a6f8b2c
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/model/TerminalTextDataFactory.java
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.model;
+
+import com.google.eclipse.elt.emulator.internal.model.*;
+
+public class TerminalTextDataFactory {
+ public static ITerminalTextData makeTerminalTextData() {
+ return new SynchronizedTerminalTextData(new TerminalTextData());
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/ITerminalConnector.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/ITerminalConnector.java
new file mode 100644
index 0000000..1668177
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/ITerminalConnector.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.provisional.api;
+
+import java.io.OutputStream;
+
+import org.eclipse.core.runtime.IAdaptable;
+
+/**
+ * A connection type.
+ *
+ * @author Michael Scharf
+ */
+public interface ITerminalConnector extends IAdaptable {
+ String getId();
+
+ String getName();
+
+ boolean isInitialized();
+
+ String getInitializationErrorMessage();
+
+ void connect(ITerminalControl control);
+
+ void disconnect();
+
+ boolean isLocalEcho();
+
+ void setTerminalSize(int newWidth, int newHeight);
+
+ OutputStream getTerminalToRemoteStream();
+
+ String getSettingsSummary();
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/ITerminalControl.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/ITerminalControl.java
new file mode 100644
index 0000000..a9b00ce
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/ITerminalControl.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.provisional.api;
+
+import java.io.*;
+
+import org.eclipse.swt.widgets.*;
+
+/**
+ * Represents the terminal view as seen by a terminal connection.
+ *
+ * @author Michael Scharf
+ */
+public interface ITerminalControl {
+ TerminalState getState();
+
+ void setState(TerminalState state);
+
+ void setUpTerminal(Composite parent);
+
+ Shell getShell();
+
+ /**
+ * Set the encoding that the terminal uses to decode bytes from the Terminal-to-remote-Stream into Unicode characters
+ * used in Java; or, to encode characters typed by the user into bytes sent over the wire to the remote.
+ * <p>
+ * By default, the local platform default encoding is used. Also note that the encoding must not be applied in case
+ * the terminal stream is processed by some data transfer protocol which requires binary data.
+ * </p>
+ * <p>
+ * Validity of the encoding set here is not checked. Since some encodings do not cover the entire range of Unicode
+ * characters, it can happen that a particular Unicode text typed in by the user can not be encoded into a byte stream
+ * with the encoding specified. and {@link UnsupportedEncodingException} will be thrown in this case at the time the
+ * text is about to be processed.
+ * </p>
+ * <p>
+ * The concrete encoding to use can either be specified manually by a user, by means of a dialog, or a connector can
+ * try to obtain it automatically from the remote side (e.g. by evaluating an environment variable such as LANG on
+ * UNIX systems.)
+ * </p>
+ *
+ * @param encoding the new encoding.
+ * @throws UnsupportedEncodingException if the given encoding is not supported.
+ */
+ void setEncoding(String encoding) throws UnsupportedEncodingException;
+
+ String getEncoding();
+
+ /**
+ * Show a text in the terminal. If puts newlines at the beginning and the end.
+ *
+ * @param text the text to display.
+ */
+ void displayTextInTerminal(String text);
+
+ /**
+ * Returns the stream used to write to the terminal. Any bytes written to this stream appear in the terminal or are
+ * interpreted by the emulator as control sequences. The stream in the opposite direction, terminal to remote is in
+ * {@link ITerminalConnector#getTerminalToRemoteStream()}.
+ *
+ * @return the stream used to write to the terminal.
+ */
+ OutputStream getRemoteToTerminalOutputStream();
+
+ void setTerminalTitle(String title);
+
+ /**
+ * Show an error message during connect.
+ * @param errorMessage the new error message.
+ */
+ // TODO(Michael Scharf): Should be replaced by a better error notification mechanism!
+ void setErrorMessage(String errorMessage);
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/Logger.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/Logger.java
new file mode 100644
index 0000000..c4d8976
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/Logger.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.provisional.api;
+
+import static org.eclipse.core.runtime.IStatus.*;
+
+import java.io.*;
+
+import org.eclipse.core.runtime.Status;
+
+import com.google.eclipse.elt.emulator.impl.TerminalPlugin;
+
+/**
+ * A simple logger class. Every method in this class is static, so they can be called from both class and instance
+ * methods. To use this class, write code like this:
+ * <p>
+ *
+ * <pre>
+ * Logger.log("something has happened");
+ * Logger.log("counter is " + counter);
+ * </pre>
+ *
+ * @author Fran Litterio <francis.litterio@windriver.com>
+ */
+public final class Logger {
+ public static final String TRACE_DEBUG_LOG = "org.eclipse.tm.terminal/debug/log";
+ public static final String TRACE_DEBUG_LOG_ERROR = "org.eclipse.tm.terminal/debug/log/error";
+ public static final String TRACE_DEBUG_LOG_INFO = "org.eclipse.tm.terminal/debug/log/info";
+ public static final String TRACE_DEBUG_LOG_CHAR = "org.eclipse.tm.terminal/debug/log/char";
+ public static final String TRACE_DEBUG_LOG_BUFFER_SIZE = "org.eclipse.tm.terminal/debug/log/buffer/size";
+
+ private static PrintStream logStream;
+
+ static {
+ String logFile = logFile();
+ if (logFile != null) {
+ try {
+ logStream = new PrintStream(new FileOutputStream(logFile, true));
+ } catch (Exception ex) {
+ logStream = System.err;
+ logStream.println("Exception when opening log file -- logging to stderr!");
+ ex.printStackTrace(logStream);
+ }
+ }
+ }
+
+ private static String logFile() {
+ File directory = new File("C:\\eclipselogs");
+ if (directory.isDirectory()) {
+ return directory + "\\tmterminal.log";
+ }
+ directory = new File("/tmp/eclipselogs");
+ if (directory.isDirectory()) {
+ return directory + "/tmterminal.log";
+ }
+ return null;
+ }
+
+ /**
+ * Encodes text such that non-printable control characters are converted into user-readable escape sequences for
+ * logging.
+ * @param message the text to encode
+ * @return encoded the encoded text;
+ */
+ public static final String encode(String message) {
+ boolean encoded = false;
+ StringBuilder buffer = new StringBuilder(message.length() + 32);
+ for (int i = 0; i < message.length(); i++) {
+ char c = message.charAt(i);
+ switch (c) {
+ case '\\':
+ case '\'':
+ buffer.append('\\').append(c);
+ encoded = true;
+ break;
+ case '\r':
+ buffer.append('\\').append('r');
+ encoded = true;
+ break;
+ case '\n':
+ buffer.append('\\').append('n');
+ encoded = true;
+ break;
+ case '\t':
+ buffer.append('\\').append('t');
+ encoded = true;
+ break;
+ case '\f':
+ buffer.append('\\').append('f');
+ encoded = true;
+ break;
+ case '\b':
+ buffer.append('\\').append('b');
+ encoded = true;
+ break;
+ default:
+ if (c <= '\u000f') {
+ buffer.append('\\').append('x').append('0').append(Integer.toHexString(c));
+ encoded = true;
+ } else if (c >= ' ' && c < '\u007f') {
+ buffer.append(c);
+ } else if (c <= '\u00ff') {
+ buffer.append('\\').append('x').append(Integer.toHexString(c));
+ encoded = true;
+ } else {
+ buffer.append('\\').append('u');
+ if (c <= '\u0fff') {
+ buffer.append('0');
+ }
+ buffer.append(Integer.toHexString(c));
+ encoded = true;
+ }
+ }
+ }
+ if (encoded) {
+ return buffer.toString();
+ }
+ return message;
+ }
+
+ public static final boolean isLogEnabled() {
+ return (logStream != null);
+ }
+
+ public static final void log(String message) {
+ if (logStream != null) {
+ StackTraceElement caller = Thread.currentThread().getStackTrace()[1];
+ String className = caller.getClassName();
+ className = className.substring(className.lastIndexOf('.') + 1);
+ logStream.println(className + "." + caller.getMethodName() + ":" + caller.getLineNumber() + ": " + message);
+ logStream.flush();
+ }
+ }
+
+ public static final void logException(Exception e) {
+ if (TerminalPlugin.getDefault() != null) {
+ TerminalPlugin.getDefault().getLog().log(new Status(ERROR, TerminalPlugin.PLUGIN_ID, OK, e.getMessage(), e));
+ } else {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/TerminalState.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/TerminalState.java
new file mode 100644
index 0000000..7a347e2
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/TerminalState.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.provisional.api;
+
+/**
+ * Represent the sate of a terminal connection.
+ *
+ * @author Michael Scharf
+ */
+public enum TerminalState {
+ /**
+ * The terminal is not connected.
+ */
+ CLOSED("CLOSED"), CONNECTING("CONNECTING..."), CONNECTED("CONNECTED");
+
+ private final String state;
+
+ private TerminalState(String state) {
+ this.state = state;
+ }
+
+ @Override public String toString() {
+ return state;
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/provider/TerminalConnectorDelegate.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/provider/TerminalConnectorDelegate.java
new file mode 100644
index 0000000..d47b593
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/provisional/api/provider/TerminalConnectorDelegate.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Michael Scharf (Wind River) - initial API and implementation
+ * Martin Oberhuber (Wind River) - [225853][api] Provide more default functionality in TerminalConnectorImpl
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.provisional.api.provider;
+
+import static com.google.eclipse.elt.emulator.provisional.api.TerminalState.CLOSED;
+
+import java.io.OutputStream;
+
+import com.google.eclipse.elt.emulator.provisional.api.ITerminalControl;
+
+/**
+ * Abstract base class for all terminal connector implementations.
+ *
+ * @since org.eclipse.tm.terminal 2.0
+ */
+public abstract class TerminalConnectorDelegate {
+ // The TerminalControl associated with this connector. Required for advertising state changes when needed.
+ protected ITerminalControl terminalControl;
+
+ /**
+ * Initialize this connector. This is called once after the constructor, in order to perform any required
+ * initializations such as loading required native libraries. Any work that may lead to runtime exceptions should be
+ * done in this method rather than in the constructor.
+ *
+ * @throws Exception when the connector fails to initialize (due to missing required libraries, for instance).
+ */
+ public void initialize() throws Exception {}
+
+ public final void connect(ITerminalControl control) {
+ this.terminalControl = control;
+ connect();
+ }
+
+ protected abstract void connect();
+
+ public final void disconnect() {
+ onDisconnect();
+ terminalControl.setState(CLOSED);
+ }
+
+ protected void onDisconnect() {}
+
+ /**
+ * Returns the terminal-to-remote stream (bytes written to this stream will be sent to the remote site). For the
+ * stream in the other direction (remote to terminal see {@link ITerminalControl#getRemoteToTerminalOutputStream()}.
+ *
+ * @return the terminal-to-remote stream.
+ */
+ public abstract OutputStream getTerminalToRemoteStream();
+
+ /**
+ * @return A string that represents the settings of the connection. This representation may be shown in the status
+ * line of the terminal view.
+ */
+ public abstract String getSettingsSummary();
+
+ /**
+ * Tests if local echo is needed. The default implementation returns {@code false}.
+ *
+ * @return {@code false} by default.
+ */
+ public boolean isLocalEcho() {
+ return false;
+ }
+
+ /**
+ * Notifies the remote site that the size of the terminal has changed.
+ *
+ * @param width the new width in characters.
+ * @param height the new height in characters.
+ */
+ public void setTerminalSize(int width, int height) {}
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/AbstractTextCanvasModel.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/AbstractTextCanvasModel.java
new file mode 100644
index 0000000..ad7697c
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/AbstractTextCanvasModel.java
@@ -0,0 +1,353 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import java.util.*;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.swt.graphics.Point;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+public abstract class AbstractTextCanvasModel implements ITextCanvasModel {
+ private final List<ITextCanvasModelListener> listeners = new ArrayList<ITextCanvasModelListener>();
+ private final Point selectionAnchor = new Point(0, 0);
+
+ private final ITerminalTextDataSnapshot snapshot;
+
+ private int cursorLine;
+ private int cursorColumn;
+ private boolean showCursor;
+ private long cursorTime;
+ private boolean cursorIsEnabled;
+ private int lines;
+
+ private int selectionStartLine = -1;
+ private int seletionEndLine;
+ private int selectionStartCoumn;
+ private int selectionEndColumn;
+ private ITerminalTextDataSnapshot selectionSnapshot;
+ private String currentSelection = "";
+
+ // do not update while update is running
+ boolean inUpdate;
+
+ private int columns;
+ private boolean useBlinkingCursor;
+
+ public AbstractTextCanvasModel(ITerminalTextDataSnapshot snapshot) {
+ this.snapshot = snapshot;
+ lines = this.snapshot.getHeight();
+ }
+
+ @Override public void addCellCanvasModelListener(ITextCanvasModelListener listener) {
+ listeners.add(listener);
+ }
+
+ @Override public void removeCellCanvasModelListener(ITextCanvasModelListener listener) {
+ listeners.remove(listener);
+ }
+
+ private void fireCellRangeChanged(int x, int y, int width, int height) {
+ for (ITextCanvasModelListener listener : listeners) {
+ listener.rangeChanged(x, y, width, height);
+ }
+ }
+
+ private void fireDimensionsChanged(int width, int height) {
+ for (ITextCanvasModelListener listener : listeners) {
+ listener.dimensionsChanged(width, height);
+ }
+ }
+
+ private void fireTerminalDataChanged() {
+ for (ITextCanvasModelListener listener : listeners) {
+ listener.terminalDataChanged();
+ }
+ }
+
+ @Override public ITerminalTextDataReadOnly getTerminalText() {
+ return snapshot;
+ }
+
+ protected ITerminalTextDataSnapshot getSnapshot() {
+ return snapshot;
+ }
+
+ private void updateSnapshot() {
+ if (!inUpdate && snapshot.isOutOfDate()) {
+ inUpdate = true;
+ try {
+ snapshot.updateSnapshot(false);
+ if (snapshot.hasTerminalChanged()) {
+ fireTerminalDataChanged();
+ }
+ // TODO why does hasDimensionsChanged not work?
+ // if (snapshot.hasDimensionsChanged()) fireDimensionsChanged();
+ if (lines != snapshot.getHeight() || columns != snapshot.getWidth()) {
+ fireDimensionsChanged(snapshot.getWidth(), snapshot.getHeight());
+ lines = snapshot.getHeight();
+ columns = snapshot.getWidth();
+ }
+ int y = snapshot.getFirstChangedLine();
+ // has any line changed?
+ if (y < Integer.MAX_VALUE) {
+ int height = snapshot.getLastChangedLine() - y + 1;
+ fireCellRangeChanged(0, y, snapshot.getWidth(), height);
+ }
+ } finally {
+ inUpdate = false;
+ }
+ }
+ }
+
+ /**
+ * This method must be called from the UI thread.
+ */
+ public void update() {
+ updateSnapshot();
+ updateSelection();
+ updateCursor();
+ }
+
+ @Override public int getCursorColumn() {
+ return cursorColumn;
+ }
+
+ @Override public int getCursorLine() {
+ return cursorLine;
+ }
+
+ @Override public boolean isCursorOn() {
+ return showCursor && cursorIsEnabled;
+ }
+
+ @Override public void setBlinkingCursor(boolean useBlinkingCursor) {
+ this.useBlinkingCursor = useBlinkingCursor;
+ updateCursor();
+ }
+
+ // TODO: should be called regularly to draw an update of the blinking cursor?
+ private void updateCursor() {
+ if (!cursorIsEnabled) {
+ return;
+ }
+ int cursorLine = getSnapshot().getCursorLine();
+ int cursorColumn = getSnapshot().getCursorColumn();
+ // If cursor at the end put it to the end of the last line.
+ if (cursorLine >= getSnapshot().getHeight()) {
+ cursorLine = getSnapshot().getHeight() - 1;
+ cursorColumn = getSnapshot().getWidth() - 1;
+ }
+ // Has the cursor moved?
+ if (this.cursorLine != cursorLine || this.cursorColumn != cursorColumn) {
+ // Hide the old cursor!
+ showCursor = false;
+ // Clean the previous cursor. Bug 206363: paint also the char to the left and right of the cursor
+ int col = this.cursorColumn;
+ int width = 2;
+ if (col > 0) {
+ col--;
+ width++;
+ }
+ fireCellRangeChanged(col, this.cursorLine, width, 1);
+ // The cursor is shown when it moves.
+ showCursor = true;
+ cursorTime = System.currentTimeMillis();
+ this.cursorLine = cursorLine;
+ this.cursorColumn = cursorColumn;
+ // Draw the new cursor
+ fireCellRangeChanged(this.cursorColumn, this.cursorLine, 1, 1);
+ } else {
+ if (useBlinkingCursor) {
+ long time = System.currentTimeMillis();
+ // TODO Make the cursor blink time customizable.
+ if (time - cursorTime > 500) {
+ showCursor = !showCursor;
+ cursorTime = time;
+ // On some windows machines, there is some leftover when updating the cursor.
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206363
+ int col = this.cursorColumn;
+ int width = 2;
+ if (col > 0) {
+ col--;
+ width++;
+ }
+ fireCellRangeChanged(col, this.cursorLine, width, 1);
+ }
+ }
+ }
+ }
+
+ @Override public void setVisibleRectangle(int startLine, int startCol, int height, int width) {
+ snapshot.setInterestWindow(Math.max(0, startLine), Math.max(1, height));
+ update();
+ }
+
+ protected void showCursor(boolean show) {
+ showCursor = true;
+ }
+
+ @Override public void setCursorEnabled(boolean visible) {
+ cursorTime = System.currentTimeMillis();
+ showCursor = visible;
+ cursorIsEnabled = visible;
+ fireCellRangeChanged(cursorColumn, cursorLine, 1, 1);
+ }
+
+ @Override public boolean isCursorEnabled() {
+ return cursorIsEnabled;
+ }
+
+ @Override public Point getSelectionEnd() {
+ if (selectionStartLine < 0) {
+ return null;
+ }
+ return new Point(selectionEndColumn, seletionEndLine);
+ }
+
+ @Override public Point getSelectionStart() {
+ if (selectionStartLine < 0) {
+ return null;
+ }
+ return new Point(selectionStartCoumn, selectionStartLine);
+ }
+
+ @Override public Point getSelectionAnchor() {
+ if (selectionStartLine < 0) {
+ return null;
+ }
+ return new Point(selectionAnchor.x, selectionAnchor.y);
+ }
+
+ @Override public void setSelectionAnchor(Point anchor) {
+ selectionAnchor.x = anchor.x;
+ selectionAnchor.y = anchor.y;
+ }
+
+ @Override public void setSelection(int startLine, int endLine, int startColumn, int endColumn) {
+ doSetSelection(startLine, endLine, startColumn, endColumn);
+ currentSelection = extractSelectedText();
+ }
+
+ private void doSetSelection(int startLine, int endLine, int startColumn, int endColumn) {
+ Assert.isTrue(startLine < 0 || startLine <= endLine);
+ if (startLine >= 0) {
+ if (selectionSnapshot == null) {
+ selectionSnapshot = snapshot.getTerminalTextData().makeSnapshot();
+ selectionSnapshot.updateSnapshot(true);
+ }
+ } else if (selectionSnapshot != null) {
+ selectionSnapshot.detach();
+ selectionSnapshot = null;
+ }
+ int oldStart = selectionStartLine;
+ int oldEnd = seletionEndLine;
+ selectionStartLine = startLine;
+ seletionEndLine = endLine;
+ selectionStartCoumn = startColumn;
+ selectionEndColumn = endColumn;
+ if (selectionSnapshot != null) {
+ selectionSnapshot.setInterestWindow(0, selectionSnapshot.getHeight());
+ }
+ int changedStart;
+ int changedEnd;
+ if (oldStart < 0) {
+ changedStart = selectionStartLine;
+ changedEnd = seletionEndLine;
+ } else if (selectionStartLine < 0) {
+ changedStart = oldStart;
+ changedEnd = oldEnd;
+ } else {
+ changedStart = Math.min(oldStart, selectionStartLine);
+ changedEnd = Math.max(oldEnd, seletionEndLine);
+ }
+ if (changedStart >= 0) {
+ fireCellRangeChanged(0, changedStart, snapshot.getWidth(), changedEnd - changedStart + 1);
+ }
+ }
+
+ @Override public boolean hasLineSelection(int line) {
+ if (selectionStartLine < 0) {
+ return false;
+ }
+ return line >= selectionStartLine && line <= seletionEndLine;
+ }
+
+ @Override public String getSelectedText() {
+ return currentSelection;
+ }
+
+ /**
+ * Calculates the currently selected text
+ * @return the currently selected text
+ */
+ private String extractSelectedText() {
+ if (selectionStartLine < 0 || selectionStartCoumn < 0 || selectionSnapshot == null) {
+ return "";
+ }
+ StringBuilder buffer = new StringBuilder();
+ for (int line = selectionStartLine; line <= seletionEndLine; line++) {
+ String text;
+ char[] chars = selectionSnapshot.getChars(line);
+ if (chars != null) {
+ text = new String(chars);
+ if (line == seletionEndLine && selectionEndColumn >= 0) {
+ text = text.substring(0, Math.min(selectionEndColumn + 1, text.length()));
+ }
+ if (line == selectionStartLine) {
+ text = text.substring(Math.min(selectionStartCoumn, text.length()));
+ }
+ // get rid of the empty space at the end of the lines
+ text = text.replaceAll("\000+$","");
+ // null means space
+ text = text.replace('\000', ' ');
+ } else {
+ text = "";
+ }
+ buffer.append(text);
+ if (line < seletionEndLine) {
+ buffer.append('\n');
+ }
+ }
+ return buffer.toString();
+ }
+
+ private void updateSelection() {
+ if (selectionSnapshot != null && selectionSnapshot.isOutOfDate()) {
+ selectionSnapshot.updateSnapshot(true);
+ // Has the selection moved?
+ if (selectionSnapshot != null && selectionStartLine >= 0 && selectionSnapshot.getScrollWindowSize() > 0) {
+ int start = selectionStartLine + selectionSnapshot.getScrollWindowShift();
+ int end = seletionEndLine + selectionSnapshot.getScrollWindowShift();
+ if (start < 0) {
+ if (end >= 0) {
+ start = 0;
+ } else {
+ start = -1;
+ }
+ }
+ doSetSelection(start, end, selectionStartCoumn, selectionEndColumn);
+ }
+ // Check if the content of the selection has changed. If the content has changed, clear the selection.
+ if (currentSelection.length() > 0 && selectionSnapshot != null
+ && selectionSnapshot.getFirstChangedLine() <= seletionEndLine
+ && selectionSnapshot.getLastChangedLine() >= selectionStartLine) {
+ // Has the selected text changed?
+ if (!currentSelection.equals(extractSelectedText())) {
+ setSelection(-1, -1, -1, -1);
+ }
+ }
+ // Update the observed window...
+ if (selectionSnapshot != null) {
+ // TODO make -1 to work!
+ selectionSnapshot.setInterestWindow(0, selectionSnapshot.getHeight());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/GridCanvas.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/GridCanvas.java
new file mode 100644
index 0000000..b964b8a
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/GridCanvas.java
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * A Grid based Canvas. The canvas has rows and columns. CellPainting is done with the abstract method drawCell
+ */
+public abstract class GridCanvas extends VirtualCanvas {
+ private int cellWidth;
+ private int cellHeight;
+
+ public GridCanvas(Composite parent, int style) {
+ super(parent, style);
+ addListener(SWT.MouseWheel, new Listener() {
+ @Override public void handleEvent(Event event) {
+ if (getVerticalBar().isVisible()) {
+ int delta = -cellHeight;
+ if (event.count < 0) {
+ delta = -delta;
+ }
+ scrollYDelta(delta);
+ }
+ event.doit = false;
+ }
+ });
+ }
+
+ @Override protected void paint(GC gc) {
+ Rectangle clipping = gc.getClipping();
+ if (clipping.width == 0 || clipping.height == 0) {
+ return;
+ }
+ Rectangle clientArea = getScreenRectInVirtualSpace();
+ // Beginning coordinates.
+ int xOffset = clientArea.x;
+ int yOffset = clientArea.y;
+ int colFirst = virtualXToCell(xOffset + clipping.x);
+ if (colFirst > getCols()) {
+ colFirst = getCols();
+ } else if (colFirst < 0) {
+ colFirst = 0;
+ }
+ int rowFirst = virtualYToCell(yOffset + clipping.y);
+ // End coordinates.
+ int colLast = virtualXToCell(xOffset + clipping.x + clipping.width + cellWidth);
+ if (colLast > getCols()) {
+ colLast = getCols();
+ }
+ int rowLast = virtualYToCell(yOffset + clipping.y + clipping.height + cellHeight);
+ if (rowLast > getRows()) {
+ rowLast = getRows();
+ }
+ // Draw the cells.
+ for (int row = rowFirst; row <= rowLast; row++) {
+ int cx = colFirst * cellWidth - xOffset;
+ int cy = row * cellHeight - yOffset;
+ drawLine(gc, row, cx, cy, colFirst, colLast);
+ }
+ paintUnoccupiedSpace(gc, clipping);
+ }
+
+ abstract void drawLine(GC gc, int row, int x, int y, int colFirst, int colLast);
+
+ abstract protected int getRows();
+
+ abstract protected int getCols();
+
+ protected void setCellWidth(int cellWidth) {
+ this.cellWidth = cellWidth;
+ getHorizontalBar().setIncrement(this.cellWidth);
+ }
+
+ public int getCellWidth() {
+ return cellWidth;
+ }
+
+ protected void setCellHeight(int cellHeight) {
+ this.cellHeight = cellHeight;
+ getVerticalBar().setIncrement(this.cellHeight);
+ }
+
+ public int getCellHeight() {
+ return cellHeight;
+ }
+
+ int virtualXToCell(int x) {
+ return x / cellWidth;
+ }
+
+ int virtualYToCell(int y) {
+ return y / cellHeight;
+ }
+
+ protected Point screenPointToCell(int x, int y) {
+ x = screenXtoVirtual(x) / cellWidth;
+ y = screenYtoVirtual(y) / cellHeight;
+ return new Point(x, y);
+ }
+
+ Point screenPointToCell(Point point) {
+ return screenPointToCell(point.x, point.y);
+ }
+
+ protected Point cellToOriginOnScreen(int x, int y) {
+ x = virtualXtoScreen(cellWidth * x);
+ y = virtualYtoScreen(cellHeight * y);
+ return new Point(x, y);
+ }
+
+ Point cellToOriginOnScreen(Point cell) {
+ return cellToOriginOnScreen(cell.x, cell.y);
+ }
+
+ Rectangle getCellScreenRect(Point cell) {
+ return getCellScreenRect(cell.x, cell.y);
+ }
+
+ Rectangle getCellScreenRect(int x, int y) {
+ x = cellWidth * virtualXtoScreen(x);
+ y = cellHeight * virtualYtoScreen(y);
+ return new Rectangle(x, y, cellWidth, cellHeight);
+ }
+
+ protected Rectangle getCellVirtualRect(Point cell) {
+ return getCellVirtualRect(cell.x, cell.y);
+ }
+
+ Rectangle getCellVirtualRect(int x, int y) {
+ x = cellWidth * x;
+ y = cellHeight * y;
+ return new Rectangle(x, y, cellWidth, cellHeight);
+ }
+
+ @Override protected void viewRectangleChanged(int x, int y, int width, int height) {
+ int cellX = virtualXToCell(x);
+ int cellY = virtualYToCell(y);
+ // End coordinates
+ int xE = virtualXToCell(x + width);
+ // if(xE>getCols())
+ // xE=getCols();
+ int yE = virtualYToCell(y + height);
+ // if(yE>getRows())
+ // yE=getRows();
+ visibleCellRectangleChanged(cellX, cellY, xE - cellX, yE - cellY);
+ }
+
+ protected void visibleCellRectangleChanged(int x, int y, int width, int height) {}
+
+ @Override protected void setVirtualExtend(int width, int height) {
+ int cellHeight = getCellHeight();
+ if (cellHeight > 0) {
+ height -= height % cellHeight;
+ }
+ super.setVirtualExtend(width, height);
+ }
+
+ @Override protected void setVirtualOrigin(int x, int y) {
+ int cellHeight = getCellHeight();
+ if (cellHeight > 0) {
+ int remainder = y % cellHeight;
+ if (remainder < 0) {
+ y -= (cellHeight + remainder);
+ } else {
+ y -= remainder;
+ }
+ }
+ super.setVirtualOrigin(x, y);
+ }
+
+ @Override protected void scrollY(ScrollBar vBar) {
+ int vSelection = vBar.getSelection();
+ Rectangle bounds = getVirtualBounds();
+ int y = -vSelection;
+ int cellHeight = getCellHeight();
+ if (cellHeight > 0) {
+ int remainder = y % cellHeight;
+ if (remainder < 0) {
+ y -= (cellHeight + remainder);
+ } else {
+ y -= remainder;
+ }
+ }
+ int deltaY = y - bounds.y;
+ if (deltaY != 0) {
+ scrollSmart(0, deltaY);
+ setVirtualOrigin(bounds.x, bounds.y += deltaY);
+ }
+ if (-bounds.y + getRows() * getCellHeight() >= bounds.height) {
+ // Scrolled to bottom - need to redraw bottom area
+ Rectangle clientRect = getClientArea();
+ redraw(0, clientRect.height - this.cellHeight, clientRect.width, this.cellHeight, false);
+ }
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ILinelRenderer.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ILinelRenderer.java
new file mode 100644
index 0000000..d6f856a
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ILinelRenderer.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import org.eclipse.swt.graphics.*;
+
+public interface ILinelRenderer {
+ int getCellWidth();
+
+ int getCellHeight();
+
+ void drawLine(ITextCanvasModel model, GC gc, int line, int x, int y, int firstColumn, int lastColumn);
+
+ void onFontChange();
+
+ void setInvertedColors(boolean invert);
+
+ Color getDefaultBackgroundColor();
+
+ void setColors(RGB background, RGB foreground);
+
+ void setFont(Font font);
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ITextCanvasModel.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ITextCanvasModel.java
new file mode 100644
index 0000000..e2aa233
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ITextCanvasModel.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import org.eclipse.swt.graphics.Point;
+
+import com.google.eclipse.elt.emulator.model.ITerminalTextDataReadOnly;
+
+public interface ITextCanvasModel {
+ void addCellCanvasModelListener(ITextCanvasModelListener listener);
+
+ void removeCellCanvasModelListener(ITextCanvasModelListener listener);
+
+ ITerminalTextDataReadOnly getTerminalText();
+
+ void setVisibleRectangle(int startLine, int startCol, int height, int width);
+
+ /**
+ * Indicates whether the cursor is shown (used for blinking cursors.)
+ *
+ * @return {@code true} if the cursor is shown, {@code false} otherwise.
+ */
+ boolean isCursorOn();
+
+ boolean isCursorEnabled();
+
+ /**
+ * Show/Hide the cursor.
+ *
+ * @param visible indicates whether the cursor should be visible.
+ */
+ void setCursorEnabled(boolean visible);
+
+ int getCursorLine();
+
+ int getCursorColumn();
+
+ Point getSelectionStart();
+
+ Point getSelectionEnd();
+
+ Point getSelectionAnchor();
+
+ void setSelectionAnchor(Point anchor);
+
+ // Negative 'startLine' clears the selection.
+ void setSelection(int startLine, int endLine, int startColumn, int endColumn);
+
+ boolean hasLineSelection(int line);
+
+ String getSelectedText();
+
+ void setBlinkingCursor(boolean useBlinkingCursor);
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ITextCanvasModelListener.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ITextCanvasModelListener.java
new file mode 100644
index 0000000..0038a9a
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/ITextCanvasModelListener.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+public interface ITextCanvasModelListener {
+ void rangeChanged(int col, int line, int width, int height);
+
+ void dimensionsChanged(int cols, int rows);
+
+ /**
+ * Called when any text change happened. Used to scroll to the end of text in auto scroll mode. This does not get
+ * fired when the window of interest has changed.
+ */
+ void terminalDataChanged();
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/PipedInputStream.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/PipedInputStream.java
new file mode 100644
index 0000000..f0dbbd2
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/PipedInputStream.java
@@ -0,0 +1,171 @@
+/*******************************************************************************
+ * Copyright (c) 1996, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import java.io.*;
+
+import com.google.eclipse.elt.emulator.util.BoundedByteBuffer;
+
+/**
+ * Starts a {@code Runnable} in the display thread when data is available and to pretend no data is available after a
+ * given amount of time the {@code Runnable} is running.
+ */
+public class PipedInputStream extends InputStream {
+ // The output stream used by the terminal back-end to write to the terminal
+ protected final OutputStream outputStream;
+
+ // A blocking byte queue.
+ private final BoundedByteBuffer queue;
+
+ /**
+ * Constructor.
+ *
+ * @param bufferSize the size of the buffer of the output stream.
+ */
+ public PipedInputStream(int bufferSize) {
+ outputStream = new PipedOutputStream();
+ queue = new BoundedByteBuffer(bufferSize);
+ }
+
+ /**
+ * Returns the output stream used by the back-end to write to the terminal.
+ *
+ * @return the output stream used by the back-end to write to the terminal.
+ */
+ public OutputStream getOutputStream() {
+ return outputStream;
+ }
+
+ /**
+ * Waits until data is available for reading.
+ *
+ * @param time the time wait, in milliseconds.
+ * @throws InterruptedException when the thread is interrupted while waiting for the buffer to become ready.
+ */
+ public void waitForAvailable(long time) throws InterruptedException {
+ synchronized (queue) {
+ if (queue.size() == 0 && !queue.isClosed()) {
+ queue.wait(time);
+ }
+ }
+ }
+
+ @Override public int available() {
+ synchronized (queue) {
+ return queue.size();
+ }
+ }
+
+ @Override public int read() throws IOException {
+ try {
+ synchronized (queue) {
+ return queue.read();
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return -1;
+ }
+ }
+
+ @Override public void close() throws IOException {
+ synchronized (queue) {
+ queue.close();
+ }
+ }
+
+ @Override public int read(byte[] b, int off, int len) throws IOException {
+ if (len == 0) {
+ return 0;
+ }
+ int n = 0;
+ // Read as much as we can using a single synchronized statement.
+ try {
+ synchronized (queue) {
+ // If nothing available, block and read one byte.
+ if (queue.size() == 0) {
+ // Block now until at least one byte is available.
+ int c = queue.read();
+ // Are we at the end of stream?
+ if (c == -1) {
+ return -1;
+ }
+ b[off] = (byte) c;
+ n++;
+ }
+ // Is there more data available?
+ if (n < len && queue.size() > 0) {
+ // Read at most available.
+ int available = Math.min(queue.size(), len - n);
+ // Are we at the end of the stream?
+ if (available == 0 && queue.isClosed()) {
+ // If no byte was read, return -1 to indicate end of stream; otherwise return the bytes we read up to now.
+ if (n == 0) {
+ n = -1;
+ }
+ return n;
+ }
+ queue.read(b, off + n, available);
+ n += available;
+ }
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ return n;
+ }
+
+ /**
+ * An output stream that calls {@link PipedInputStream#textAvailable} every time data is written to the stream. The
+ * data is written to {@link PipedInputStream#queue}.
+ */
+ private class PipedOutputStream extends OutputStream {
+ @Override public void write(byte[] b, int off, int len) throws IOException {
+ try {
+ synchronized (queue) {
+ if (queue.isClosed()) {
+ throw new IOException("Stream is closed!");
+ }
+ int written = 0;
+ while (written < len) {
+ if (queue.getFreeSlots() == 0) {
+ // If no slots available, write one byte and block until free slots are available.
+ queue.write(b[off + written]);
+ written++;
+ } else {
+ // If slots are available, write as much as we can in one junk
+ int n = Math.min(queue.getFreeSlots(), len - written);
+ queue.write(b, off + written, n);
+ written += n;
+ }
+ }
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ @Override public void write(int b) throws IOException {
+ try {
+ synchronized (queue) {
+ if (queue.isClosed()) {
+ throw new IOException("Stream is closed!");
+ }
+ queue.write((byte) b);
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ @Override public void close() throws IOException {
+ synchronized (queue) {
+ queue.close();
+ }
+ }
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/PollingTextCanvasModel.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/PollingTextCanvasModel.java
new file mode 100644
index 0000000..37c7059
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/PollingTextCanvasModel.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import org.eclipse.swt.widgets.Display;
+
+import com.google.eclipse.elt.emulator.model.ITerminalTextDataSnapshot;
+
+public class PollingTextCanvasModel extends AbstractTextCanvasModel {
+ private int pollInterval = 50;
+
+ public PollingTextCanvasModel(ITerminalTextDataSnapshot snapshot) {
+ super(snapshot);
+ Display.getDefault().timerExec(pollInterval, new Runnable() {
+ @Override public void run() {
+ update();
+ Display.getDefault().timerExec(pollInterval, this);
+ }
+ });
+ }
+
+ public void setUpdateInterval(int interval) {
+ pollInterval = interval;
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/StyleMap.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/StyleMap.java
new file mode 100644
index 0000000..c277bac
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/StyleMap.java
@@ -0,0 +1,265 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import java.util.*;
+
+import org.eclipse.jface.resource.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.Display;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+public class StyleMap {
+ private static final String BLACK = "black";
+ private static final String WHITE = "white";
+ private static final String WHITE_FOREGROUND = "white_foreground";
+ private static final String GRAY = "gray";
+ private static final String MAGENTA = "magenta";
+ private static final String CYAN = "cyan";
+ private static final String YELLOW = "yellow";
+ private static final String BLUE = "blue";
+ private static final String GREEN = "green";
+ private static final String RED = "red";
+
+ private static final String PREFIX = "org.eclipse.tm.internal.";
+
+ private final Map<StyleColor, Color> colorMapForeground = new HashMap<StyleColor, Color>();
+ private final Map<StyleColor, Color> colorMapBackground = new HashMap<StyleColor, Color>();
+ private final Map<StyleColor, Color> colorMapIntense = new HashMap<StyleColor, Color>();
+
+ private Point charSize;
+
+ private boolean invertColors;
+ private boolean proportional;
+
+ private final int[] offsets = new int[256];
+
+ private Color background = getColor(new RGB(0, 0, 0));
+ private Color foreground = getColor(new RGB(229, 229, 229));
+
+ private Font font = JFaceResources.getFontRegistry().get("org.eclipse.jface.textfont");
+
+ StyleMap() {
+ initColors();
+ updateFont();
+ }
+
+ private void initColors() {
+ initForegroundColors();
+ initBackgroundColors();
+ initIntenseColors();
+ }
+
+ private void initForegroundColors() {
+ if (invertColors) {
+ setColor(colorMapForeground, WHITE, 0, 0, 0);
+ setColor(colorMapForeground, WHITE_FOREGROUND, 50, 50, 50);
+ setColor(colorMapForeground, BLACK, 229, 229, 229);
+ } else {
+ setColor(colorMapForeground, WHITE, 255, 255, 255);
+ setColor(colorMapForeground, WHITE_FOREGROUND, 229, 229, 229);
+ setColor(colorMapForeground, BLACK, 50, 50, 50);
+ }
+ setColor(colorMapForeground, RED, 205, 0, 0);
+ setColor(colorMapForeground, GREEN, 0, 205, 0);
+ setColor(colorMapForeground, BLUE, 0, 0, 238);
+ setColor(colorMapForeground, YELLOW, 205, 205, 0);
+ setColor(colorMapForeground, CYAN, 0, 205, 205);
+ setColor(colorMapForeground, MAGENTA, 205, 0, 205);
+ setColor(colorMapForeground, GRAY, 229, 229, 229);
+ }
+
+ private void initBackgroundColors() {
+ if (invertColors) {
+ setColor(colorMapBackground, WHITE, 0, 0, 0);
+ setColor(colorMapBackground, WHITE_FOREGROUND, 50, 50, 50); // only used when colors are inverse
+ setColor(colorMapBackground, BLACK, 255, 255, 255); // cursor color
+ } else {
+ setColor(colorMapBackground, WHITE, 255, 255, 255);
+ setColor(colorMapBackground, WHITE_FOREGROUND, 229, 229, 229);
+ setColor(colorMapBackground, BLACK, 0, 0, 0);
+ }
+ setColor(colorMapBackground, RED, 205, 0, 0);
+ setColor(colorMapBackground, GREEN, 0, 205, 0);
+ setColor(colorMapBackground, BLUE, 0, 0, 238);
+ setColor(colorMapBackground, YELLOW, 205, 205, 0);
+ setColor(colorMapBackground, CYAN, 0, 205, 205);
+ setColor(colorMapBackground, MAGENTA, 205, 0, 205);
+ setColor(colorMapBackground, GRAY, 229, 229, 229);
+ }
+
+ private void initIntenseColors() {
+ if (invertColors) {
+ setColor(colorMapIntense, WHITE, 127, 127, 127);
+ setColor(colorMapIntense, WHITE_FOREGROUND, 0, 0, 0); // only used when colors are inverse
+ setColor(colorMapIntense, BLACK, 255, 255, 255);
+ } else {
+ setColor(colorMapIntense, WHITE, 255, 255, 255);
+ setColor(colorMapIntense, WHITE_FOREGROUND, 255, 255, 255);
+ setColor(colorMapIntense, BLACK, 0, 0, 0);
+ }
+ setColor(colorMapIntense, RED, 255, 0, 0);
+ setColor(colorMapIntense, GREEN, 0, 255, 0);
+ setColor(colorMapIntense, BLUE, 92, 92, 255);
+ setColor(colorMapIntense, YELLOW, 255, 255, 0);
+ setColor(colorMapIntense, CYAN, 0, 255, 255);
+ setColor(colorMapIntense, MAGENTA, 255, 0, 255);
+ setColor(colorMapIntense, GRAY, 255, 255, 255);
+ }
+
+ private void setColor(Map<StyleColor, Color> colorMap, String name, int r, int g, int b) {
+ Color color = getColor(new RGB(r, g, b));
+ setColor(colorMap, color, StyleColor.getStyleColor(name));
+ setColor(colorMap, color, StyleColor.getStyleColor(name.toUpperCase()));
+ }
+
+ private void setColor(Map<StyleColor, Color> colorMap, Color color, StyleColor styleColor) {
+ if (styleColor != null) {
+ colorMap.put(styleColor, color);
+ }
+ }
+
+ public Color getForegroundColor(Style style) {
+ if (style == null) {
+ return foreground;
+ }
+ StyleColor color = style.isReverse() ? style.getBackground() : style.getForeground();
+ Map<StyleColor, Color> map = style.isBold() ? colorMapIntense : colorMapForeground;
+ Color actualColor = map.get(color);
+ if (actualColor == null) {
+ actualColor = foreground;
+ }
+ return actualColor;
+ }
+
+ public Color getBackgroundColor(Style style) {
+ if (style == null) {
+ return background;
+ }
+ StyleColor color = style.isReverse() ? style.getForeground() : style.getBackground();
+ Color actualColor = colorMapBackground.get(color);
+ if (actualColor == null) {
+ actualColor = background;
+ }
+ return actualColor;
+ }
+
+ public void setInvertedColors(boolean invert) {
+ if (invert == invertColors) {
+ return;
+ }
+ invertColors = invert;
+ initColors();
+ }
+
+ public Font getFont(Style style) {
+ if (style == null) {
+ return font;
+ }
+ FontData fontDatas[] = font.getFontData();
+ FontData data = fontDatas[0];
+ if (style.isBold()) {
+ return new Font(font.getDevice(), data.getName(), data.getHeight(), data.getStyle() | SWT.BOLD);
+ }
+ if (style.isUnderline()) {
+ return new Font(font.getDevice(), data.getName(), data.getHeight(), data.getStyle() | SWT.ITALIC);
+ }
+ return font;
+ }
+
+ public Font getFont() {
+ return font;
+ }
+
+ public int getFontWidth() {
+ return charSize.x;
+ }
+
+ public int getFontHeight() {
+ return charSize.y;
+ }
+
+ public void updateFont() {
+ Display display = Display.getCurrent();
+ GC gc = new GC(display);
+ gc.setFont(font);
+ charSize = gc.textExtent("W");
+ proportional = false;
+ for (char c = ' '; c <= '~'; c++) {
+ // Consider only the first 128 chars for deciding if a font is proportional.
+ if (measureChar(gc, c, true)) {
+ proportional = true;
+ }
+ }
+ // TODO should we also consider the upper 128 chars?
+ for (char c = ' ' + 128; c <= '~' + 128; c++) {
+ measureChar(gc, c, false);
+ }
+ if (proportional) {
+ charSize.x -= 2; // Works better on small fonts.
+ }
+ for (int i = 0; i < offsets.length; i++) {
+ offsets[i] = (charSize.x - offsets[i]) / 2;
+ }
+ gc.dispose();
+ }
+
+ private boolean measureChar(GC gc, char c, boolean updateMax) {
+ boolean proportional = false;
+ Point extent = gc.textExtent(String.valueOf(c));
+ if (extent.x > 0 && extent.y > 0 && (charSize.x != extent.x || charSize.y != extent.y)) {
+ proportional = true;
+ if (updateMax) {
+ charSize.x = Math.max(charSize.x, extent.x);
+ charSize.y = Math.max(charSize.y, extent.y);
+ }
+ }
+ offsets[c] = extent.x;
+ return proportional;
+ }
+
+ public boolean isFontProportional() {
+ return proportional;
+ }
+
+ /**
+ * Return the offset in pixels required to center a given character.
+ *
+ * @param c the character to measure.
+ * @return the offset in x direction to center this character.
+ */
+ public int getCharOffset(char c) {
+ if (c >= offsets.length) {
+ return 0;
+ }
+ return offsets[c];
+ }
+
+ public void setColors(RGB background, RGB foreground) {
+ this.background = getColor(background);
+ this.foreground = getColor(foreground);
+ }
+
+ private Color getColor(RGB colorData) {
+ String name = PREFIX + colorData.red + "-" + colorData.green + "-" + colorData.blue;
+ ColorRegistry colorRegistry = JFaceResources.getColorRegistry();
+ Color color = colorRegistry.get(name);
+ if (color == null) {
+ colorRegistry.put(name, colorData);
+ color = colorRegistry.get(name);
+ }
+ return color;
+ }
+
+ public void setFont(Font font) {
+ this.font = font;
+ updateFont();
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/TextCanvas.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/TextCanvas.java
new file mode 100644
index 0000000..02b19b2
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/TextCanvas.java
@@ -0,0 +1,399 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * A cell oriented Canvas. Maintains a list of "cells". It can either be vertically or horizontally scrolled. The
+ * CellRenderer is responsible for painting the cell.
+ */
+public class TextCanvas extends GridCanvas {
+ private final ITextCanvasModel cellCanvasModel;
+ private final ILinelRenderer cellRenderer;
+ private boolean scrollLockOn;
+ private Point draggingStart;
+ private Point draggingEnd;
+ private boolean hasSelection;
+ private ResizeListener resizeListener;
+
+ // The minSize is meant to determine the minimum size of the backing store (grid) into which remote data is rendered.
+ // If the viewport is smaller than that minimum size, the backing store size remains at the minSize,and a scrollbar is
+ // shown instead. In reality, this has the following issues or effects today:
+ //
+ // (a) Bug 281328: For very early data coming in before the widget is realized, the minSize determines into what
+ // initial grid that is rendered. See also {@link #addResizeHandler(ResizeListener)}.
+ //
+ // (b) Bug 294468: Since we have redraw and size computation problems with horizontal scrollers, for now the
+ // minColumns must be small enough to avoid a horizontal scroller appearing in most cases.
+ //
+ // (c) Bug 294327: Since we have problems with the vertical scroller showing the correct location, minLines must be
+ // small enough to avoid a vertical scroller or new data may be rendered off-screen.
+ //
+ // As a compromise, we have been working with a 20x4 since the terminal inception, though many users would want a
+ // 80x24 minSize and backing store.
+
+ // Pros and cons of the small minsize:
+ // + consistent "remote size==viewport size", vi works as expected
+ // - dumb terminals which expect 80x24 render garbled on small viewport
+ //
+ // If bug 294468 were resolved, an 80 wide minSize would be preferrable since it allows switching the terminal
+ // viewport small/large as needed, without destroying the backing store. For a complete solution, bug 196462 tracks
+ // the request for a user-defined fixed-widow-size-mode.
+ private int minColumns = 80;
+
+ private int minLines = 4;
+ private boolean cursorEnabled;
+ private boolean resizing;
+
+ /**
+ * Create a new CellCanvas with the given SWT style bits. (SWT.H_SCROLL and SWT.V_SCROLL are automatically added).
+ */
+ public TextCanvas(Composite parent, ITextCanvasModel model, int style, ILinelRenderer cellRenderer) {
+ super(parent, style | SWT.H_SCROLL | SWT.V_SCROLL);
+ this.cellRenderer = cellRenderer;
+ setCellWidth(cellRenderer.getCellWidth());
+ setCellHeight(cellRenderer.getCellHeight());
+ cellCanvasModel = model;
+ cellCanvasModel.addCellCanvasModelListener(new ITextCanvasModelListener() {
+ @Override public void rangeChanged(int col, int line, int width, int height) {
+ repaintRange(col, line, width, height);
+ }
+
+ @Override public void dimensionsChanged(int cols, int rows) {
+ calculateGrid();
+ }
+
+ @Override public void terminalDataChanged() {
+ if (!isDisposed() && !resizing) {
+ // scroll to end (unless scroll lock is active)
+ calculateGrid();
+ scrollToEnd();
+ }
+ }
+ });
+ // let the cursor blink if the text canvas gets the focus...
+ addFocusListener(new FocusListener() {
+ @Override public void focusGained(FocusEvent e) {
+ cellCanvasModel.setCursorEnabled(cursorEnabled);
+ }
+
+ @Override public void focusLost(FocusEvent e) {
+ cellCanvasModel.setCursorEnabled(false);
+ }
+ });
+ addMouseListener(new MouseAdapter() {
+ @Override public void mouseDown(MouseEvent e) {
+ if (e.button == 1) { // left button
+ draggingStart = screenPointToCell(e.x, e.y);
+ hasSelection = false;
+ if ((e.stateMask & SWT.SHIFT) != 0) {
+ Point anchor = cellCanvasModel.getSelectionAnchor();
+ if (anchor != null) {
+ draggingStart = anchor;
+ }
+ } else {
+ cellCanvasModel.setSelectionAnchor(draggingStart);
+ }
+ draggingEnd = null;
+ }
+ }
+
+ @Override public void mouseUp(MouseEvent e) {
+ if (e.button == 1) { // left button
+ updateHasSelection(e);
+ if (hasSelection) {
+ setSelection(screenPointToCell(e.x, e.y));
+ } else {
+ cellCanvasModel.setSelection(-1, -1, -1, -1);
+ }
+ draggingStart = null;
+ }
+ }
+ });
+ addMouseMoveListener(new MouseMoveListener() {
+ @Override public void mouseMove(MouseEvent e) {
+ if (draggingStart != null) {
+ updateHasSelection(e);
+ setSelection(screenPointToCell(e.x, e.y));
+ }
+ }
+ });
+ serVerticalBarVisible(true);
+ setHorizontalBarVisible(false);
+ }
+
+ // The user has to drag the mouse to at least one character to make a selection. Once this is done, even a one char
+ // selection is OK.
+ private void updateHasSelection(MouseEvent e) {
+ if (draggingStart != null) {
+ Point p = screenPointToCell(e.x, e.y);
+ if (draggingStart.x != p.x || draggingStart.y != p.y) {
+ hasSelection = true;
+ }
+ }
+ }
+
+ void setSelection(Point p) {
+ if (draggingStart != null && !p.equals(draggingEnd)) {
+ draggingEnd = p;
+ if (compare(p, draggingStart) < 0) {
+ // bug 219589 - make sure selection start coordinates are non-negative
+ int startColumn = Math.max(0, p.x);
+ int startRow = Math.max(p.y, 0);
+ cellCanvasModel.setSelection(startRow, draggingStart.y, startColumn, draggingStart.x);
+ } else {
+ cellCanvasModel.setSelection(draggingStart.y, p.y, draggingStart.x, p.x);
+ }
+ }
+ }
+
+ int compare(Point p1, Point p2) {
+ if (p1.equals(p2)) {
+ return 0;
+ }
+ if (p1.y == p2.y) {
+ return p1.x > p2.x ? 1 : -1;
+ }
+ return p1.y > p2.y ? 1 : -1;
+ }
+
+ public ILinelRenderer getCellRenderer() {
+ return cellRenderer;
+ }
+
+ public int getMinColumns() {
+ return minColumns;
+ }
+
+ public void setMinColumns(int minColumns) {
+ this.minColumns = minColumns;
+ }
+
+ public int getMinLines() {
+ return minLines;
+ }
+
+ public void setMinLines(int minLines) {
+ this.minLines = minLines;
+ }
+
+ protected void onResize(boolean init) {
+ if (resizeListener != null) {
+ Rectangle bonds = getClientArea();
+ int cellHeight = getCellHeight();
+ int cellWidth = getCellWidth();
+ int lines = bonds.height / cellHeight;
+ int columns = bonds.width / cellWidth;
+ // When the view is minimized, its size is set to 0 we don't sent this to the terminal!
+ if ((lines > 0 && columns > 0) || init) {
+ if (columns < minColumns) {
+ if (!isHorizontalBarVisble()) {
+ setHorizontalBarVisible(true);
+ bonds = getClientArea();
+ lines = bonds.height / cellHeight;
+ }
+ columns = minColumns;
+ } else if (columns >= minColumns && isHorizontalBarVisble()) {
+ setHorizontalBarVisible(false);
+ bonds = getClientArea();
+ lines = bonds.height / cellHeight;
+ columns = bonds.width / cellWidth;
+ }
+ if (lines < minLines) {
+ lines = minLines;
+ }
+ resizeListener.sizeChanged(lines, columns);
+ }
+ }
+ super.onResize();
+ calculateGrid();
+ }
+
+ @Override protected void onResize() {
+ resizing = true;
+ try {
+ onResize(false);
+ } finally {
+ resizing = false;
+ }
+ }
+
+ private void calculateGrid() {
+ Rectangle virtualBounds = getVirtualBounds();
+ setRedraw(false);
+ try {
+ setVirtualExtend(getCols() * getCellWidth(), getRows() * getCellHeight());
+ getParent().layout();
+ if (resizing) {
+ // scroll to end if view port was near last line
+ Rectangle viewRect = getViewRectangle();
+ if (virtualBounds.height - (viewRect.y + viewRect.height) < getCellHeight() * 2) {
+ scrollToEnd();
+ }
+ }
+ } finally {
+ setRedraw(true);
+ }
+ }
+
+ void scrollToEnd() {
+ if (!scrollLockOn) {
+ int y = -(getRows() * getCellHeight() - getClientArea().height);
+ if (y > 0) {
+ y = 0;
+ }
+ Rectangle v = getViewRectangle();
+ if (v.y != -y) {
+ setVirtualOrigin(v.x, y);
+ }
+ // make sure the scroll area is correct.
+ scrollY(getVerticalBar());
+ scrollX(getHorizontalBar());
+ }
+ }
+
+ public boolean isScrollLockOn() {
+ return scrollLockOn;
+ }
+
+ public void setScrollLockOn(boolean on) {
+ scrollLockOn = on;
+ }
+
+ protected void repaintRange(int col, int line, int width, int height) {
+ Point origin = cellToOriginOnScreen(col, line);
+ Rectangle r = new Rectangle(origin.x, origin.y, width * getCellWidth(), height * getCellHeight());
+ repaint(r);
+ }
+
+ @Override protected void drawLine(GC gc, int line, int x, int y, int colFirst, int colLast) {
+ cellRenderer.drawLine(cellCanvasModel, gc, line, x, y, colFirst, colLast);
+ }
+
+ @Override protected Color getTerminalBackgroundColor() {
+ return cellRenderer.getDefaultBackgroundColor();
+ }
+
+ @Override protected void visibleCellRectangleChanged(int x, int y, int width, int height) {
+ cellCanvasModel.setVisibleRectangle(y, x, height, width);
+ update();
+ }
+
+ @Override protected int getCols() {
+ return cellCanvasModel.getTerminalText().getWidth();
+ }
+
+ @Override protected int getRows() {
+ return cellCanvasModel.getTerminalText().getHeight();
+ }
+
+ public String getSelectionText() {
+ // TODO -- create a hasSelectionMethod!
+ return cellCanvasModel.getSelectedText();
+ }
+
+ public void copy() {
+ Clipboard clipboard = new Clipboard(getDisplay());
+ clipboard.setContents(new Object[] { getSelectionText() }, new Transfer[] { TextTransfer.getInstance() });
+ clipboard.dispose();
+ }
+
+ public void selectAll() {
+ cellCanvasModel.setSelection(
+ 0, cellCanvasModel.getTerminalText().getHeight(), 0, cellCanvasModel.getTerminalText().getWidth());
+ cellCanvasModel.setSelectionAnchor(new Point(0, 0));
+ }
+
+ public boolean isEmpty() {
+ return false;
+ }
+
+ /**
+ * Gets notified when the visible size of the terminal changes. This should update the model!
+ */
+ public static interface ResizeListener {
+ void sizeChanged(int lines, int columns);
+ }
+
+ public void addResizeHandler(ResizeListener listener) {
+ if (resizeListener != null) {
+ throw new IllegalArgumentException("There can be at most one listener at the moment!");
+ }
+ resizeListener = listener;
+ // Bug 281328: The very first few characters might be missing in the terminal control if opened and connected
+ // programmatically.
+ //
+ // In case the terminal had not been visible yet or is too small (less than one line visible), the terminal should
+ // have a minimum size to avoid RuntimeExceptions.
+ Rectangle bonds = getClientArea();
+ if (bonds.height < getCellHeight() || bonds.width < getCellWidth()) {
+ // Widget not realized yet, or minimized to < 1 item. Just tell the listener our min size.
+ resizeListener.sizeChanged(getMinLines(), getMinColumns());
+ } else {
+ // Widget realized: compute actual size and force telling the listener
+ onResize(true);
+ }
+ }
+
+ public void onFontChange() {
+ cellRenderer.onFontChange();
+ setCellWidth(cellRenderer.getCellWidth());
+ setCellHeight(cellRenderer.getCellHeight());
+ calculateGrid();
+ }
+
+ public void setInvertedColors(boolean invert) {
+ cellRenderer.setInvertedColors(invert);
+ redraw();
+ }
+
+ /**
+ * Indicates whether the cursor is enabled (blinking.) By default the cursor is not enabled.
+ *
+ * @return {@code true} if the cursor is enabled, {@code false} otherwise.
+ */
+ public boolean isCursorEnabled() {
+ return cursorEnabled;
+ }
+
+ /**
+ * Enables or disables the cursor (enabling means that the cursor blinks.)
+ *
+ * @param enabled indicates whether the cursor should be enabled.
+ */
+ public void setCursorEnabled(boolean enabled) {
+ if (enabled != cursorEnabled) {
+ cursorEnabled = enabled;
+ cellCanvasModel.setCursorEnabled(cursorEnabled);
+ }
+ }
+
+ public void setColors(RGB background, RGB foreground) {
+ cellRenderer.setColors(background, foreground);
+ redraw();
+ }
+
+ @Override public void setFont(Font font) {
+ super.setFont(font);
+ cellRenderer.setFont(font);
+ redraw();
+ }
+
+ @Override public Point screenPointToCell(int x, int y) {
+ return super.screenPointToCell(x, y);
+ }
+
+ public void setBlinkingCursor(boolean useBlinkingCursor) {
+ cellCanvasModel.setBlinkingCursor(useBlinkingCursor);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/TextLineRenderer.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/TextLineRenderer.java
new file mode 100644
index 0000000..409b562
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/TextLineRenderer.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import static org.eclipse.swt.SWT.*;
+
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.Display;
+
+import com.google.eclipse.elt.emulator.model.*;
+
+public class TextLineRenderer implements ILinelRenderer {
+ private final ITextCanvasModel model;
+ private final StyleMap styleMap = new StyleMap();
+
+ public TextLineRenderer(TextCanvas c, ITextCanvasModel model) {
+ this.model = model;
+ }
+
+ @Override public int getCellWidth() {
+ return styleMap.getFontWidth();
+ }
+
+ @Override public int getCellHeight() {
+ return styleMap.getFontHeight();
+ }
+
+ @Override public void drawLine(
+ ITextCanvasModel model, GC gc, int line, int x, int y, int firstColumn, int lastColumn) {
+ if (line < 0 || line >= getTerminalText().getHeight() || firstColumn >= getTerminalText().getWidth()
+ || firstColumn - lastColumn == 0) {
+ fillBackground(gc, x, y, getCellWidth() * (lastColumn - firstColumn), getCellHeight());
+ return;
+ }
+ lastColumn = Math.min(lastColumn, getTerminalText().getWidth());
+ LineSegment[] segments = getTerminalText().getLineSegments(line, firstColumn, lastColumn - firstColumn);
+ for (int i = 0; i < segments.length; i++) {
+ LineSegment segment = segments[i];
+ Style style = segment.getStyle();
+ setupGC(gc, style);
+ String text = segment.getText();
+ drawText(gc, x, y, firstColumn, segment.getColumn(), text);
+ drawCursor(model, gc, line, x, y, firstColumn);
+ }
+ if (this.model.hasLineSelection(line)) {
+ Display display = Display.getCurrent();
+ gc.setForeground(display.getSystemColor(COLOR_LIST_SELECTION_TEXT));
+ gc.setBackground(display.getSystemColor(COLOR_LIST_SELECTION));
+ Point start = model.getSelectionStart();
+ Point end = model.getSelectionEnd();
+ char[] chars = model.getTerminalText().getChars(line);
+ if (chars == null) {
+ return;
+ }
+ int offset = 0;
+ if (start.y == line) {
+ offset = start.x;
+ }
+ offset = Math.max(offset, firstColumn);
+ int len;
+ if (end.y == line) {
+ len = end.x - offset + 1;
+ } else {
+ len = chars.length - offset + 1;
+ }
+ len = Math.min(len, chars.length - offset);
+ if (len > 0) {
+ String text = new String(chars, offset, len);
+ drawText(gc, x, y, firstColumn, offset, text);
+ }
+ }
+ }
+
+ private void fillBackground(GC gc, int x, int y, int width, int height) {
+ Color bg = gc.getBackground();
+ gc.setBackground(getDefaultBackgroundColor());
+ gc.fillRectangle(x, y, width, height);
+ gc.setBackground(bg);
+ }
+
+ @Override public Color getDefaultBackgroundColor() {
+ // null == default style
+ return styleMap.getBackgroundColor(null);
+ }
+
+ private void drawCursor(ITextCanvasModel model, GC gc, int row, int x, int y, int colFirst) {
+ if (!model.isCursorOn()) {
+ return;
+ }
+ int cursorLine = model.getCursorLine();
+ if (row == cursorLine) {
+ int cursorColumn = model.getCursorColumn();
+ if (cursorColumn < getTerminalText().getWidth()) {
+ Style style = getTerminalText().getStyle(row, cursorColumn);
+ if (style != null) {
+ style = style.setReverse(!style.isReverse());
+ setupGC(gc, style);
+ } else {
+ setBackground(gc, styleMap.getForegroundColor(null));
+ setForeground(gc, styleMap.getBackgroundColor(null));
+ }
+ String text = String.valueOf(getTerminalText().getChar(row, cursorColumn));
+ drawText(gc, x, y, colFirst, cursorColumn, text);
+ }
+ }
+ }
+
+ private void drawText(GC gc, int x, int y, int colFirst, int col, String text) {
+ int offset = (col - colFirst) * getCellWidth();
+ if (styleMap.isFontProportional()) {
+ // Draw the background.
+ // TODO why does this not work?
+ // gc.fillRectangle(x, y, styleMap.getFontWidth() * text.length(), styleMap.getFontHeight());
+ for (int i = 0; i < text.length(); i++) {
+ char c = text.charAt(i);
+ int fontWidth = styleMap.getFontWidth();
+ int newX = x + offset + i * fontWidth;
+ // TODO why do I have to draw the background character by character?
+ gc.fillRectangle(newX, y, fontWidth, styleMap.getFontHeight());
+ if (c != ' ' && c != '\000') {
+ gc.drawString(String.valueOf(c), styleMap.getCharOffset(c) + newX, y, true);
+ }
+ }
+ } else {
+ text = text.replace('\000', ' ');
+ gc.drawString(text, x + offset, y, false);
+ }
+ }
+
+ private void setupGC(GC gc, Style style) {
+ setForeground(gc, styleMap.getForegroundColor(style));
+ setBackground(gc, styleMap.getBackgroundColor(style));
+ Font font = styleMap.getFont(style);
+ if (font != gc.getFont()) {
+ gc.setFont(font);
+ }
+ }
+
+ private void setForeground(GC gc, Color color) {
+ if (color != gc.getForeground()) {
+ gc.setForeground(color);
+ }
+ }
+
+ private void setBackground(GC gc, Color color) {
+ if (color != gc.getBackground()) {
+ gc.setBackground(color);
+ }
+ }
+
+ ITerminalTextDataReadOnly getTerminalText() {
+ return model.getTerminalText();
+ }
+
+ @Override public void onFontChange() {
+ styleMap.updateFont();
+ }
+
+ @Override public void setInvertedColors(boolean invert) {
+ styleMap.setInvertedColors(invert);
+
+ }
+
+ @Override public void setColors(RGB background, RGB foreground) {
+ styleMap.setColors(background, foreground);
+ }
+
+ @Override public void setFont(Font font) {
+ styleMap.setFont(font);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/VirtualCanvas.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/VirtualCanvas.java
new file mode 100644
index 0000000..901ddc7
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/textcanvas/VirtualCanvas.java
@@ -0,0 +1,303 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.emulator.textcanvas;
+
+import static org.eclipse.core.runtime.IStatus.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+import com.google.eclipse.elt.emulator.impl.TerminalPlugin;
+
+/**
+ * A {@code Canvas} showing a virtual object. Virtual: the extent of the total canvas. Screen: the visible client area
+ * in the screen.
+ */
+public abstract class VirtualCanvas extends Canvas {
+ private final Rectangle virtualBounds = new Rectangle(0, 0, 0, 0);
+
+ /** Called when the viewed part is changing. */
+ private final Rectangle viewRectangle = new Rectangle(0, 0, 0, 0);
+
+ private Rectangle clientArea;
+
+ /** Prevent infinite loop in {@link #updateScrollbars()} */
+ private boolean inUpdateScrollbars;
+
+ private static boolean inUpdateScrollbarsLogged;
+
+ public VirtualCanvas(Composite parent, int style) {
+ super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE);
+ clientArea = getClientArea();
+ addListener(SWT.Paint, new Listener() {
+ @Override public void handleEvent(Event event) {
+ paint(event.gc);
+ }
+ });
+ addListener(SWT.Resize, new Listener() {
+ @Override public void handleEvent(Event event) {
+ clientArea = getClientArea();
+ onResize();
+ }
+ });
+ getVerticalBar().addListener(SWT.Selection, new Listener() {
+ @Override public void handleEvent(Event e) {
+ scrollY((ScrollBar) e.widget);
+ }
+ });
+ getHorizontalBar().addListener(SWT.Selection, new Listener() {
+ @Override public void handleEvent(Event e) {
+ scrollX((ScrollBar) e.widget);
+ }
+ });
+ }
+
+ protected void onResize() {
+ updateViewRectangle();
+ }
+
+ protected void scrollX(ScrollBar horizontalBar) {
+ int selection = horizontalBar.getSelection();
+ int destinationX = -selection - virtualBounds.x;
+ virtualBounds.x = -selection;
+ scrollSmart(destinationX, 0);
+ updateViewRectangle();
+ }
+
+ protected void scrollXDelta(int delta) {
+ getHorizontalBar().setSelection(-virtualBounds.x + delta);
+ scrollX(getHorizontalBar());
+ }
+
+ protected void scrollY(ScrollBar vBar) {
+ int vSelection = vBar.getSelection();
+ int destY = -vSelection - virtualBounds.y;
+ if (destY != 0) {
+ virtualBounds.y = -vSelection;
+ scrollSmart(0, destY);
+ updateViewRectangle();
+ }
+ }
+
+ protected void scrollYDelta(int delta) {
+ getVerticalBar().setSelection(-virtualBounds.y + delta);
+ scrollY(getVerticalBar());
+ }
+
+ protected void scrollSmart(int deltaX, int deltaY) {
+ if (deltaX != 0 || deltaY != 0) {
+ Rectangle rect = getBounds();
+ scroll(deltaX, deltaY, 0, 0, rect.width, rect.height, false);
+ }
+ }
+
+ protected void revealRect(Rectangle rect) {
+ Rectangle visibleRect = getScreenRectInVirtualSpace();
+ // scroll the X part
+ int deltaX = 0;
+ if (rect.x < visibleRect.x) {
+ deltaX = rect.x - visibleRect.x;
+ } else if (visibleRect.x + visibleRect.width < rect.x + rect.width) {
+ deltaX = (rect.x + rect.width) - (visibleRect.x + visibleRect.width);
+ }
+ if (deltaX != 0) {
+ getHorizontalBar().setSelection(-virtualBounds.x + deltaX);
+ scrollX(getHorizontalBar());
+ }
+ // scroll the Y part
+ int deltaY = 0;
+ if (rect.y < visibleRect.y) {
+ deltaY = rect.y - visibleRect.y;
+ } else if (visibleRect.y + visibleRect.height < rect.y + rect.height) {
+ deltaY = (rect.y + rect.height) - (visibleRect.y + visibleRect.height);
+ }
+ if (deltaY != 0) {
+ getVerticalBar().setSelection(-virtualBounds.y + deltaY);
+ scrollY(getVerticalBar());
+ }
+ }
+
+ protected void repaint(Rectangle r) {
+ if (isDisposed()) {
+ return;
+ }
+ if (inClipping(r, clientArea)) {
+ redraw(r.x, r.y, r.width, r.height, true);
+ update();
+ }
+ }
+
+ abstract protected void paint(GC gc);
+
+ protected Color getTerminalBackgroundColor() {
+ return getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
+ }
+
+ protected void paintUnoccupiedSpace(GC gc, Rectangle clipping) {
+ int width = virtualBounds.width + virtualBounds.x;
+ int height = virtualBounds.height + virtualBounds.y;
+ int marginWidth = (clipping.x + clipping.width) - width;
+ int marginHeight = (clipping.y + clipping.height) - height;
+ if (marginWidth > 0 || marginHeight > 0) {
+ Color background = getBackground();
+ gc.setBackground(getTerminalBackgroundColor());
+ if (marginWidth > 0) {
+ gc.fillRectangle(width, clipping.y, marginWidth, clipping.height);
+ }
+ if (marginHeight > 0) {
+ gc.fillRectangle(clipping.x, height, clipping.width, marginHeight);
+ }
+ gc.setBackground(background);
+ }
+ }
+
+ protected boolean inClipping(Rectangle clipping, Rectangle r) {
+ // TODO check if this is OK in all cases (the <=!)
+ if (r.x + r.width <= clipping.x) {
+ return false;
+ }
+ if (clipping.x + clipping.width <= r.x) {
+ return false;
+ }
+ if (r.y + r.height <= clipping.y) {
+ return false;
+ }
+ if (clipping.y + clipping.height <= r.y) {
+ return false;
+ }
+ return true;
+ }
+
+ protected Rectangle getScreenRectInVirtualSpace() {
+ return new Rectangle(
+ clientArea.x - virtualBounds.x, clientArea.y - virtualBounds.y, clientArea.width, clientArea.height);
+ }
+
+ protected Rectangle getRectInVirtualSpace(Rectangle r) {
+ return new Rectangle(r.x - virtualBounds.x, r.y - virtualBounds.y, r.width, r.height);
+ }
+
+ protected void setVirtualExtend(int width, int height) {
+ virtualBounds.width = width;
+ virtualBounds.height = height;
+ updateScrollbars();
+ updateViewRectangle();
+ }
+
+ protected void setVirtualOrigin(int x, int y) {
+ if (virtualBounds.x != x || virtualBounds.y != y) {
+ virtualBounds.x = x;
+ virtualBounds.y = y;
+ getHorizontalBar().setSelection(-x);
+ getVerticalBar().setSelection(-y);
+ updateViewRectangle();
+ }
+ }
+
+ protected Rectangle getVirtualBounds() {
+ return cloneRectangle(virtualBounds);
+ }
+
+ protected int virtualXtoScreen(int x) {
+ return x + virtualBounds.x;
+ }
+
+ protected int virtualYtoScreen(int y) {
+ return y + virtualBounds.y;
+ }
+
+ protected int screenXtoVirtual(int x) {
+ return x - virtualBounds.x;
+ }
+
+ protected int screenYtoVirtual(int y) {
+ return y - virtualBounds.y;
+ }
+
+ protected void updateViewRectangle() {
+ if (viewRectangle.x == -virtualBounds.x && viewRectangle.y == -virtualBounds.y
+ && viewRectangle.width == clientArea.width && viewRectangle.height == clientArea.height) {
+ return;
+ }
+ viewRectangle.x = -virtualBounds.x;
+ viewRectangle.y = -virtualBounds.y;
+ viewRectangle.width = clientArea.width;
+ viewRectangle.height = clientArea.height;
+ viewRectangleChanged(viewRectangle.x, viewRectangle.y, viewRectangle.width, viewRectangle.height);
+ }
+
+ protected Rectangle getViewRectangle() {
+ return cloneRectangle(viewRectangle);
+ }
+
+ private Rectangle cloneRectangle(Rectangle r) {
+ return new Rectangle(r.x, r.y, r.width, r.height);
+ }
+
+ protected void viewRectangleChanged(int x, int y, int width, int height) {}
+
+ private void updateScrollbars() {
+ // don't get into infinite loops....
+ if (!inUpdateScrollbars) {
+ inUpdateScrollbars = true;
+ try {
+ doUpdateScrollbar();
+ } finally {
+ inUpdateScrollbars = false;
+ }
+ } else {
+ if (!inUpdateScrollbarsLogged) {
+ inUpdateScrollbarsLogged = true;
+ ILog logger = TerminalPlugin.getDefault().getLog();
+ logger.log(new Status(WARNING, TerminalPlugin.PLUGIN_ID, OK, "Unexpected Recursion in terminal", null));
+ }
+ }
+ }
+
+ private void doUpdateScrollbar() {
+ Rectangle clientArea = getClientArea();
+ ScrollBar horizontal = getHorizontalBar();
+ // Even if setVisible was called on the scrollbar, isVisible returns false if its parent is not visible.
+ if (!isVisible() || horizontal.isVisible()) {
+ horizontal.setPageIncrement(clientArea.width - horizontal.getIncrement());
+ int max = virtualBounds.width;
+ horizontal.setMaximum(max);
+ horizontal.setThumb(clientArea.width);
+ }
+ ScrollBar vertical = getVerticalBar();
+ // even if setVisible was called on the scrollbar, isVisible returns false if its parent is not visible.
+ if (!isVisible() || vertical.isVisible()) {
+ vertical.setPageIncrement(clientArea.height - vertical.getIncrement());
+ int max = virtualBounds.height;
+ vertical.setMaximum(max);
+ vertical.setThumb(clientArea.height);
+ }
+ }
+
+ protected boolean isVertialBarVisible() {
+ return getVerticalBar().isVisible();
+ }
+
+ protected void serVerticalBarVisible(boolean showVScrollBar) {
+ ScrollBar vertical = getVerticalBar();
+ vertical.setVisible(showVScrollBar);
+ vertical.setSelection(0);
+ }
+
+ protected boolean isHorizontalBarVisble() {
+ return getHorizontalBar().isVisible();
+ }
+
+ protected void setHorizontalBarVisible(boolean showHScrollBar) {
+ ScrollBar horizontal = getHorizontalBar();
+ horizontal.setVisible(showHScrollBar);
+ horizontal.setSelection(0);
+ }
+}
diff --git a/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/util/BoundedByteBuffer.java b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/util/BoundedByteBuffer.java
new file mode 100644
index 0000000..6440bd2
--- /dev/null
+++ b/com.google.eclipse.elt.emulator/src/com/google/eclipse/elt/emulator/util/BoundedByteBuffer.java
@@ -0,0 +1,138 @@
+package com.google.eclipse.elt.emulator.util;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * A byte bounded buffer used to synchronize the input and the output stream.
+ * <p>
+ * Adapted from {@code BoundedBufferWithStateTracking} http://gee.cs.oswego.edu/dl/cpj/allcode.java
+ * http://gee.cs.oswego.edu/dl/cpj/
+ * <p>
+ * BoundedBufferWithStateTracking is part of the examples for the book Concurrent Programming in Java: Design
+ * Principles and Patterns by Doug Lea (ISBN 0-201-31009-0). Second edition published by Addison-Wesley, November
+ * 1999. The code is Copyright(c) Douglas Lea 1996, 1999 and released to the public domain and may be used for any
+ * purposes whatsoever.
+ * <p>
+ * For some reasons a solution based on PipedOutputStream/PipedIntputStream does work *very* slowly:
+ * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4404700
+ * <p>
+ */
+public class BoundedByteBuffer {
+ private final byte[] buffer; // the elements
+ private int putPosition; // circular indices
+ private int takePosition;
+ private int usedSlots; // the count
+ private boolean closed;
+
+ public BoundedByteBuffer(int capacity) throws IllegalArgumentException {
+ // Make sure we don't deadlock on too small capacity.
+ if (capacity <= 0) {
+ throw new IllegalArgumentException("Capacity should be greater than zero");
+ }
+ buffer = new byte[capacity];
+ }
+
+ /**
+ * Returns the bytes available for {@link #read()}.
+ *
+ * @return the bytes available for reading.
+ */
+ public int size() {
+ return usedSlots;
+ }
+
+ /**
+ * Writes a single byte to the buffer. Blocks if the buffer is full.
+ *
+ * @param b byte to write to the buffer.
+ * @throws InterruptedException when the thread is interrupted while waiting for the buffer to become ready.
+ */
+ public void write(byte b) throws InterruptedException {
+ while (usedSlots == buffer.length) {
+ // Wait until not full.
+ wait();
+ }
+ buffer[putPosition] = b;
+ putPosition = (putPosition + 1) % buffer.length; // cyclically increment
+ if (usedSlots++ == 0) {
+ notifyAll();
+ }
+ }
+
+ public int getFreeSlots() {
+ return buffer.length - usedSlots;
+ }
+
+ public void write(byte[] b, int off, int len) throws InterruptedException {
+ Assert.isTrue(len <= getFreeSlots());
+ while (usedSlots == buffer.length) {
+ // Wait until not full.
+ wait();
+ }
+ int n = Math.min(len, buffer.length - putPosition);
+ System.arraycopy(b, off, buffer, putPosition, n);
+ if (putPosition + len > buffer.length) {
+ System.arraycopy(b, off + n, buffer, 0, len - n);
+ }
+ putPosition = (putPosition + len) % buffer.length; // cyclically increment
+ boolean wasEmpty = usedSlots == 0;
+ usedSlots += len;
+ if (wasEmpty) {
+ notifyAll();
+ }
+ }
+
+ /**
+ * Read a single byte. Blocks until a byte is available.
+ *
+ * @return a byte from the buffer.
+ * @throws InterruptedException when the thread is interrupted while waiting for the buffer to become ready.
+ */
+ public byte read() throws InterruptedException {
+ while (usedSlots == 0) {
+ if (closed) {
+ return -1;
+ }
+ // Wait until not empty.
+ wait();
+ }
+ byte b = buffer[takePosition];
+ takePosition = (takePosition + 1) % buffer.length;
+ if (usedSlots-- == buffer.length) {
+ notifyAll();
+ }
+ return b;
+ }
+
+ public int read(byte[] b, int off, int len) throws InterruptedException {
+ Assert.isTrue(len <= size());
+ while (usedSlots == 0) {
+ if (closed) {
+ return 0;
+ }
+ // Wait until not empty.
+ wait();
+ }
+ int n = Math.min(len, buffer.length - takePosition);
+ System.arraycopy(buffer, takePosition, b, off, n);
+ if (takePosition + len > n) {
+ System.arraycopy(buffer, 0, b, off + n, len - n);
+ }
+ takePosition = (takePosition + len) % buffer.length;
+ boolean wasFull = usedSlots == buffer.length;
+ usedSlots -= len;
+ if (wasFull) {
+ notifyAll();
+ }
+ return len;
+ }
+
+ public void close() {
+ closed = true;
+ notifyAll();
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+}
diff --git a/com.google.eclipse.elt.feature/.project b/com.google.eclipse.elt.feature/.project
new file mode 100644
index 0000000..d0fc7d6
--- /dev/null
+++ b/com.google.eclipse.elt.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.google.eclipse.elt.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.feature/build.properties b/com.google.eclipse.elt.feature/build.properties
new file mode 100644
index 0000000..b73855a
--- /dev/null
+++ b/com.google.eclipse.elt.feature/build.properties
@@ -0,0 +1,7 @@
+bin.includes = feature.xml,\
+ epl-v10.html,\
+ feature.properties
+src.includes = feature.xml,\
+ feature.properties,\
+ build.properties,\
+ epl-v10.html
diff --git a/com.google.eclipse.elt.feature/epl-v10.html b/com.google.eclipse.elt.feature/epl-v10.html
new file mode 100644
index 0000000..5443e16
--- /dev/null
+++ b/com.google.eclipse.elt.feature/epl-v10.html
@@ -0,0 +1,324 @@
+
+<!-- saved from url=(0088)https://workspacemechanic.eclipselabs.org.codespot.com/hg/features/mechanic/epl-v10.html -->
+<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-html40"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<meta name="ProgId" content="Word.Document">
+<meta name="Generator" content="Microsoft Word 9">
+<meta name="Originator" content="Microsoft Word 9">
+<link rel="File-List" href="https://workspacemechanic.eclipselabs.org.codespot.com/hg/features/mechanic/Eclipse%20EPL%202003_11_10%20Final_files/filelist.xml">
+<title>Eclipse Public License - Version 1.0</title>
+<!--[if gte mso 9]><xml>
+ <o:DocumentProperties>
+ <o:Revision>2</o:Revision>
+ <o:TotalTime>3</o:TotalTime>
+ <o:Created>2004-03-05T23:03:00Z</o:Created>
+ <o:LastSaved>2004-03-05T23:03:00Z</o:LastSaved>
+ <o:Pages>4</o:Pages>
+ <o:Words>1626</o:Words>
+ <o:Characters>9270</o:Characters>
+ <o:Lines>77</o:Lines>
+ <o:Paragraphs>18</o:Paragraphs>
+ <o:CharactersWithSpaces>11384</o:CharactersWithSpaces>
+ <o:Version>9.4402</o:Version>
+ </o:DocumentProperties>
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <w:WordDocument>
+ <w:TrackRevisions/>
+ </w:WordDocument>
+</xml><![endif]-->
+<style>
+<!--
+ /* Font Definitions */
+@font-face
+ {font-family:Tahoma;
+ panose-1:2 11 6 4 3 5 4 4 2 4;
+ mso-font-charset:0;
+ mso-generic-font-family:swiss;
+ mso-font-pitch:variable;
+ mso-font-signature:553679495 -2147483648 8 0 66047 0;}
+ /* Style Definitions */
+p.MsoNormal, li.MsoNormal, div.MsoNormal
+ {mso-style-parent:"";
+ margin:0in;
+ margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ font-size:12.0pt;
+ font-family:"Times New Roman";
+ mso-fareast-font-family:"Times New Roman";}
+p
+ {margin-right:0in;
+ mso-margin-top-alt:auto;
+ mso-margin-bottom-alt:auto;
+ margin-left:0in;
+ mso-pagination:widow-orphan;
+ font-size:12.0pt;
+ font-family:"Times New Roman";
+ mso-fareast-font-family:"Times New Roman";}
+p.BalloonText, li.BalloonText, div.BalloonText
+ {mso-style-name:"Balloon Text";
+ margin:0in;
+ margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ font-size:8.0pt;
+ font-family:Tahoma;
+ mso-fareast-font-family:"Times New Roman";}
+@page Section1
+ {size:8.5in 11.0in;
+ margin:1.0in 1.25in 1.0in 1.25in;
+ mso-header-margin:.5in;
+ mso-footer-margin:.5in;
+ mso-paper-source:0;}
+div.Section1
+ {page:Section1;}
+-->
+</style>
+</head>
+
+<body lang="EN-US" style="tab-interval:.5in">
+
+<div class="Section1">
+
+<p align="center" style="text-align:center"><b>Eclipse Public License - v 1.0</b>
+</p>
+
+<p><span style="font-size:10.0pt">THE ACCOMPANYING PROGRAM IS PROVIDED UNDER
+THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE,
+REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE
+OF THIS AGREEMENT.</span> </p>
+
+<p><b><span style="font-size:10.0pt">1. DEFINITIONS</span></b> </p>
+
+<p><span style="font-size:10.0pt">"Contribution" means:</span> </p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">a)
+in the case of the initial Contributor, the initial code and documentation
+distributed under this Agreement, and<br clear="left">
+b) in the case of each subsequent Contributor:</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">i)
+changes to the Program, and</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">ii)
+additions to the Program;</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">where
+such changes and/or additions to the Program originate from and are distributed
+by that particular Contributor. A Contribution 'originates' from a Contributor
+if it was added to the Program by such Contributor itself or anyone acting on
+such Contributor's behalf. Contributions do not include additions to the
+Program which: (i) are separate modules of software distributed in conjunction
+with the Program under their own license agreement, and (ii) are not derivative
+works of the Program. </span></p>
+
+<p><span style="font-size:10.0pt">"Contributor" means any person or
+entity that distributes the Program.</span> </p>
+
+<p><span style="font-size:10.0pt">"Licensed Patents " mean patent
+claims licensable by a Contributor which are necessarily infringed by the use
+or sale of its Contribution alone or when combined with the Program. </span></p>
+
+<p><span style="font-size:10.0pt">"Program" means the Contributions
+distributed in accordance with this Agreement.</span> </p>
+
+<p><span style="font-size:10.0pt">"Recipient" means anyone who
+receives the Program under this Agreement, including all Contributors.</span> </p>
+
+<p><b><span style="font-size:10.0pt">2. GRANT OF RIGHTS</span></b> </p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">a)
+Subject to the terms of this Agreement, each Contributor hereby grants Recipient
+a non-exclusive, worldwide, royalty-free copyright license to<span style="color:red"> </span>reproduce, prepare derivative works of, publicly
+display, publicly perform, distribute and sublicense the Contribution of such
+Contributor, if any, and such derivative works, in source code and object code
+form.</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">b)
+Subject to the terms of this Agreement, each Contributor hereby grants
+Recipient a non-exclusive, worldwide,<span style="color:green"> </span>royalty-free
+patent license under Licensed Patents to make, use, sell, offer to sell, import
+and otherwise transfer the Contribution of such Contributor, if any, in source
+code and object code form. This patent license shall apply to the combination
+of the Contribution and the Program if, at the time the Contribution is added
+by the Contributor, such addition of the Contribution causes such combination
+to be covered by the Licensed Patents. The patent license shall not apply to
+any other combinations which include the Contribution. No hardware per se is
+licensed hereunder. </span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">c)
+Recipient understands that although each Contributor grants the licenses to its
+Contributions set forth herein, no assurances are provided by any Contributor
+that the Program does not infringe the patent or other intellectual property
+rights of any other entity. Each Contributor disclaims any liability to Recipient
+for claims brought by any other entity based on infringement of intellectual
+property rights or otherwise. As a condition to exercising the rights and
+licenses granted hereunder, each Recipient hereby assumes sole responsibility
+to secure any other intellectual property rights needed, if any. For example,
+if a third party patent license is required to allow Recipient to distribute
+the Program, it is Recipient's responsibility to acquire that license before
+distributing the Program.</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">d)
+Each Contributor represents that to its knowledge it has sufficient copyright
+rights in its Contribution, if any, to grant the copyright license set forth in
+this Agreement. </span></p>
+
+<p><b><span style="font-size:10.0pt">3. REQUIREMENTS</span></b> </p>
+
+<p><span style="font-size:10.0pt">A Contributor may choose to distribute the
+Program in object code form under its own license agreement, provided that:</span>
+</p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">a)
+it complies with the terms and conditions of this Agreement; and</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">b)
+its license agreement:</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">i)
+effectively disclaims on behalf of all Contributors all warranties and
+conditions, express and implied, including warranties or conditions of title
+and non-infringement, and implied warranties or conditions of merchantability
+and fitness for a particular purpose; </span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">ii)
+effectively excludes on behalf of all Contributors all liability for damages,
+including direct, indirect, special, incidental and consequential damages, such
+as lost profits; </span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">iii)
+states that any provisions which differ from this Agreement are offered by that
+Contributor alone and not by any other party; and</span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">iv)
+states that source code for the Program is available from such Contributor, and
+informs licensees how to obtain it in a reasonable manner on or through a
+medium customarily used for software exchange.<span style="color:blue"> </span></span></p>
+
+<p><span style="font-size:10.0pt">When the Program is made available in source
+code form:</span> </p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">a)
+it must be made available under this Agreement; and </span></p>
+
+<p class="MsoNormal" style="margin-left:.5in"><span style="font-size:10.0pt">b) a
+copy of this Agreement must be included with each copy of the Program. </span></p>
+
+<p><span style="font-size:10.0pt">Contributors may not remove or alter any
+copyright notices contained within the Program. </span></p>
+
+<p><span style="font-size:10.0pt">Each Contributor must identify itself as the
+originator of its Contribution, if any, in a manner that reasonably allows
+subsequent Recipients to identify the originator of the Contribution. </span></p>
+
+<p><b><span style="font-size:10.0pt">4. COMMERCIAL DISTRIBUTION</span></b> </p>
+
+<p><span style="font-size:10.0pt">Commercial distributors of software may
+accept certain responsibilities with respect to end users, business partners
+and the like. While this license is intended to facilitate the commercial use
+of the Program, the Contributor who includes the Program in a commercial
+product offering should do so in a manner which does not create potential
+liability for other Contributors. Therefore, if a Contributor includes the
+Program in a commercial product offering, such Contributor ("Commercial
+Contributor") hereby agrees to defend and indemnify every other
+Contributor ("Indemnified Contributor") against any losses, damages and
+costs (collectively "Losses") arising from claims, lawsuits and other
+legal actions brought by a third party against the Indemnified Contributor to
+the extent caused by the acts or omissions of such Commercial Contributor in
+connection with its distribution of the Program in a commercial product
+offering. The obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement. In order
+to qualify, an Indemnified Contributor must: a) promptly notify the Commercial
+Contributor in writing of such claim, and b) allow the Commercial Contributor
+to control, and cooperate with the Commercial Contributor in, the defense and
+any related settlement negotiations. The Indemnified Contributor may participate
+in any such claim at its own expense.</span> </p>
+
+<p><span style="font-size:10.0pt">For example, a Contributor might include the
+Program in a commercial product offering, Product X. That Contributor is then a
+Commercial Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance claims and
+warranties are such Commercial Contributor's responsibility alone. Under this
+section, the Commercial Contributor would have to defend claims against the
+other Contributors related to those performance claims and warranties, and if a
+court requires any other Contributor to pay any damages as a result, the
+Commercial Contributor must pay those damages.</span> </p>
+
+<p><b><span style="font-size:10.0pt">5. NO WARRANTY</span></b> </p>
+
+<p><span style="font-size:10.0pt">EXCEPT AS EXPRESSLY SET FORTH IN THIS
+AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING,
+WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and distributing the
+Program and assumes all risks associated with its exercise of rights under this
+Agreement , including but not limited to the risks and costs of program errors,
+compliance with applicable laws, damage to or loss of data, programs or
+equipment, and unavailability or interruption of operations. </span></p>
+
+<p><b><span style="font-size:10.0pt">6. DISCLAIMER OF LIABILITY</span></b> </p>
+
+<p><span style="font-size:10.0pt">EXCEPT AS EXPRESSLY SET FORTH IN THIS
+AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF
+THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGES.</span> </p>
+
+<p><b><span style="font-size:10.0pt">7. GENERAL</span></b> </p>
+
+<p><span style="font-size:10.0pt">If any provision of this Agreement is invalid
+or unenforceable under applicable law, it shall not affect the validity or
+enforceability of the remainder of the terms of this Agreement, and without
+further action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.</span> </p>
+
+<p><span style="font-size:10.0pt">If Recipient institutes patent litigation
+against any entity (including a cross-claim or counterclaim in a lawsuit)
+alleging that the Program itself (excluding combinations of the Program with
+other software or hardware) infringes such Recipient's patent(s), then such
+Recipient's rights granted under Section 2(b) shall terminate as of the date
+such litigation is filed. </span></p>
+
+<p><span style="font-size:10.0pt">All Recipient's rights under this Agreement
+shall terminate if it fails to comply with any of the material terms or
+conditions of this Agreement and does not cure such failure in a reasonable
+period of time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use and
+distribution of the Program as soon as reasonably practicable. However,
+Recipient's obligations under this Agreement and any licenses granted by
+Recipient relating to the Program shall continue and survive. </span></p>
+
+<p><span style="font-size:10.0pt">Everyone is permitted to copy and distribute
+copies of this Agreement, but in order to avoid inconsistency the Agreement is
+copyrighted and may only be modified in the following manner. The Agreement
+Steward reserves the right to publish new versions (including revisions) of
+this Agreement from time to time. No one other than the Agreement Steward has
+the right to modify this Agreement. The Eclipse Foundation is the initial
+Agreement Steward. The Eclipse Foundation may assign the responsibility to
+serve as the Agreement Steward to a suitable separate entity. Each new version
+of the Agreement will be given a distinguishing version number. The Program
+(including Contributions) may always be distributed subject to the version of
+the Agreement under which it was received. In addition, after a new version of
+the Agreement is published, Contributor may elect to distribute the Program
+(including its Contributions) under the new version. Except as expressly stated
+in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to
+the intellectual property of any Contributor under this Agreement, whether
+expressly, by implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.</span> </p>
+
+<p><span style="font-size:10.0pt">This Agreement is governed by the laws of the
+State of New York and the intellectual property laws of the United States of
+America. No party to this Agreement will bring a legal action under this
+Agreement more than one year after the cause of action arose. Each party waives
+its rights to a jury trial in any resulting litigation.</span> </p>
+
+<p class="MsoNormal"><!--[if !supportEmptyParas]--> <!--[endif]--><o:p></o:p></p>
+
+</div>
+
+
+
+</body></html>
\ No newline at end of file
diff --git a/com.google.eclipse.elt.feature/feature.properties b/com.google.eclipse.elt.feature/feature.properties
new file mode 100644
index 0000000..7c9a8e4
--- /dev/null
+++ b/com.google.eclipse.elt.feature/feature.properties
@@ -0,0 +1,169 @@
+###############################################################################
+# Copyright (c) 2012 Google Inc
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+#
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Alex Ruiz - initial API and implementation
+###############################################################################
+# features.properties
+# contains externalized strings for feature.xml
+# "%foo" in feature.xml corresponds to the key "foo" in this file
+# java.io.Properties file (ISO 8859-1 with "\" escapes)
+# This file should be translated.
+
+# "featureName" property - name of the feature
+featureName=Terminal
+
+# "providerName" property - name of the company that provides the feature
+providerName=Google, Inc.
+
+# "updateSiteName" property - label for the update site
+updateSiteName=elt
+
+# "description" property - description of the feature
+description=Command-line terminal for Eclipse
+
+# copyright
+copyright=\
+Copyright (c) 2012 Google, Inc.\n\
+All rights reserved. This program and the accompanying materials\n\
+are made available under the terms of the Eclipse Public License v1.0\n\
+which accompanies this distribution, and is available at\n\
+http://www.eclipse.org/legal/epl-v10.html
+
+# "licenseURL" property - URL of the "Feature License"
+# do not translate value - just change to point to a locale-specific HTML page
+licenseURL=epl-v10.html
+
+# "license" property - text of the "Feature Update License"
+# should be plain text version of license agreement pointed to be "licenseURL"
+license=\
+Eclipse Foundation Software User Agreement\n\
+February 1, 2011\n\
+\n\
+Usage Of Content\n\
+\n\
+THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR\n\
+OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT").\n\
+USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS\n\
+AGREEMENT AND/OR THE TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR\n\
+NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU\n\
+AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED BY THIS AGREEMENT\n\
+AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS\n\
+OR NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE\n\
+TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS\n\
+OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\
+BELOW, THEN YOU MAY NOT USE THE CONTENT.\n\
+\n\
+Applicable Licenses\n\
+\n\
+Unless otherwise indicated, all Content made available by the\n\
+Eclipse Foundation is provided to you under the terms and conditions of\n\
+the Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is\n\
+provided with this Content and is also available at http://www.eclipse.org/legal/epl-v10.html.\n\
+For purposes of the EPL, "Program" will mean the Content.\n\
+\n\
+Content includes, but is not limited to, source code, object code,\n\
+documentation and other files maintained in the Eclipse Foundation source code\n\
+repository ("Repository") in software modules ("Modules") and made available\n\
+as downloadable archives ("Downloads").\n\
+\n\
+ - Content may be structured and packaged into modules to facilitate delivering,\n\
+ extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"),\n\
+ plug-in fragments ("Fragments"), and features ("Features").\n\
+ - Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java(TM) ARchive)\n\
+ in a directory named "plugins".\n\
+ - A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material.\n\
+ Each Feature may be packaged as a sub-directory in a directory named "features".\n\
+ Within a Feature, files named "feature.xml" may contain a list of the names and version\n\
+ numbers of the Plug-ins and/or Fragments associated with that Feature.\n\
+ - Features may also include other Features ("Included Features"). Within a Feature, files\n\
+ named "feature.xml" may contain a list of the names and version numbers of Included Features.\n\
+\n\
+The terms and conditions governing Plug-ins and Fragments should be\n\
+contained in files named "about.html" ("Abouts"). The terms and\n\
+conditions governing Features and Included Features should be contained\n\
+in files named "license.html" ("Feature Licenses"). Abouts and Feature\n\
+Licenses may be located in any directory of a Download or Module\n\
+including, but not limited to the following locations:\n\
+\n\
+ - The top-level (root) directory\n\
+ - Plug-in and Fragment directories\n\
+ - Inside Plug-ins and Fragments packaged as JARs\n\
+ - Sub-directories of the directory named "src" of certain Plug-ins\n\
+ - Feature directories\n\
+\n\
+Note: if a Feature made available by the Eclipse Foundation is installed using the\n\
+Provisioning Technology (as defined below), you must agree to a license ("Feature \n\
+Update License") during the installation process. If the Feature contains\n\
+Included Features, the Feature Update License should either provide you\n\
+with the terms and conditions governing the Included Features or inform\n\
+you where you can locate them. Feature Update Licenses may be found in\n\
+the "license" property of files named "feature.properties" found within a Feature.\n\
+Such Abouts, Feature Licenses, and Feature Update Licenses contain the\n\
+terms and conditions (or references to such terms and conditions) that\n\
+govern your use of the associated Content in that directory.\n\
+\n\
+THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER\n\
+TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS.\n\
+SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\
+\n\
+ - Eclipse Distribution License Version 1.0 (available at http://www.eclipse.org/licenses/edl-v1.0.html)\n\
+ - Common Public License Version 1.0 (available at http://www.eclipse.org/legal/cpl-v10.html)\n\
+ - Apache Software License 1.1 (available at http://www.apache.org/licenses/LICENSE)\n\
+ - Apache Software License 2.0 (available at http://www.apache.org/licenses/LICENSE-2.0)\n\
+ - Metro Link Public License 1.00 (available at http://www.opengroup.org/openmotif/supporters/metrolink/license.html)\n\
+ - Mozilla Public License Version 1.1 (available at http://www.mozilla.org/MPL/MPL-1.1.html)\n\
+\n\
+IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR\n\
+TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License\n\
+is provided, please contact the Eclipse Foundation to determine what terms and conditions\n\
+govern that particular Content.\n\
+\n\
+\n\Use of Provisioning Technology\n\
+\n\
+The Eclipse Foundation makes available provisioning software, examples of which include,\n\
+but are not limited to, p2 and the Eclipse Update Manager ("Provisioning Technology") for\n\
+the purpose of allowing users to install software, documentation, information and/or\n\
+other materials (collectively "Installable Software"). This capability is provided with\n\
+the intent of allowing such users to install, extend and update Eclipse-based products.\n\
+Information about packaging Installable Software is available at\n\
+http://eclipse.org/equinox/p2/repository_packaging.html ("Specification").\n\
+\n\
+You may use Provisioning Technology to allow other parties to install Installable Software.\n\
+You shall be responsible for enabling the applicable license agreements relating to the\n\
+Installable Software to be presented to, and accepted by, the users of the Provisioning Technology\n\
+in accordance with the Specification. By using Provisioning Technology in such a manner and\n\
+making it available in accordance with the Specification, you further acknowledge your\n\
+agreement to, and the acquisition of all necessary rights to permit the following:\n\
+\n\
+ 1. A series of actions may occur ("Provisioning Process") in which a user may execute\n\
+ the Provisioning Technology on a machine ("Target Machine") with the intent of installing,\n\
+ extending or updating the functionality of an Eclipse-based product.\n\
+ 2. During the Provisioning Process, the Provisioning Technology may cause third party\n\
+ Installable Software or a portion thereof to be accessed and copied to the Target Machine.\n\
+ 3. Pursuant to the Specification, you will provide to the user the terms and conditions that\n\
+ govern the use of the Installable Software ("Installable Software Agreement") and such\n\
+ Installable Software Agreement shall be accessed from the Target Machine in accordance\n\
+ with the Specification. Such Installable Software Agreement must inform the user of the\n\
+ terms and conditions that govern the Installable Software and must solicit acceptance by\n\
+ the end user in the manner prescribed in such Installable Software Agreement. Upon such\n\
+ indication of agreement by the user, the provisioning Technology will complete installation\n\
+ of the Installable Software.\n\
+\n\
+Cryptography\n\
+\n\
+Content may contain encryption software. The country in which you are\n\
+currently may have restrictions on the import, possession, and use,\n\
+and/or re-export to another country, of encryption software. BEFORE\n\
+using any encryption software, please check the country's laws,\n\
+regulations and policies concerning the import, possession, or use, and\n\
+re-export of encryption software, to see if this is permitted.\n\
+\n\
+Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.\n
+########### end of license property ##########################################
diff --git a/com.google.eclipse.elt.feature/feature.xml b/com.google.eclipse.elt.feature/feature.xml
new file mode 100644
index 0000000..969eac7
--- /dev/null
+++ b/com.google.eclipse.elt.feature/feature.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="com.google.eclipse.elt"
+ label="%featureName"
+ version="1.1.0.qualifier"
+ provider-name="%providerName">
+
+ <description url="https://code.google.com/p/elt/">
+ %description
+ </description>
+
+ <copyright>
+ %description
+ </copyright>
+
+ <license url="%licenseURL">
+ %license
+ </license>
+
+ <url>
+ <update label="%updateSiteName" url="http://elt.googlecode.com/git/update-site"/>
+ </url>
+
+ <requires>
+ <import plugin="org.eclipse.core.runtime" version="3.7.0" match="compatible"/>
+ <import plugin="org.eclipse.ui"/>
+ <import plugin="org.eclipse.jface.text"/>
+ <import plugin="org.eclipse.core.resources"/>
+ <import plugin="com.ibm.icu" version="4.4.2" match="greaterOrEqual"/>
+ <import plugin="org.eclipse.debug.core"/>
+ <import plugin="com.google.eclipse.elt.pty" version="1.1.0" match="greaterOrEqual"/>
+ <import plugin="com.google.eclipse.elt.emulator" version="1.1.0" match="greaterOrEqual"/>
+ </requires>
+
+ <plugin
+ id="com.google.eclipse.elt.emulator"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.google.eclipse.elt.macosx"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ fragment="true"/>
+
+ <plugin
+ id="com.google.eclipse.elt.pty"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.google.eclipse.elt.pty.linux"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ fragment="true"/>
+
+ <plugin
+ id="com.google.eclipse.elt.pty.linux.x86"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ fragment="true"/>
+
+ <plugin
+ id="com.google.eclipse.elt.pty.linux.x86_64"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ fragment="true"/>
+
+ <plugin
+ id="com.google.eclipse.elt.view"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+</feature>
diff --git a/com.google.eclipse.elt.pty.linux.x86/.project b/com.google.eclipse.elt.pty.linux.x86/.project
new file mode 100644
index 0000000..6b9e649
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86/.project
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.google.eclipse.elt.pty.linux.x86</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ </natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.pty.linux.x86/META-INF/MANIFEST.MF b/com.google.eclipse.elt.pty.linux.x86/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..79238ec
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %fragmentName.linux.x86
+Bundle-SymbolicName: com.google.eclipse.elt.pty.linux.x86;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-Vendor: %providerName
+Fragment-Host: com.google.eclipse.elt.pty;bundle-version="1.1.0"
+Bundle-Localization: plugin
+Eclipse-PlatformFilter: (&(osgi.os=linux)(osgi.arch=x86))
diff --git a/com.google.eclipse.elt.pty.linux.x86/build.properties b/com.google.eclipse.elt.pty.linux.x86/build.properties
new file mode 100644
index 0000000..e16c8a6
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86/build.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 2005, 2009 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+bin.includes = os/,\
+ META-INF/
diff --git a/com.google.eclipse.elt.pty.linux.x86/os/linux/x86/libgpty.so b/com.google.eclipse.elt.pty.linux.x86/os/linux/x86/libgpty.so
new file mode 100644
index 0000000..60088bc
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86/os/linux/x86/libgpty.so
Binary files differ
diff --git a/com.google.eclipse.elt.pty.linux.x86/os/linux/x86/libgspawner.so b/com.google.eclipse.elt.pty.linux.x86/os/linux/x86/libgspawner.so
new file mode 100644
index 0000000..b6fb632
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86/os/linux/x86/libgspawner.so
Binary files differ
diff --git a/com.google.eclipse.elt.pty.linux.x86_64/.project b/com.google.eclipse.elt.pty.linux.x86_64/.project
new file mode 100644
index 0000000..12c9497
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86_64/.project
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.google.eclipse.elt.pty.linux.x86_64</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ </natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.pty.linux.x86_64/META-INF/MANIFEST.MF b/com.google.eclipse.elt.pty.linux.x86_64/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5d6b87b
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86_64/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %fragmentName.linux.x86_64
+Bundle-SymbolicName: com.google.eclipse.elt.pty.linux.x86_64;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-Vendor: %providerName
+Fragment-Host: com.google.eclipse.elt.pty;bundle-version="1.1.0"
+Bundle-Localization: plugin
+Eclipse-PlatformFilter: (&(osgi.os=linux)(osgi.arch=x86_64))
diff --git a/com.google.eclipse.elt.pty.linux.x86_64/build.properties b/com.google.eclipse.elt.pty.linux.x86_64/build.properties
new file mode 100644
index 0000000..9faf19a
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86_64/build.properties
@@ -0,0 +1,14 @@
+###############################################################################
+# Copyright (c) 2005, 2009 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+bin.includes = os/,\
+ about.html,\
+ META-INF/
+src.includes = about.html
diff --git a/com.google.eclipse.elt.pty.linux.x86_64/os/linux/x86_64/libgpty.so b/com.google.eclipse.elt.pty.linux.x86_64/os/linux/x86_64/libgpty.so
new file mode 100644
index 0000000..587fa69
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86_64/os/linux/x86_64/libgpty.so
Binary files differ
diff --git a/com.google.eclipse.elt.pty.linux.x86_64/os/linux/x86_64/libgspawner.so b/com.google.eclipse.elt.pty.linux.x86_64/os/linux/x86_64/libgspawner.so
new file mode 100644
index 0000000..18736e1
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux.x86_64/os/linux/x86_64/libgspawner.so
Binary files differ
diff --git a/com.google.eclipse.elt.pty.linux/.classpath b/com.google.eclipse.elt.pty.linux/.classpath
new file mode 100644
index 0000000..7d9b38e
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/com.google.eclipse.elt.pty.linux/.gitignore b/com.google.eclipse.elt.pty.linux/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/com.google.eclipse.elt.pty.linux/.project b/com.google.eclipse.elt.pty.linux/.project
new file mode 100644
index 0000000..2ca6207
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.google.eclipse.elt.pty.linux</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ </natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.pty.linux/.settings/org.eclipse.jdt.core.prefs b/com.google.eclipse.elt.pty.linux/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..c537b63
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/com.google.eclipse.elt.pty.linux/META-INF/MANIFEST.MF b/com.google.eclipse.elt.pty.linux/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..add2061
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %fragmentName.linux
+Bundle-SymbolicName: com.google.eclipse.elt.pty.linux;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-ClassPath: cdt_linux.jar
+Bundle-Vendor: %providerName
+Fragment-Host: com.google.eclipse.elt.pty;bundle-version="1.1.0"
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Eclipse-PlatformFilter: (osgi.os=linux)
diff --git a/com.google.eclipse.elt.pty.linux/build.properties b/com.google.eclipse.elt.pty.linux/build.properties
new file mode 100644
index 0000000..6542e91
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/build.properties
@@ -0,0 +1,17 @@
+###############################################################################
+# Copyright (c) 2005, 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+bin.includes = fragment.xml,\
+ about.html,\
+ .,\
+ META-INF/
+src.includes = about.html,\
+ library/
+source.. = src/
diff --git a/com.google.eclipse.elt.pty.linux/fragment.xml b/com.google.eclipse.elt.pty.linux/fragment.xml
new file mode 100644
index 0000000..aedc72a
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/fragment.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<fragment>
+
+</fragment>
diff --git a/com.google.eclipse.elt.pty.linux/library/Makefile b/com.google.eclipse.elt.pty.linux/library/Makefile
new file mode 100644
index 0000000..6e67977
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/Makefile
@@ -0,0 +1,44 @@
+# makefile for libspawner.so
+
+ifeq ($(JAVA_HOME),)
+$(warning JAVA_HOME not set in environment)
+endif
+
+# Defaults which can be overridden.
+OS = linux
+ARCH = x86
+
+JDK_INCLUDES= $(JAVA_HOME)/include
+JDK_OS_INCLUDES= $(JAVA_HOME)/include/$(OS)
+
+CC=gcc
+CPPFLAGS = -I. -I$(JDK_INCLUDES) -I$(JDK_OS_INCLUDES)
+CFLAGS +=-fpic -D_REENTRANT -D_GNU_SOURCE
+
+INSTALL_DIR = ../../org.eclipse.cdt.core.linux.$(ARCH)/os/$(OS)/$(ARCH)
+
+LIB_NAME_SPAWNER = libspawner.so
+LIB_NAME_FULL_SPAWNER = $(INSTALL_DIR)/libspawner.so
+OBJS_SPAWNER=spawner.o io.o exec_unix.o exec_pty.o pfind.o openpty.o
+
+LIB_NAME_PTY = libpty.so
+LIB_NAME_FULL_PTY = $(INSTALL_DIR)/libpty.so
+OBJS_PTY= openpty.o pty.o ptyio.o
+
+OBJS = $(OBJS_SPAWNER) $(OBJS_PTY)
+
+all: $(LIB_NAME_FULL_SPAWNER) $(LIB_NAME_FULL_PTY)
+
+rebuild: clean all
+
+$(LIB_NAME_FULL_SPAWNER) : $(OBJS_SPAWNER)
+ mkdir -p $(INSTALL_DIR)
+ $(CC) -g -shared -Wl,-soname,$(LIB_NAME_SPAWNER) $(LDFLAGS) -o $(LIB_NAME_FULL_SPAWNER) $(OBJS_SPAWNER) -lc
+
+$(LIB_NAME_FULL_PTY): $(OBJS_PTY)
+ mkdir -p $(INSTALL_DIR)
+ $(CC) -g -shared -Wl,-soname,$(LIB_NAME_PTY) $(LDFLAGS) -o $(LIB_NAME_FULL_PTY) $(OBJS_PTY)
+
+clean :
+ $(RM) $(OBJS_SPAWNER) $(LIB_NAME_FULL_SPAWNER)
+ $(RM) $(OBJS_PTY) $(LIB_NAME_FULL_PTY)
diff --git a/com.google.eclipse.elt.pty.linux/library/PTY.h b/com.google.eclipse.elt.pty.linux/library/PTY.h
new file mode 100644
index 0000000..f717942
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/PTY.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_pty_PTY */
+
+#ifndef _Included_org_eclipse_cdt_utils_pty_PTY
+#define _Included_org_eclipse_cdt_utils_pty_PTY
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTY
+ * Method: openMaster
+ * Signature: (Z)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_eclipse_cdt_utils_pty_PTY_openMaster
+ (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTY
+ * Method: change_window_size
+ * Signature: (III)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_change_1window_1size
+ (JNIEnv *, jobject, jint, jint, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/PTYInputStream.h b/com.google.eclipse.elt.pty.linux/library/PTYInputStream.h
new file mode 100644
index 0000000..e734927
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/PTYInputStream.h
@@ -0,0 +1,32 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_pty_PTYInputStream */
+
+#ifndef _Included_org_eclipse_cdt_utils_pty_PTYInputStream
+#define _Included_org_eclipse_cdt_utils_pty_PTYInputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef org_eclipse_cdt_utils_pty_PTYInputStream_SKIP_BUFFER_SIZE
+#define org_eclipse_cdt_utils_pty_PTYInputStream_SKIP_BUFFER_SIZE 2048L
+/* Inaccessible static: skipBuffer */
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method: read0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYInputStream_read0
+ (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYInputStream_close0
+ (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/PTYOutputStream.h b/com.google.eclipse.elt.pty.linux/library/PTYOutputStream.h
new file mode 100644
index 0000000..fb28491
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/PTYOutputStream.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_pty_PTYOutputStream */
+
+#ifndef _Included_org_eclipse_cdt_utils_pty_PTYOutputStream
+#define _Included_org_eclipse_cdt_utils_pty_PTYOutputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method: write0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYOutputStream_write0
+ (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYOutputStream_close0
+ (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/Spawner.h b/com.google.eclipse.elt.pty.linux/library/Spawner.h
new file mode 100644
index 0000000..02727f9
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/Spawner.h
@@ -0,0 +1,53 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_spawner_Spawner */
+
+#ifndef _Included_org_eclipse_cdt_utils_spawner_Spawner
+#define _Included_org_eclipse_cdt_utils_spawner_Spawner
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: exec0
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0
+ (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring, jintArray);
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: exec1
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec1
+ (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring);
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: exec2
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[ILjava/lang/String;IZ)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec2
+ (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring, jintArray, jstring, jint, jboolean);
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: raise
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_raise
+ (JNIEnv *, jobject, jint, jint);
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: waitFor
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor
+ (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/SpawnerInputStream.h b/com.google.eclipse.elt.pty.linux/library/SpawnerInputStream.h
new file mode 100644
index 0000000..3b32d2b
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/SpawnerInputStream.h
@@ -0,0 +1,32 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_qnx_tools_utils_spawner_SpawnerInputStream */
+
+#ifndef _Included_com_qnx_tools_utils_spawner_SpawnerInputStream
+#define _Included_com_qnx_tools_utils_spawner_SpawnerInputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef com_qnx_tools_utils_spawner_SpawnerInputStream_SKIP_BUFFER_SIZE
+#define com_qnx_tools_utils_spawner_SpawnerInputStream_SKIP_BUFFER_SIZE 2048L
+/* Inaccessible static: skipBuffer */
+/*
+ * Class: org_elipse_cdt_utils_spawner_SpawnerInputStream
+ * Method: read0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_read0
+ (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerInputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_close0
+ (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/SpawnerOutputStream.h b/com.google.eclipse.elt.pty.linux/library/SpawnerOutputStream.h
new file mode 100644
index 0000000..f835947
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/SpawnerOutputStream.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_qnx_tools_utils_spawner_SpawnerOutputStream */
+
+#ifndef _Included_com_qnx_tools_utils_spawner_SpawnerOutputStream
+#define _Included_com_qnx_tools_utils_spawner_SpawnerOutputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerOutputStream
+ * Method: write0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_write0
+ (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerOutputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_close0
+ (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/exec0.h b/com.google.eclipse.elt.pty.linux/library/exec0.h
new file mode 100644
index 0000000..3033ec1
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/exec0.h
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2010 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ * Wind River Systems, Inc.
+ * Mikhail Zabaluev (Nokia) - bug 82744
+ *******************************************************************************/
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+
+extern pid_t exec0(const char *path, char *const argv[],
+ char *const envp[], const char *dirpath,
+ int channels[3]);
+
+
+extern pid_t exec_pty(const char *path, char *const argv[],
+ char *const envp[], const char *dirpath,
+ int channels[3], const char *pts_name, int fdm,
+ int console);
+
+extern int wait0(pid_t pid);
diff --git a/com.google.eclipse.elt.pty.linux/library/exec_pty.c b/com.google.eclipse.elt.pty.linux/library/exec_pty.c
new file mode 100644
index 0000000..dd68615
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/exec_pty.c
@@ -0,0 +1,186 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2010 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ * Wind River Systems, Inc.
+ * Mikhail Zabaluev (Nokia) - bug 82744
+ * Mikhail Sennikovsky - bug 145737
+ *******************************************************************************/
+#include "exec0.h"
+#include "openpty.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <termios.h>
+
+/* from pfind.c */
+extern char *pfind(const char *name, char * const envp[]);
+
+pid_t
+exec_pty(const char *path, char *const argv[], char *const envp[],
+ const char *dirpath, int channels[3], const char *pts_name, int fdm, int console)
+{
+ int pipe2[2];
+ pid_t childpid;
+ char *full_path;
+
+ /*
+ * We use pfind() to check that the program exists and is an executable.
+ * If not pass the error up. Also execve() wants a full path.
+ */
+ full_path = pfind(path, envp);
+ if (full_path == NULL) {
+ fprintf(stderr, "Unable to find full path for \"%s\"\n", (path) ? path : "");
+ return -1;
+ }
+
+ /*
+ * Make sure we can create our pipes before forking.
+ */
+ if (channels != NULL && console) {
+ if (pipe(pipe2) < 0) {
+ fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ free(full_path);
+ return -1;
+ }
+ }
+
+ childpid = fork();
+
+ if (childpid < 0) {
+ fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ free(full_path);
+ return -1;
+ } else if (childpid == 0) { /* child */
+
+ chdir(dirpath);
+
+ if (channels != NULL) {
+ int fds;
+
+ if (!console && setsid() < 0) {
+ perror("setsid()");
+ return -1;
+ }
+
+ fds = ptys_open(fdm, pts_name);
+ if (fds < 0) {
+ fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ return -1;
+ }
+
+ /* Close the read end of pipe2 */
+ if (console && close(pipe2[0]) == -1) {
+ perror("close(pipe2[0]))");
+ }
+
+ /* close the master, no need in the child */
+ close(fdm);
+
+ if (console) {
+ set_noecho(fds);
+ if (setpgid(getpid(), getpid()) < 0) {
+ perror("setpgid()");
+ return -1;
+ }
+ }
+
+ /* redirections */
+ dup2(fds, STDIN_FILENO); /* dup stdin */
+ dup2(fds, STDOUT_FILENO); /* dup stdout */
+ if (console) {
+ dup2(pipe2[1], STDERR_FILENO); /* dup stderr */
+ } else {
+ dup2(fds, STDERR_FILENO); /* dup stderr */
+ }
+ close(fds); /* done with fds. */
+ }
+
+ /* Close all the fd's in the child */
+ {
+ int fdlimit = sysconf(_SC_OPEN_MAX);
+ int fd = 3;
+
+ while (fd < fdlimit)
+ close(fd++);
+ }
+
+ if (envp[0] == NULL) {
+ execv(full_path, argv);
+ } else {
+ execve(full_path, argv, envp);
+ }
+
+ _exit(127);
+
+ } else if (childpid != 0) { /* parent */
+ if (console) {
+ set_noecho(fdm);
+ }
+ if (channels != NULL) {
+ channels[0] = fdm; /* Input Stream. */
+ channels[1] = fdm; /* Output Stream. */
+ if (console) {
+ /* close the write end of pipe1 */
+ if (close(pipe2[1]) == -1)
+ perror("close(pipe2[1])");
+ channels[2] = pipe2[0]; /* stderr Stream. */
+ } else {
+ channels[2] = fdm; /* Error Stream. */
+ }
+ }
+
+ free(full_path);
+ return childpid;
+ }
+
+ free(full_path);
+ return -1; /*NOT REACHED */
+}
+#ifdef __STAND_ALONE__
+int main(int argc, char **argv, char **envp) {
+ const char *path = "./bufferring_test";
+ int channels[3] = { -1, -1, -1};
+ int status;
+ FILE *app_stdin;
+ FILE *app_stdout;
+ FILE *app_stderr;
+ char pts_name[32];
+ int fdm;
+ char buffer[32];
+
+ fdm = ptym_open(pts_name);
+ status = exec_pty(path, argv, envp, ".", channels, pts_name, fdm);
+ if (status >= 0) {
+ app_stdin = fdopen(channels[0], "w");
+ app_stdout = fdopen(channels[1], "r");
+ app_stderr = fdopen(channels[2], "r");
+ if (app_stdout == NULL || app_stderr == NULL || app_stdin == NULL) {
+ fprintf(stderr, "PROBLEMS\n");
+ } else {
+ fputs("foo\n", app_stdin);
+ fputs("bar\n", app_stdin);
+ while(fgets(buffer, sizeof buffer, app_stdout) != NULL) {
+ fprintf(stdout, "STDOUT: %s\n", buffer);
+ }
+ while(fgets(buffer, sizeof buffer, app_stderr) != NULL) {
+ fprintf(stdout, "STDERR: %s\n", buffer);
+ }
+ }
+ }
+ fputs("bye\n", stdout);
+ close(channels[0]);
+ close(channels[1]);
+ close(channels[2]);
+ return 0;
+}
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/exec_unix.c b/com.google.eclipse.elt.pty.linux/library/exec_unix.c
new file mode 100644
index 0000000..ced7b74
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/exec_unix.c
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2010 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ * Wind River Systems, Inc.
+ * Mikhail Sennikovsky - bug 145737
+ *******************************************************************************/
+#include "exec0.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <libgen.h>
+#include <stdlib.h>
+
+/* from pfind.c */
+extern char *pfind(const char *name, char * const envp[]);
+
+pid_t
+exec0(const char *path, char *const argv[], char *const envp[],
+ const char *dirpath, int channels[3])
+{
+ int pipe0[2], pipe1[2], pipe2[2];
+ pid_t childpid;
+ char *full_path;
+
+ /*
+ * We use pfind() to check that the program exists and is an executable.
+ * If not pass the error up. Also execve() wants a full path.
+ */
+ full_path = pfind(path, envp);
+ if (full_path == NULL) {
+ fprintf(stderr, "Unable to find full path for \"%s\"\n", (path) ? path : "");
+ return -1;
+ }
+
+ /*
+ * Make sure we can create our pipes before forking.
+ */
+ if (channels != NULL) {
+ if (pipe(pipe0) < 0 || pipe(pipe1) < 0 || pipe(pipe2) < 0) {
+ fprintf(stderr, "%s(%d): returning due to error.\n",
+ __FUNCTION__, __LINE__);
+ free(full_path);
+ return -1;
+ }
+ }
+
+ childpid = fork();
+
+ if (childpid < 0) {
+ fprintf(stderr, "%s(%d): returning due to error: %s\n",
+ __FUNCTION__, __LINE__, strerror(errno));
+ free(full_path);
+ return -1;
+ } else if (childpid == 0) { /* child */
+ char *ptr;
+
+ chdir(dirpath);
+
+ if (channels != NULL) {
+ /* Close the write end of pipe0 */
+ if (close(pipe0[1]) == -1)
+ perror("close(pipe0[1])");
+
+ /* Close the read end of pipe1 */
+ if (close(pipe1[0]) == -1)
+ perror("close(pipe1[0])");
+
+ /* Close the read end of pipe2 */
+ if (close(pipe2[0]) == -1)
+ perror("close(pipe2[0]))");
+
+ /* redirections */
+ dup2(pipe0[0], STDIN_FILENO); /* dup stdin */
+ dup2(pipe1[1], STDOUT_FILENO); /* dup stdout */
+ dup2(pipe2[1], STDERR_FILENO); /* dup stderr */
+ }
+
+ /* Close all the fd's in the child */
+ {
+ int fdlimit = sysconf(_SC_OPEN_MAX);
+ int fd = 3;
+
+ while (fd < fdlimit)
+ close(fd++);
+ }
+
+ setpgid(getpid(), getpid());
+
+ if (envp[0] == NULL) {
+ execv(full_path, argv);
+ } else {
+ execve(full_path, argv, envp);
+ }
+
+ _exit(127);
+
+ } else if (childpid != 0) { /* parent */
+
+ char b;
+
+ if (channels != NULL) {
+ /* close the read end of pipe1 */
+ if (close(pipe0[0]) == -1)
+ perror("close(pipe0[0])");
+
+ /* close the write end of pipe2 */
+ if (close(pipe1[1]) == -1)
+ perror("close(pipe1[1])");
+
+ /* close the write end of pipe2 */
+ if (close(pipe2[1]) == -1)
+ perror("close(pipe2[1])");
+
+ channels[0] = pipe0[1]; /* Output Stream. */
+ channels[1] = pipe1[0]; /* Input Stream. */
+ channels[2] = pipe2[0]; /* Input Stream. */
+ }
+
+ free(full_path);
+ return childpid;
+ }
+
+ free(full_path);
+ return -1; /*NOT REACHED */
+}
+
+
+int wait0(pid_t pid)
+{
+ int status;
+ int val = -1;
+
+ if (pid < 0)
+ return -1;
+
+ for (;;) {
+ if (waitpid(pid, &status, 0) < 0) {
+ if (errno == EINTR) {
+ // interrupted system call - retry
+ continue;
+ }
+ }
+ break;
+ }
+ if (WIFEXITED(status)) {
+ val = WEXITSTATUS(status);
+ }
+
+ return val;
+}
diff --git a/com.google.eclipse.elt.pty.linux/library/io.c b/com.google.eclipse.elt.pty.linux/library/io.c
new file mode 100644
index 0000000..8540b82
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/io.c
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2006 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ * Wind River Systems, Inc.
+ *******************************************************************************/
+#include <jni.h>
+#include <stdio.h>
+#include <SpawnerInputStream.h>
+#include <SpawnerOutputStream.h>
+#include <unistd.h>
+
+/* Header for class _org_eclipse_cdt_utils_spawner_SpawnerInputStream */
+/* Header for class _org_eclipse_cdt_utils_spawner_SpawnerOutputStream */
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerInputStream
+ * Method: read0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_read0(JNIEnv * env,
+ jobject jobj,
+ jint jfd,
+ jbyteArray buf,
+ jint buf_len)
+{
+ int fd;
+ int status;
+ jbyte *data;
+ int data_len;
+
+ data = (*env)->GetByteArrayElements(env, buf, 0);
+ data_len = buf_len;
+ fd = jfd;
+
+ status = read( fd, data, data_len );
+ (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+ if (status == 0) {
+ /* EOF. */
+ status = -1;
+ } else if (status == -1) {
+ /* Error, toss an exception */
+ jclass exception = (*env)->FindClass(env, "java/io/IOException");
+ if (exception == NULL) {
+ /* Give up. */
+ return -1;
+ }
+ (*env)->ThrowNew(env, exception, "read error");
+ }
+
+ return status;
+}
+
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerInputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_close0(JNIEnv * env,
+ jobject jobj,
+ jint fd)
+{
+ return close(fd);
+}
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerOutputStream
+ * Method: write0
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_write0(JNIEnv * env,
+ jobject jobj,
+ jint jfd,
+ jbyteArray buf,
+ jint buf_len)
+{
+ int status;
+ int fd;
+ jbyte *data;
+ int data_len;
+
+ data = (*env)->GetByteArrayElements(env, buf, 0);
+ data_len = buf_len;
+ fd = jfd;
+
+ status = write(fd, data, data_len);
+ (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+ return status;
+}
+
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerOutputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_close0(JNIEnv * env,
+ jobject jobj,
+ jint fd)
+{
+ return close(fd);
+}
diff --git a/com.google.eclipse.elt.pty.linux/library/openpty.c b/com.google.eclipse.elt.pty.linux/library/openpty.c
new file mode 100644
index 0000000..818ca8e
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/openpty.c
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2010 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ * Wind River Systems, Inc.
+ * Mikhail Zabaluev (Nokia) - bug 82744
+ * Corey Ashford (IBM) - bug 272370, bug 272372
+ *******************************************************************************/
+
+/* _XOPEN_SOURCE is needed to bring in the header for ptsname */
+#define _XOPEN_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <grp.h>
+
+#include <stdlib.h>
+
+/**
+ * This is taken from R. W. Stevens book.
+ * Alain Magloire.
+ */
+
+int ptym_open (char *pts_name);
+int ptys_open (int fdm, const char * pts_name);
+void set_noecho(int fd);
+
+int
+openpty(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp)
+{
+ char line[20];
+ line[0]=0;
+ *amaster = ptym_open(line);
+ if (*amaster < 0)
+ return -1;
+ *aslave = ptys_open(*amaster, line);
+ if (*aslave < 0) {
+ close(*amaster);
+ return -1;
+ }
+
+ if (name)
+ strcpy(name, line);
+#ifndef TCSAFLUSH
+#define TCSAFLUSH TCSETAF
+#endif
+ if (termp)
+ (void) tcsetattr(*aslave, TCSAFLUSH, termp);
+#ifdef TIOCSWINSZ
+ if (winp)
+ (void) ioctl(*aslave, TIOCSWINSZ, (char *)winp);
+#endif
+ return 0;
+}
+
+void
+set_noecho(int fd)
+{
+ struct termios stermios;
+ if (tcgetattr(fd, &stermios) < 0) {
+ return ;
+ }
+
+ /* turn off echo */
+ stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+ /* Turn off the NL to CR/NL mapping ou output. */
+ /*stermios.c_oflag &= ~(ONLCR);*/
+
+ stermios.c_iflag |= (IGNCR);
+
+ tcsetattr(fd, TCSANOW, &stermios);
+}
+
+int
+ptym_open(char * pts_name)
+{
+ int fdm;
+ char *ptr;
+
+ strcpy(pts_name, "/dev/ptmx");
+ fdm = getpt();
+ if (fdm < 0)
+ return -1;
+ if (grantpt(fdm) < 0) { /* grant access to slave */
+ close(fdm);
+ return -2;
+ }
+ if (unlockpt(fdm) < 0) { /* clear slave's lock flag */
+ close(fdm);
+ return -3;
+ }
+ ptr = ptsname(fdm);
+ if (ptr == NULL) { /* get slave's name */
+ close (fdm);
+ return -4;
+ }
+ strcpy(pts_name, ptr); /* return name of slave */
+ return fdm; /* return fd of master */
+}
+
+int
+ptys_open(int fdm, const char * pts_name)
+{
+ int fds;
+ /* following should allocate controlling terminal */
+ fds = open(pts_name, O_RDWR);
+ if (fds < 0) {
+ close(fdm);
+ return -5;
+ }
+
+#if defined(TIOCSCTTY)
+ /* TIOCSCTTY is the BSD way to acquire a controlling terminal. */
+ if (ioctl(fds, TIOCSCTTY, (char *)0) < 0) {
+ // ignore error: this is expected in console-mode
+ }
+#endif
+ return fds;
+}
diff --git a/com.google.eclipse.elt.pty.linux/library/openpty.h b/com.google.eclipse.elt.pty.linux/library/openpty.h
new file mode 100644
index 0000000..c6475fa
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/openpty.h
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2010 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ * Wind River Systems, Inc.
+ * Mikhail Zabaluev (Nokia) - bug 82744
+ *******************************************************************************/
+#ifndef _OPENPTY_H
+#define _OPENPTY_H
+int ptym_open (char *pts_name);
+int ptys_open (int fdm, const char * pts_name);
+void set_noecho(int fd);
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/pfind.c b/com.google.eclipse.elt.pty.linux/library/pfind.c
new file mode 100644
index 0000000..519c6c3
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/pfind.c
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2010 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ * Wind River Systems, Inc.
+ * Mikhail Sennikovsky - bug 145737
+ * Everton Rufino Constantino (IBM) - bug 237611
+ *******************************************************************************/
+/*
+ * pfind.c - Search for a binary in $PATH.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define PATH_DEF "PATH="
+const int path_def_len = 5; /* strlen(PATH_DEF); */
+
+char * path_val(char * const envp[])
+{
+ int i;
+ if (envp == NULL || envp[0] == NULL)
+ return getenv("PATH" );
+
+ for(i = 0; envp[i] != NULL; i++){
+ char* p = envp[i];
+ if(!strncmp(PATH_DEF, p, path_def_len)){
+ return p + path_def_len;
+ }
+ }
+
+ return NULL;
+}
+
+char * pfind(const char *name, char * const envp[])
+{
+ char *tok;
+ char *sp;
+ char *path;
+ char fullpath[PATH_MAX+1];
+
+ /* Sanity check. */
+ if (name == NULL) {
+ fprintf(stderr, "pfind(): Null argument.\n");
+ return NULL;
+ }
+
+ /* For absolute name or name with a path, check if it is an executable. */
+ if (name[0] == '/' || name[0] == '.') {
+ if (access(name, X_OK) == 0) {
+ return strdup(name);
+ }
+ return NULL;
+ }
+
+ /* Search in the PATH environment. */
+ path = path_val( envp );
+
+ if (path == NULL || strlen(path) <= 0) {
+ fprintf(stderr, "Unable to get $PATH.\n");
+ return NULL;
+ }
+
+ /* The value return by getenv() is readonly */
+ path = strdup(path);
+
+ tok = strtok_r(path, ":", &sp);
+ while (tok != NULL) {
+ snprintf(fullpath, sizeof(fullpath) - 1, "%s/%s", tok, name);
+
+ if (access(fullpath, X_OK) == 0) {
+ free(path);
+ return strdup(fullpath);
+ }
+
+ tok = strtok_r( NULL, ":", &sp );
+ }
+
+ free(path);
+ return NULL;
+}
+
+#ifdef BUILD_WITH_MAIN
+int main(int argc, char **argv)
+{
+ int i;
+ char *fullpath;
+
+ for (i=1; i<argc; i++) {
+ fullpath = pfind(argv[i], NULL);
+ if (fullpath == NULL)
+ printf("Unable to find %s in $PATH.\n", argv[i]);
+ else
+ printf("Found %s @ %s.\n", argv[i], fullpath);
+ }
+}
+#endif
diff --git a/com.google.eclipse.elt.pty.linux/library/pty.c b/com.google.eclipse.elt.pty.linux/library/pty.c
new file mode 100644
index 0000000..5c1e864
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/pty.c
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2010 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ * Wind River Systems, Inc.
+ *******************************************************************************/
+#include <sys/ioctl.h>
+#include "PTY.h"
+#include "openpty.h"
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTY
+ * Method: forkpty
+ * Signature: ()I
+ */
+JNIEXPORT jstring JNICALL
+Java_org_eclipse_cdt_utils_pty_PTY_openMaster (JNIEnv *env, jobject jobj, jboolean console) {
+ jfieldID fid; /* Store the field ID */
+ jstring jstr = NULL;
+ int master = -1;
+ char line[1024]; /* FIXME: Should be enough */
+ jclass cls;
+
+ line[0] = '\0';
+
+ master = ptym_open(line);
+ if (master >= 0) {
+ // turn off echo
+ if (console) {
+ set_noecho(master);
+ }
+
+ /* Get a reference to the obj's class */
+ cls = (*env)->GetObjectClass(env, jobj);
+
+ /* Set the master fd. */
+ fid = (*env)->GetFieldID(env, cls, "master", "I");
+ if (fid == NULL) {
+ return NULL;
+ }
+ (*env)->SetIntField(env, jobj, fid, (jint)master);
+
+ /* Create a new String for the slave. */
+ jstr = (*env)->NewStringUTF(env, line);
+ }
+ return jstr;
+}
+
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_change_1window_1size
+ (JNIEnv *env, jobject jobj, jint fdm, jint width, jint height)
+{
+#ifdef TIOCGWINSZ
+ struct winsize win;
+
+ win.ws_col = width;
+ win.ws_row = height;
+ win.ws_xpixel = 0;
+ win.ws_ypixel = 0;
+
+ return ioctl(fdm, TIOCSWINSZ, &win);
+#else
+ return 0;
+#endif
+}
+
diff --git a/com.google.eclipse.elt.pty.linux/library/ptyio.c b/com.google.eclipse.elt.pty.linux/library/ptyio.c
new file mode 100644
index 0000000..a999787
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/ptyio.c
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2002 - 2005 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ *******************************************************************************/
+#include <jni.h>
+#include <stdio.h>
+#include <PTYInputStream.h>
+#include <PTYOutputStream.h>
+#include <unistd.h>
+
+/* Header for class _org_eclipse_cdt_utils_pty_PTYInputStream */
+/* Header for class _org_eclipse_cdt_utils_pty_PTYOutputStream */
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method: read0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYInputStream_read0(JNIEnv * env,
+ jobject jobj,
+ jint jfd,
+ jbyteArray buf,
+ jint buf_len)
+{
+ int fd;
+ int status;
+ jbyte *data;
+ int data_len;
+
+ data = (*env)->GetByteArrayElements(env, buf, 0);
+ data_len = buf_len;
+ fd = jfd;
+
+ status = read( fd, data, data_len );
+ (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+ if (status == 0) {
+ /* EOF. */
+ status = -1;
+ } else if (status == -1) {
+ /* Error, toss an exception */
+ /* Ignore the error for now, the debugger will attempt
+ * to close this multiple time. */
+#if 0
+ jclass exception = (*env)->FindClass(env, "java/io/IOException");
+ if (exception == NULL) {
+ /* Give up. */
+ return -1;
+ }
+ (*env)->ThrowNew(env, exception, "read error");
+#endif
+ }
+
+ return status;
+}
+
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYInputStream_close0(JNIEnv * env,
+ jobject jobj,
+ jint fd)
+{
+ return close(fd);
+}
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method: write0
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYOutputStream_write0(JNIEnv * env,
+ jobject jobj,
+ jint jfd,
+ jbyteArray buf,
+ jint buf_len)
+{
+ int status;
+ int fd;
+ jbyte *data;
+ int data_len;
+
+ data = (*env)->GetByteArrayElements(env, buf, 0);
+ data_len = buf_len;
+ fd = jfd;
+
+ status = write(fd, data, data_len);
+ (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+ return status;
+}
+
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYOutputStream_close0(JNIEnv * env,
+ jobject jobj,
+ jint fd)
+{
+ return close(fd);
+}
diff --git a/com.google.eclipse.elt.pty.linux/library/spawner.c b/com.google.eclipse.elt.pty.linux/library/spawner.c
new file mode 100644
index 0000000..67b12b0
--- /dev/null
+++ b/com.google.eclipse.elt.pty.linux/library/spawner.c
@@ -0,0 +1,302 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2010 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ * Wind River Systems, Inc.
+ * Mikhail Zabaluev (Nokia) - bug 82744
+ *******************************************************************************/
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <jni.h>
+
+#include "exec0.h"
+#include <Spawner.h>
+
+
+#define DEBUGIT 0
+
+
+/*
+ * Header for class org_eclipse_cdt_utils_spawner_Spawner
+ */
+
+
+#if DEBUGIT
+static void print_array(char **c_array)
+{
+ if (c_array) {
+ char **p = c_array;
+ for (; *p; p++) {
+ if (*p) {
+ fprintf(stderr, " %s", *p);
+ }
+ }
+ } else {
+ fprintf(stderr, "null");
+ }
+ fprintf(stderr, "\n");
+}
+#endif
+
+
+static char **alloc_c_array(JNIEnv * env, jobjectArray j_array)
+{
+ int i;
+ jint c_array_size = (*env)->GetArrayLength(env, j_array);
+ char **c_array = calloc(c_array_size + 1, sizeof(*c_array));
+
+ if (c_array == NULL)
+ return NULL;
+
+ for (i = 0; i < c_array_size; i++) {
+ jstring j_str =
+ (jstring) (*env)->GetObjectArrayElement(env, j_array, i);
+ const char *c_str = (*env)->GetStringUTFChars(env, j_str, NULL);
+ c_array[i] = (char *) strdup(c_str);
+ (*env)->ReleaseStringUTFChars(env, j_str, c_str);
+ (*env)->DeleteLocalRef(env, j_str);
+ }
+
+ return c_array;
+}
+
+
+static void free_c_array(char **c_array)
+{
+ if (c_array) {
+ char **p = c_array;
+ for (; *p; p++) {
+ if (*p) {
+ free(*p);
+ }
+ }
+ free(c_array);
+ }
+}
+
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: exec2
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[ILorg/eclipse/cdt/utils/pty/PTY;)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec2
+ (JNIEnv *env, jobject jobj, jobjectArray jcmd, jobjectArray jenv, jstring jdir, jintArray jchannels,
+ jstring jslaveName, jint masterFD, jboolean console)
+{
+ jint *channels = (*env)->GetIntArrayElements(env, jchannels, 0);
+ const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);
+ const char *pts_name = (*env)->GetStringUTFChars(env, jslaveName, NULL);
+ char **cmd = NULL;
+ char **envp = NULL;
+ int fd[3];
+ pid_t pid = -1;
+
+ if (channels == NULL)
+ goto bail_out;
+
+ cmd = alloc_c_array(env, jcmd);
+ if (cmd == NULL)
+ goto bail_out;
+
+ envp = alloc_c_array(env, jenv);
+ if (envp == NULL)
+ goto bail_out;
+
+#if DEBUGIT
+ fprintf(stderr, "command:");
+ print_array(cmd);
+ fprintf(stderr, "Envp:");
+ print_array(envp);
+ fprintf(stderr, "dirpath: %s\n", dirpath);
+ fprintf(stderr, "pts_name: %s\n", pts_name);
+#endif
+
+ pid = exec_pty(cmd[0], cmd, envp, dirpath, fd, pts_name, masterFD, console);
+ if (pid < 0)
+ goto bail_out;
+
+ channels[0] = fd[0];
+ channels[1] = fd[1];
+ channels[2] = fd[2];
+
+ bail_out:
+ (*env)->ReleaseIntArrayElements(env, jchannels, channels, 0);
+ (*env)->ReleaseStringUTFChars(env, jdir, dirpath);
+ (*env)->ReleaseStringUTFChars(env, jslaveName, pts_name);
+ if (cmd)
+ free_c_array(cmd);
+ if (envp)
+ free_c_array(envp);
+ return pid;
+}
+
+
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_Spawner_exec1(JNIEnv * env, jobject jobj,
+ jobjectArray jcmd,
+ jobjectArray jenv,
+ jstring jdir)
+{
+ const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);
+ char **cmd = NULL;
+ char **envp = NULL;
+ pid_t pid = -1;
+
+ cmd = alloc_c_array(env, jcmd);
+ if (cmd == NULL)
+ goto bail_out;
+
+ envp = alloc_c_array(env, jenv);
+ if (envp == NULL)
+ goto bail_out;
+
+#if DEBUGIT
+ fprintf(stderr, "command:");
+ print_array(cmd);
+ fprintf(stderr, "Envp:");
+ print_array(envp);
+ fprintf(stderr, "dirpath: %s\n", dirpath);
+#endif
+
+ pid = exec0(cmd[0], cmd, envp, dirpath, NULL);
+ if (pid < 0)
+ goto bail_out;
+
+ bail_out:
+ (*env)->ReleaseStringUTFChars(env, jdir, dirpath);
+ if (cmd)
+ free_c_array(cmd);
+ if (envp)
+ free_c_array(envp);
+ return pid;
+}
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: exec0
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_Spawner_exec0(JNIEnv * env, jobject jobj,
+ jobjectArray jcmd,
+ jobjectArray jenv,
+ jstring jdir,
+ jintArray jchannels)
+{
+ jint *channels = (*env)->GetIntArrayElements(env, jchannels, 0);
+ const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);
+ char **cmd = NULL;
+ char **envp = NULL;
+ int fd[3];
+ pid_t pid = -1;
+
+ if (channels == NULL)
+ goto bail_out;
+
+ cmd = alloc_c_array(env, jcmd);
+ if (cmd == NULL)
+ goto bail_out;
+
+ envp = alloc_c_array(env, jenv);
+ if (envp == NULL)
+ goto bail_out;
+
+#if DEBUGIT
+ fprintf(stderr, "command:");
+ print_array(cmd);
+ fprintf(stderr, "Envp:");
+ print_array(envp);
+ fprintf(stderr, "dirpath: %s\n", dirpath);
+#endif
+
+ pid = exec0(cmd[0], cmd, envp, dirpath, fd);
+ if (pid < 0)
+ goto bail_out;
+
+ channels[0] = fd[0];
+ channels[1] = fd[1];
+ channels[2] = fd[2];
+
+ bail_out:
+ (*env)->ReleaseIntArrayElements(env, jchannels, channels, 0);
+ (*env)->ReleaseStringUTFChars(env, jdir, dirpath);
+ if (cmd)
+ free_c_array(cmd);
+ if (envp)
+ free_c_array(envp);
+ return pid;
+}
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: raise
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_Spawner_raise(JNIEnv * env, jobject jobj,
+ jint pid, jint sig)
+{
+ int status = -1;
+
+ switch (sig) {
+ case 0: /* NOOP */
+ status = killpg(pid, 0);
+ if(status == -1) {
+ status = kill(pid, 0);
+ }
+ break;
+
+ case 2: /* INTERRUPT */
+ status = killpg(pid, SIGINT);
+ if(status == -1) {
+ status = kill(pid, SIGINT);
+ }
+ break;
+
+ case 9: /* KILL */
+ status = killpg(pid, SIGKILL);
+ if(status == -1) {
+ status = kill(pid, SIGKILL);
+ }
+ break;
+
+ case 15: /* TERM */
+ status = killpg(pid, SIGTERM);
+ if(status == -1) {
+ status = kill(pid, SIGTERM);
+ }
+ break;
+
+ default:
+ status = killpg(pid, sig); /* WHAT ?? */
+ if(status == -1) {
+ status = kill(pid, sig); /* WHAT ?? */
+ }
+ break;
+ }
+
+ return status;
+}
+
+
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: waitFor
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor(JNIEnv * env,
+ jobject jobj, jint pid)
+{
+ return wait0(pid);
+}
diff --git a/com.google.eclipse.elt.pty.macosx/.classpath b/com.google.eclipse.elt.pty.macosx/.classpath
new file mode 100644
index 0000000..7d9b38e
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/com.google.eclipse.elt.pty.macosx/.gitignore b/com.google.eclipse.elt.pty.macosx/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/com.google.eclipse.elt.pty.macosx/.project b/com.google.eclipse.elt.pty.macosx/.project
new file mode 100644
index 0000000..6ca68f6
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.google.eclipse.elt.macosx</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ </natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.pty.macosx/.settings/org.eclipse.jdt.core.prefs b/com.google.eclipse.elt.pty.macosx/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..c537b63
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/com.google.eclipse.elt.pty.macosx/META-INF/MANIFEST.MF b/com.google.eclipse.elt.pty.macosx/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..f30f562
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %fragmentName.macosx
+Bundle-SymbolicName: com.google.eclipse.elt.macosx;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-ClassPath: cdt_macosx.jar
+Bundle-Vendor: %providerName
+Fragment-Host: com.google.eclipse.elt.pty;bundle-version="1.1.0"
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
+Eclipse-PlatformFilter: (osgi.os=macosx)
diff --git a/com.google.eclipse.elt.pty.macosx/build.properties b/com.google.eclipse.elt.pty.macosx/build.properties
new file mode 100644
index 0000000..fc76b9b
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/build.properties
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2005, 2007 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+bin.includes = fragment.xml,\
+ about.html,\
+ .,\
+ os/,\
+ META-INF/
+src.includes = about.html,\
+ library/
+source.. = src/
diff --git a/com.google.eclipse.elt.pty.macosx/fragment.xml b/com.google.eclipse.elt.pty.macosx/fragment.xml
new file mode 100644
index 0000000..aedc72a
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/fragment.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<fragment>
+
+</fragment>
diff --git a/com.google.eclipse.elt.pty.macosx/library/Makefile b/com.google.eclipse.elt.pty.macosx/library/Makefile
new file mode 100644
index 0000000..26de453
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/Makefile
@@ -0,0 +1,189 @@
+#*******************************************************************************
+# Copyright (c) 2002, 2009 QNX Software Systems and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# QNX Software Systems - initial API and implementation
+# Alex Blewitt - MacOSX with a 64-bit vm
+#*******************************************************************************/
+
+# makefile for libspawner.so
+# See http://developer.apple.com/documentation/Java/Conceptual/Java141Development/Core_APIs/chapter_6_section_4.html
+
+JAVA_HOME = /Library/Java/Home
+ifeq ($(JAVA_HOME),)
+$(warning JAVA_HOME not set in environment)
+endif
+
+# Defaults which can be overridden.
+OS = macosx
+ARCH_PPC = ppc
+ARCH_X86 = x86
+ARCH_X86_64 = x86_64
+
+JDK_INCLUDES= $(JAVA_HOME)/include
+
+CC=gcc
+LD=libtool
+CPPFLAGS = -I. -I$(JDK_INCLUDES) #-I$(JDK_OS_INCLUDES)
+CFLAGS +=-fPIC -D_REENTRANT
+
+ARCH_FLAG_PPC = -arch ppc
+ARCH_FLAG_X86 = -arch i386
+ARCH_FLAG_X86_64 = -arch x86_64
+
+INSTALL_DIR_PPC = ../os/$(OS)/$(ARCH_PPC)
+INSTALL_DIR_X86 = ../os/$(OS)/$(ARCH_X86)
+INSTALL_DIR_X86_64 = ../os/$(OS)/$(ARCH_X86_64)
+
+LIB_NAME_SPAWNER = libspawner.dylib
+LIB_NAME_FULL_SPAWNER_PPC = $(INSTALL_DIR_PPC)/libspawner.jnilib
+LIB_NAME_FULL_SPAWNER_X86 = $(INSTALL_DIR_X86)/libspawner.jnilib
+LIB_NAME_FULL_SPAWNER_X86_64 = $(INSTALL_DIR_X86_64)/libspawner.jnilib
+OBJS_SPAWNER_PPC = spawner_$(ARCH_PPC).o \
+ io_$(ARCH_PPC).o \
+ exec_unix_$(ARCH_PPC).o \
+ exec_pty_$(ARCH_PPC).o \
+ openpty_$(ARCH_PPC).o \
+ pfind_$(ARCH_PPC).o
+OBJS_SPAWNER_X86 = spawner_$(ARCH_X86).o \
+ io_$(ARCH_X86).o \
+ exec_unix_$(ARCH_X86).o \
+ exec_pty_$(ARCH_X86).o \
+ openpty_$(ARCH_X86).o \
+ pfind_$(ARCH_X86).o
+OBJS_SPAWNER_X86_64 = spawner_$(ARCH_X86_64).o \
+ io_$(ARCH_X86_64).o \
+ exec_unix_$(ARCH_X86_64).o \
+ exec_pty_$(ARCH_X86_64).o \
+ openpty_$(ARCH_X86_64).o \
+ pfind_$(ARCH_X86_64).o
+
+LIB_NAME_PTY = libpty.so
+LIB_NAME_FULL_PTY_PPC = $(INSTALL_DIR_PPC)/libpty.jnilib
+LIB_NAME_FULL_PTY_X86 = $(INSTALL_DIR_X86)/libpty.jnilib
+LIB_NAME_FULL_PTY_X86_64 = $(INSTALL_DIR_X86_64)/libpty.jnilib
+OBJS_PTY_PPC = openpty_$(ARCH_PPC).o pty_$(ARCH_PPC).o ptyio_$(ARCH_PPC).o
+OBJS_PTY_X86 = openpty_$(ARCH_X86).o pty_$(ARCH_X86).o ptyio_$(ARCH_X86).o
+OBJS_PTY_X86_64 = openpty_$(ARCH_X86_64).o pty_$(ARCH_X86_64).o ptyio_$(ARCH_X86_64).o
+
+OBJS_PPC = $(OBJS_SPAWNER_PPC) $(OBJS_PTY_PPC)
+OBJS_X86 = $(OBJS_SPAWNER_X86) $(OBJS_PTY_X86)
+OBJS_X86_64 = $(OBJS_SPAWNER_X86_64) $(OBJS_PTY_X86_64)
+
+all: ppc x86 x86_64
+
+ppc: $(LIB_NAME_FULL_SPAWNER_PPC) $(LIB_NAME_FULL_PTY_PPC)
+
+x86: $(LIB_NAME_FULL_SPAWNER_X86) $(LIB_NAME_FULL_PTY_X86)
+
+x86_64: $(LIB_NAME_FULL_SPAWNER_X86_64) $(LIB_NAME_FULL_PTY_X86_64)
+
+rebuild: clean all
+
+$(LIB_NAME_FULL_SPAWNER_PPC) : $(OBJS_SPAWNER_PPC)
+ mkdir -p $(INSTALL_DIR_PPC)
+ $(CC) -dynamiclib $(ARCH_FLAG_PPC) -o $(LIB_NAME_FULL_SPAWNER_PPC) $(OBJS_SPAWNER_PPC) -lc -framework JavaVM
+
+$(LIB_NAME_FULL_SPAWNER_X86) : $(OBJS_SPAWNER_X86)
+ mkdir -p $(INSTALL_DIR_X86)
+ $(CC) -dynamiclib $(ARCH_FLAG_X86) -o $(LIB_NAME_FULL_SPAWNER_X86) $(OBJS_SPAWNER_X86) -lc -framework JavaVM
+
+$(LIB_NAME_FULL_SPAWNER_X86_64) : $(OBJS_SPAWNER_X86_64)
+ mkdir -p $(INSTALL_DIR_X86_64)
+ $(CC) -dynamiclib $(ARCH_FLAG_X86_64) -o $(LIB_NAME_FULL_SPAWNER_X86_64) $(OBJS_SPAWNER_X86_64) -lc -framework JavaVM
+
+$(LIB_NAME_FULL_PTY_PPC): $(OBJS_PTY_PPC)
+ mkdir -p $(INSTALL_DIR_PPC)
+ $(CC) -dynamiclib $(ARCH_FLAG_PPC) -o $(LIB_NAME_FULL_PTY_PPC) $(OBJS_PTY_PPC) -lc -framework JavaVM
+
+$(LIB_NAME_FULL_PTY_X86): $(OBJS_PTY_X86)
+ mkdir -p $(INSTALL_DIR_X86)
+ $(CC) -dynamiclib $(ARCH_FLAG_X86) -o $(LIB_NAME_FULL_PTY_X86) $(OBJS_PTY_X86) -lc -framework JavaVM
+
+$(LIB_NAME_FULL_PTY_X86_64): $(OBJS_PTY_X86_64)
+ mkdir -p $(INSTALL_DIR_X86_64)
+ $(CC) -dynamiclib $(ARCH_FLAG_X86_64) -o $(LIB_NAME_FULL_PTY_X86_64) $(OBJS_PTY_X86_64) -lc -framework JavaVM
+
+spawner_$(ARCH_PPC).o: spawner.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ spawner.c
+
+io_$(ARCH_PPC).o: io.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ io.c
+
+exec_unix_$(ARCH_PPC).o: exec_unix.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ exec_unix.c
+
+exec_pty_$(ARCH_PPC).o: exec_pty.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ exec_pty.c
+
+openpty_$(ARCH_PPC).o: openpty.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ openpty.c
+
+pfind_$(ARCH_PPC).o: pfind.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ pfind.c
+
+openpty_$(ARCH_PPC).o: openpty.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ openpty.c
+
+pty_$(ARCH_PPC).o: pty.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ pty.c
+
+ptyio_$(ARCH_PPC).o: ptyio.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_PPC) $(CPPFLAGS) -c -o $@ ptyio.c
+
+spawner_$(ARCH_X86).o: spawner.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ spawner.c
+
+io_$(ARCH_X86).o: io.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ io.c
+
+exec_unix_$(ARCH_X86).o: exec_unix.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ exec_unix.c
+
+exec_pty_$(ARCH_X86).o: exec_pty.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ exec_pty.c
+
+openpty_$(ARCH_X86).o: openpty.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ openpty.c
+
+pfind_$(ARCH_X86).o: pfind.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ pfind.c
+
+pty_$(ARCH_X86).o: pty.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ pty.c
+
+ptyio_$(ARCH_X86).o: ptyio.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ ptyio.c
+
+spawner_$(ARCH_X86_64).o: spawner.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ spawner.c
+
+io_$(ARCH_X86_64).o: io.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ io.c
+
+exec_unix_$(ARCH_X86_64).o: exec_unix.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ exec_unix.c
+
+exec_pty_$(ARCH_X86_64).o: exec_pty.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ exec_pty.c
+
+openpty_$(ARCH_X86_64).o: openpty.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ openpty.c
+
+pfind_$(ARCH_X86_64).o: pfind.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ pfind.c
+
+pty_$(ARCH_X86_64).o: pty.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ pty.c
+
+ptyio_$(ARCH_X86_64).o: ptyio.c
+ $(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ ptyio.c
+
+clean :
+ $(RM) $(OBJS_PPC) $(LIB_NAME_FULL_SPAWNER_PPC) $(LIB_NAME_FULL_PTY_PPC)
+ $(RM) $(OBJS_X86) $(LIB_NAME_FULL_SPAWNER_X86) $(LIB_NAME_FULL_PTY_X86)
+ $(RM) $(OBJS_X86_64) $(LIB_NAME_FULL_SPAWNER_X86_64) $(LIB_NAME_FULL_PTY_X86_64)
diff --git a/com.google.eclipse.elt.pty.macosx/library/PTY.h b/com.google.eclipse.elt.pty.macosx/library/PTY.h
new file mode 100644
index 0000000..f717942
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/PTY.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_pty_PTY */
+
+#ifndef _Included_org_eclipse_cdt_utils_pty_PTY
+#define _Included_org_eclipse_cdt_utils_pty_PTY
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTY
+ * Method: openMaster
+ * Signature: (Z)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_eclipse_cdt_utils_pty_PTY_openMaster
+ (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTY
+ * Method: change_window_size
+ * Signature: (III)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_change_1window_1size
+ (JNIEnv *, jobject, jint, jint, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/PTYInputStream.h b/com.google.eclipse.elt.pty.macosx/library/PTYInputStream.h
new file mode 100644
index 0000000..e734927
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/PTYInputStream.h
@@ -0,0 +1,32 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_pty_PTYInputStream */
+
+#ifndef _Included_org_eclipse_cdt_utils_pty_PTYInputStream
+#define _Included_org_eclipse_cdt_utils_pty_PTYInputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef org_eclipse_cdt_utils_pty_PTYInputStream_SKIP_BUFFER_SIZE
+#define org_eclipse_cdt_utils_pty_PTYInputStream_SKIP_BUFFER_SIZE 2048L
+/* Inaccessible static: skipBuffer */
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method: read0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYInputStream_read0
+ (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYInputStream_close0
+ (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/PTYOutputStream.h b/com.google.eclipse.elt.pty.macosx/library/PTYOutputStream.h
new file mode 100644
index 0000000..fb28491
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/PTYOutputStream.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_pty_PTYOutputStream */
+
+#ifndef _Included_org_eclipse_cdt_utils_pty_PTYOutputStream
+#define _Included_org_eclipse_cdt_utils_pty_PTYOutputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method: write0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYOutputStream_write0
+ (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYOutputStream_close0
+ (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/Spawner.h b/com.google.eclipse.elt.pty.macosx/library/Spawner.h
new file mode 100644
index 0000000..02727f9
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/Spawner.h
@@ -0,0 +1,53 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_eclipse_cdt_utils_spawner_Spawner */
+
+#ifndef _Included_org_eclipse_cdt_utils_spawner_Spawner
+#define _Included_org_eclipse_cdt_utils_spawner_Spawner
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: exec0
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec0
+ (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring, jintArray);
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: exec1
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec1
+ (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring);
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: exec2
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[ILjava/lang/String;IZ)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec2
+ (JNIEnv *, jobject, jobjectArray, jobjectArray, jstring, jintArray, jstring, jint, jboolean);
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: raise
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_raise
+ (JNIEnv *, jobject, jint, jint);
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: waitFor
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor
+ (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/SpawnerInputStream.h b/com.google.eclipse.elt.pty.macosx/library/SpawnerInputStream.h
new file mode 100644
index 0000000..ecf8f8c
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/SpawnerInputStream.h
@@ -0,0 +1,32 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_qnx_tools_utils_spawner_SpawnerInputStream */
+
+#ifndef _Included_com_qnx_tools_utils_spawner_SpawnerInputStream
+#define _Included_com_qnx_tools_utils_spawner_SpawnerInputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef com_qnx_tools_utils_spawner_SpawnerInputStream_SKIP_BUFFER_SIZE
+#define com_qnx_tools_utils_spawner_SpawnerInputStream_SKIP_BUFFER_SIZE 2048L
+/* Inaccessible static: skipBuffer */
+/*
+ * Class: org_elipse_cdt_utils_spawner_SpawnerInputStream
+ * Method: read0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_read0
+ (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerInputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_close0
+ (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/SpawnerOutputStream.h b/com.google.eclipse.elt.pty.macosx/library/SpawnerOutputStream.h
new file mode 100644
index 0000000..444d71c
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/SpawnerOutputStream.h
@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_qnx_tools_utils_spawner_SpawnerOutputStream */
+
+#ifndef _Included_com_qnx_tools_utils_spawner_SpawnerOutputStream
+#define _Included_com_qnx_tools_utils_spawner_SpawnerOutputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerOutputStream
+ * Method: write0
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_write0
+ (JNIEnv *, jobject, jint, jbyteArray, jint);
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerOutputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_close0
+ (JNIEnv *, jobject, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/exec0.h b/com.google.eclipse.elt.pty.macosx/library/exec0.h
new file mode 100644
index 0000000..fa23bd7
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/exec0.h
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2009 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ *******************************************************************************/
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+
+extern pid_t exec0(const char *path, char *const argv[],
+ char *const envp[], const char *dirpath,
+ int channels[3] );
+
+extern pid_t exec_pty(const char *path, char *const argv[],
+ char *const envp[], const char *dirpath,
+ int channels[3], const char *pts_name, int fdm,
+ int console);
+
+extern int wait0(pid_t pid);
diff --git a/com.google.eclipse.elt.pty.macosx/library/exec_pty.c b/com.google.eclipse.elt.pty.macosx/library/exec_pty.c
new file mode 100644
index 0000000..c326805
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/exec_pty.c
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2009 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ *******************************************************************************/
+#include "exec0.h"
+#include "openpty.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <termios.h>
+
+/* from pfind.c */
+extern char *pfind(const char *name, char *const envp[]);
+
+pid_t
+exec_pty(const char *path, char *const argv[], char *const envp[],
+ const char *dirpath, int channels[3], const char *pts_name, int fdm, int console)
+{
+ int pipe2[2];
+ pid_t childpid;
+ char *full_path;
+
+ /*
+ * We use pfind() to check that the program exists and is an executable.
+ * If not pass the error up. Also execve() wants a full path.
+ */
+ full_path = pfind(path, envp);
+ if (full_path == NULL) {
+ fprintf(stderr, "Unable to find full path for \"%s\"\n", (path) ? path : "");
+ return -1;
+ }
+
+ /*
+ * Make sure we can create our pipes before forking.
+ */
+ if (console && channels != NULL) {
+ if (pipe(pipe2) < 0) {
+ fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ free(full_path);
+ return -1;
+ }
+ }
+
+ childpid = fork();
+
+ if (childpid < 0) {
+ fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ free(full_path);
+ return -1;
+ } else if (childpid == 0) { /* child */
+
+ chdir(dirpath);
+
+ if (channels != NULL) {
+ int fds;
+
+ if (!console && setsid() < 0) {
+ perror("setsid()");
+ return -1;
+ }
+
+ fds = ptys_open(fdm, pts_name);
+ if (fds < 0) {
+ fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ return -1;
+ }
+
+ /* Close the read end of pipe2 */
+ if (console && close(pipe2[0]) == -1) {
+ perror("close(pipe2[0]))");
+ }
+
+ /* close the master, no need in the child */
+ close(fdm);
+
+ if (console) {
+ set_noecho(fds);
+ if (setpgid(getpid(), getpid()) < 0) {
+ perror("setpgid()");
+ return -1;
+ }
+ }
+ /* redirections */
+ dup2(fds, STDIN_FILENO); /* dup stdin */
+ dup2(fds, STDOUT_FILENO); /* dup stdout */
+ if (console) {
+ dup2(pipe2[1], STDERR_FILENO); /* dup stderr */
+ } else {
+ dup2(fds, STDERR_FILENO); /* dup stderr */
+ }
+ close(fds); /* done with fds. */
+ }
+
+ /* Close all the fd's in the child */
+ {
+ int fdlimit = sysconf(_SC_OPEN_MAX);
+ int fd = 3;
+
+ while (fd < fdlimit)
+ close(fd++);
+ }
+
+ if (envp[0] == NULL) {
+ execv(full_path, argv);
+ } else {
+ execve(full_path, argv, envp);
+ }
+
+ _exit(127);
+
+ } else if (childpid != 0) { /* parent */
+
+ if (console) {
+ set_noecho(fdm);
+ }
+ if (channels != NULL) {
+ /* close the write end of pipe1 */
+ if (console && close(pipe2[1]) == -1)
+ perror("close(pipe2[1])");
+
+ channels[0] = fdm; /* Input Stream. */
+ channels[1] = fdm; /* Output Stream. */
+ if (console) { /* stderr Stream. */
+ channels[2] = pipe2[0];
+ } else {
+ channels[2] = fdm;
+ }
+ }
+
+ free(full_path);
+ return childpid;
+ }
+
+ free(full_path);
+ return -1; /*NOT REACHED */
+}
+#ifdef __STAND_ALONE__
+int main(int argc, char **argv, char **envp) {
+ const char *path = "./bufferring_test";
+ int channels[3] = { -1, -1, -1};
+ int status;
+ FILE *app_stdin;
+ FILE *app_stdout;
+ FILE *app_stderr;
+ char pts_name[32];
+ int fdm;
+ char buffer[32];
+
+ fdm = ptym_open(pts_name);
+ status = exec_pty(path, argv, envp, ".", channels, pts_name, fdm);
+ if (status >= 0) {
+ app_stdin = fdopen(channels[0], "w");
+ app_stdout = fdopen(channels[1], "r");
+ app_stderr = fdopen(channels[2], "r");
+ if (app_stdout == NULL || app_stderr == NULL || app_stdin == NULL) {
+ fprintf(stderr, "PROBLEMS\n");
+ } else {
+ fputs("foo\n", app_stdin);
+ fputs("bar\n", app_stdin);
+ while(fgets(buffer, sizeof buffer, app_stdout) != NULL) {
+ fprintf(stdout, "STDOUT: %s\n", buffer);
+ }
+ while(fgets(buffer, sizeof buffer, app_stderr) != NULL) {
+ fprintf(stdout, "STDERR: %s\n", buffer);
+ }
+ }
+ }
+ fputs("bye\n", stdout);
+ close(channels[0]);
+ close(channels[1]);
+ close(channels[2]);
+ return 0;
+}
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/exec_unix.c b/com.google.eclipse.elt.pty.macosx/library/exec_unix.c
new file mode 100644
index 0000000..ee70513
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/exec_unix.c
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2002 - 2005 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ *******************************************************************************/
+#include "exec0.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <libgen.h>
+#include <stdlib.h>
+
+/* from pfind.c */
+extern char *pfind(const char *name, char *const envp[]);
+
+pid_t
+exec0(const char *path, char *const argv[], char *const envp[],
+ const char *dirpath, int channels[3])
+{
+ int pipe0[2], pipe1[2], pipe2[2];
+ pid_t childpid;
+ char *full_path;
+
+ /*
+ * We use pfind() to check that the program exists and is an executable.
+ * If not pass the error up. Also execve() wants a full path.
+ */
+ full_path = pfind(path, envp);
+ if (full_path == NULL) {
+ fprintf(stderr, "Unable to find full path for \"%s\"\n", (path) ? path : "");
+ return -1;
+ }
+
+ /*
+ * Make sure we can create our pipes before forking.
+ */
+ if (channels != NULL) {
+ if (pipe(pipe0) < 0 || pipe(pipe1) < 0 || pipe(pipe2) < 0) {
+ fprintf(stderr, "%s(%d): returning due to error.\n",
+ __FUNCTION__, __LINE__);
+ free(full_path);
+ return -1;
+ }
+ }
+
+ childpid = fork();
+
+ if (childpid < 0) {
+ fprintf(stderr, "%s(%d): returning due to error: %s\n",
+ __FUNCTION__, __LINE__, strerror(errno));
+ free(full_path);
+ return -1;
+ } else if (childpid == 0) { /* child */
+ char *ptr;
+
+ chdir(dirpath);
+
+ if (channels != NULL) {
+ /* Close the write end of pipe0 */
+ if (close(pipe0[1]) == -1)
+ perror("close(pipe0[1])");
+
+ /* Close the read end of pipe1 */
+ if (close(pipe1[0]) == -1)
+ perror("close(pipe1[0])");
+
+ /* Close the read end of pipe2 */
+ if (close(pipe2[0]) == -1)
+ perror("close(pipe2[0]))");
+
+ /* redirections */
+ dup2(pipe0[0], STDIN_FILENO); /* dup stdin */
+ dup2(pipe1[1], STDOUT_FILENO); /* dup stdout */
+ dup2(pipe2[1], STDERR_FILENO); /* dup stderr */
+ }
+
+ /* Close all the fd's in the child */
+ {
+ int fdlimit = sysconf(_SC_OPEN_MAX);
+ int fd = 3;
+
+ while (fd < fdlimit)
+ close(fd++);
+ }
+
+ if (envp[0] == NULL) {
+ execv(full_path, argv);
+ } else {
+ execve(full_path, argv, envp);
+ }
+
+ _exit(127);
+
+ } else if (childpid != 0) { /* parent */
+
+ char b;
+
+ if (channels != NULL) {
+ /* close the read end of pipe1 */
+ if (close(pipe0[0]) == -1)
+ perror("close(pipe0[0])");
+
+ /* close the write end of pipe2 */
+ if (close(pipe1[1]) == -1)
+ perror("close(pipe1[1])");
+
+ /* close the write end of pipe2 */
+ if (close(pipe2[1]) == -1)
+ perror("close(pipe2[1])");
+
+ channels[0] = pipe0[1]; /* Output Stream. */
+ channels[1] = pipe1[0]; /* Input Stream. */
+ channels[2] = pipe2[0]; /* Input Stream. */
+ }
+
+ free(full_path);
+ return childpid;
+ }
+
+ free(full_path);
+ return -1; /*NOT REACHED */
+}
+
+
+int wait0(pid_t pid)
+{
+ int status;
+ int val = -1;
+
+ if (pid < 0 || waitpid(pid, &status, 0) < 0)
+ return -1;
+
+ if (WIFEXITED(status)) {
+ val = WEXITSTATUS(status);
+ }
+
+ return val;
+}
diff --git a/com.google.eclipse.elt.pty.macosx/library/io.c b/com.google.eclipse.elt.pty.macosx/library/io.c
new file mode 100644
index 0000000..8c45e5d
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/io.c
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2002 - 2005 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ *******************************************************************************/
+#include <jni.h>
+#include <stdio.h>
+#include <SpawnerInputStream.h>
+#include <SpawnerOutputStream.h>
+#include <unistd.h>
+
+/* Header for class _org_eclipse_cdt_utils_spawner_SpawnerInputStream */
+/* Header for class _org_eclipse_cdt_utils_spawner_SpawnerOutputStream */
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerInputStream
+ * Method: read0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_read0(JNIEnv * env,
+ jobject jobj,
+ jint jfd,
+ jbyteArray buf,
+ jint buf_len)
+{
+ int fd;
+ int status;
+ jbyte *data;
+ int data_len;
+
+ data = (*env)->GetByteArrayElements(env, buf, 0);
+ data_len = buf_len;
+ fd = jfd;
+
+ status = read( fd, data, data_len );
+ (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+ if (status == 0) {
+ /* EOF. */
+ status = -1;
+ } else if (status == -1) {
+ /* Error, toss an exception */
+ jclass exception = (*env)->FindClass(env, "java/io/IOException");
+ if (exception == NULL) {
+ /* Give up. */
+ return -1;
+ }
+ (*env)->ThrowNew(env, exception, "read error");
+ }
+
+ return status;
+}
+
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerInputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_SpawnerInputStream_close0(JNIEnv * env,
+ jobject jobj,
+ jint fd)
+{
+ return close(fd);
+}
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerOutputStream
+ * Method: write0
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_write0(JNIEnv * env,
+ jobject jobj,
+ jint jfd,
+ jbyteArray buf,
+ jint buf_len)
+{
+ int status;
+ int fd;
+ jbyte *data;
+ int data_len;
+
+ data = (*env)->GetByteArrayElements(env, buf, 0);
+ data_len = buf_len;
+ fd = jfd;
+
+ status = write(fd, data, data_len);
+ (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+ return status;
+}
+
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_SpawnerOutputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_SpawnerOutputStream_close0(JNIEnv * env,
+ jobject jobj,
+ jint fd)
+{
+ return close(fd);
+}
diff --git a/com.google.eclipse.elt.pty.macosx/library/openpty.c b/com.google.eclipse.elt.pty.macosx/library/openpty.c
new file mode 100644
index 0000000..0d720f5
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/openpty.c
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2009 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ *******************************************************************************/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <grp.h>
+#include <termios.h>
+
+#include <stdlib.h>
+
+/**
+ * This is taken from R. W. Stevens book.
+ * Alain Magloire.
+ */
+
+int ptym_open (char *pts_name);
+int ptys_open (int fdm, const char * pts_name);
+
+int
+openpty(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp)
+{
+ char line[20];
+ line[0]=0;
+ *amaster = ptym_open(line);
+ if (*amaster < 0)
+ return -1;
+ *aslave = ptys_open(*amaster, line);
+ if (*aslave < 0) {
+ close(*amaster);
+ return -1;
+ }
+ if (name)
+ strcpy(name, line);
+#ifndef TCSAFLUSH
+#define TCSAFLUSH TCSETAF
+#endif
+ if (termp)
+ (void) tcsetattr(*aslave, TCSAFLUSH, termp);
+#ifdef TIOCSWINSZ
+ if (winp)
+ (void) ioctl(*aslave, TIOCSWINSZ, (char *)winp);
+#endif
+ return 0;
+}
+
+int
+ptym_open(char * pts_name)
+{
+ char *ptr1, *ptr2;
+ int fdm;
+
+ strcpy(pts_name, "/dev/ptyXY");
+ /* array index: 012345689 (for references in following code) */
+ for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1 != 0; ptr1++) {
+ pts_name[8] = *ptr1;
+ for (ptr2 = "0123456789abcdef"; *ptr2 != 0; ptr2++) {
+ pts_name[9] = *ptr2;
+ /* try to open master */
+ fdm = open(pts_name, O_RDWR);
+ if (fdm < 0) {
+ if (errno == ENOENT) {/* different from EIO */
+ return -1; /* out of pty devices */
+ } else {
+ continue; /* try next pty device */
+ }
+ }
+ pts_name[5] = 't'; /* chage "pty" to "tty" */
+ return fdm; /* got it, return fd of master */
+ }
+ }
+ return -1; /* out of pty devices */
+}
+
+int
+ptys_open(int fdm, const char * pts_name)
+{
+ int gid, fds;
+ struct group *grptr;
+
+ grptr = getgrnam("tty");
+ if (grptr != NULL) {
+ gid = grptr->gr_gid;
+ } else {
+ gid = -1; /* group tty is not in the group file */
+ }
+
+ /* following two functions don't work unless we're root */
+ chown(pts_name, getuid(), gid);
+ chmod(pts_name, S_IRUSR | S_IWUSR | S_IWGRP);
+ fds = open(pts_name, O_RDWR);
+ if (fds < 0) {
+ close(fdm);
+ return -1;
+ }
+
+#if defined(TIOCSCTTY)
+ /* TIOCSCTTY is the BSD way to acquire a controlling terminal. */
+ if (ioctl(fds, TIOCSCTTY, (char *)0) < 0) {
+ // ignore error: this is expected in console-mode
+ }
+#endif
+
+ return fds;
+}
+
+void
+set_noecho(int fd)
+{
+ struct termios stermios;
+ if (tcgetattr(fd, &stermios) < 0) {
+ return ;
+ }
+
+ /* turn off echo */
+ stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+ /* Turn off the NL to CR/NL mapping ou output. */
+ /*stermios.c_oflag &= ~(ONLCR);*/
+
+ stermios.c_iflag |= (IGNCR);
+
+ tcsetattr(fd, TCSANOW, &stermios);
+}
diff --git a/com.google.eclipse.elt.pty.macosx/library/openpty.h b/com.google.eclipse.elt.pty.macosx/library/openpty.h
new file mode 100644
index 0000000..fd974da
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/openpty.h
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2009 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ *******************************************************************************/
+#ifndef _OPENPTY_H
+#define _OPENPTY_H
+int ptym_open (char *pts_name);
+int ptys_open (int fdm, const char * pts_name);
+void set_noecho(int fd);
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/pfind.c b/com.google.eclipse.elt.pty.macosx/library/pfind.c
new file mode 100644
index 0000000..83abf24
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/pfind.c
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2010 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ * Wind River Systems, Inc.
+ * Mikhail Sennikovsky - bug 145737
+ * Everton Rufino Constantino (IBM) - bug 237611
+ *******************************************************************************/
+/*
+ * pfind.c - Search for a binary in $PATH.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define PATH_DEF "PATH="
+const int path_def_len = 5; /* strlen(PATH_DEF); */
+
+char * path_val(char * const envp[])
+{
+ int i;
+ if (envp == NULL || envp[0] == NULL)
+ return getenv("PATH" );
+
+ for(i = 0; envp[i] != NULL; i++){
+ char* p = envp[i];
+ if(!strncmp(PATH_DEF, p, path_def_len)){
+ return p + path_def_len;
+ }
+ }
+
+ return NULL;
+}
+
+char * pfind(const char *name, char * const envp[])
+{
+ char *tok;
+ char *sp;
+ char *path;
+ char fullpath[PATH_MAX+1];
+
+ /* Sanity check. */
+ if (name == NULL) {
+ fprintf(stderr, "pfind(): Null argument.\n");
+ return NULL;
+ }
+
+ /* For absolute name or name with a path, check if it is an executable. */
+ if (name[0] == '/' || name[0] == '.') {
+ if (access(name, X_OK) == 0) {
+ return strdup(name);
+ }
+ return NULL;
+ }
+
+ /* Search in the PATH environment. */
+ path = path_val( envp );
+
+ if (path == NULL || strlen(path) <= 0) {
+ fprintf(stderr, "Unable to get $PATH.\n");
+ return NULL;
+ }
+
+ /* The value return by getenv() is readonly */
+ path = strdup(path);
+
+ tok = strtok_r(path, ":", &sp);
+ while (tok != NULL) {
+ snprintf(fullpath, sizeof(fullpath) - 1, "%s/%s", tok, name);
+
+ if (access(fullpath, X_OK) == 0) {
+ free(path);
+ return strdup(fullpath);
+ }
+
+ tok = strtok_r( NULL, ":", &sp );
+ }
+
+ free(path);
+ return NULL;
+}
+
+#ifdef BUILD_WITH_MAIN
+int main(int argc, char **argv)
+{
+ int i;
+ char *fullpath;
+
+ for (i=1; i<argc; i++) {
+ fullpath = pfind(argv[i], NULL);
+ if (fullpath == NULL)
+ printf("Unable to find %s in $PATH.\n", argv[i]);
+ else
+ printf("Found %s @ %s.\n", argv[i], fullpath);
+ }
+}
+#endif
diff --git a/com.google.eclipse.elt.pty.macosx/library/pty.c b/com.google.eclipse.elt.pty.macosx/library/pty.c
new file mode 100644
index 0000000..e7c23e8
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/pty.c
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2009 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ *******************************************************************************/
+#include "PTY.h"
+#include "openpty.h"
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTY
+ * Method: forkpty
+ * Signature: ()I
+ */
+JNIEXPORT jstring JNICALL
+Java_org_eclipse_cdt_utils_pty_PTY_openMaster (JNIEnv *env, jobject jobj, jboolean console) {
+ jfieldID fid; /* Store the field ID */
+ jstring jstr = NULL;
+ int master = -1;
+ char line[1024]; /* FIXME: Should be enough */
+ jclass cls;
+
+ line[0] = '\0';
+
+ master = ptym_open(line);
+ if (master >= 0) {
+ if (console) {
+ // turn off echo
+ set_noecho(master);
+ }
+
+ /* Get a reference to the obj's class */
+ cls = (*env)->GetObjectClass(env, jobj);
+
+ /* Set the master fd. */
+ fid = (*env)->GetFieldID(env, cls, "master", "I");
+ if (fid == NULL) {
+ return NULL;
+ }
+ (*env)->SetIntField(env, jobj, fid, (jint)master);
+
+ /* Create a new String for the slave. */
+ jstr = (*env)->NewStringUTF(env, line);
+ }
+ return jstr;
+}
+
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_change_1window_1size
+ (JNIEnv *env, jobject jobj, jint fdm, jint width, jint height)
+{
+#ifdef TIOCGWINSZ
+ struct winsize win;
+
+ win.ws_col = width;
+ win.ws_row = height;
+ win.ws_xpixel = 0;
+ win.ws_ypixel = 0;
+
+ return ioctl(fdm, TIOCSWINSZ, &win);
+#else
+ return 0;
+#endif
+}
diff --git a/com.google.eclipse.elt.pty.macosx/library/ptyio.c b/com.google.eclipse.elt.pty.macosx/library/ptyio.c
new file mode 100644
index 0000000..a999787
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/ptyio.c
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2002 - 2005 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ *******************************************************************************/
+#include <jni.h>
+#include <stdio.h>
+#include <PTYInputStream.h>
+#include <PTYOutputStream.h>
+#include <unistd.h>
+
+/* Header for class _org_eclipse_cdt_utils_pty_PTYInputStream */
+/* Header for class _org_eclipse_cdt_utils_pty_PTYOutputStream */
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method: read0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYInputStream_read0(JNIEnv * env,
+ jobject jobj,
+ jint jfd,
+ jbyteArray buf,
+ jint buf_len)
+{
+ int fd;
+ int status;
+ jbyte *data;
+ int data_len;
+
+ data = (*env)->GetByteArrayElements(env, buf, 0);
+ data_len = buf_len;
+ fd = jfd;
+
+ status = read( fd, data, data_len );
+ (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+ if (status == 0) {
+ /* EOF. */
+ status = -1;
+ } else if (status == -1) {
+ /* Error, toss an exception */
+ /* Ignore the error for now, the debugger will attempt
+ * to close this multiple time. */
+#if 0
+ jclass exception = (*env)->FindClass(env, "java/io/IOException");
+ if (exception == NULL) {
+ /* Give up. */
+ return -1;
+ }
+ (*env)->ThrowNew(env, exception, "read error");
+#endif
+ }
+
+ return status;
+}
+
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYInputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYInputStream_close0(JNIEnv * env,
+ jobject jobj,
+ jint fd)
+{
+ return close(fd);
+}
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method: write0
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYOutputStream_write0(JNIEnv * env,
+ jobject jobj,
+ jint jfd,
+ jbyteArray buf,
+ jint buf_len)
+{
+ int status;
+ int fd;
+ jbyte *data;
+ int data_len;
+
+ data = (*env)->GetByteArrayElements(env, buf, 0);
+ data_len = buf_len;
+ fd = jfd;
+
+ status = write(fd, data, data_len);
+ (*env)->ReleaseByteArrayElements(env, buf, data, 0);
+
+ return status;
+}
+
+
+/*
+ * Class: org_eclipse_cdt_utils_pty_PTYOutputStream
+ * Method: close0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_pty_PTYOutputStream_close0(JNIEnv * env,
+ jobject jobj,
+ jint fd)
+{
+ return close(fd);
+}
diff --git a/com.google.eclipse.elt.pty.macosx/library/spawner.c b/com.google.eclipse.elt.pty.macosx/library/spawner.c
new file mode 100644
index 0000000..99f7fde
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/library/spawner.c
@@ -0,0 +1,284 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2009 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * QNX Software Systems - initial API and implementation
+ *******************************************************************************/
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <jni.h>
+
+#include "exec0.h"
+#include <Spawner.h>
+
+
+#define DEBUGIT 0
+
+
+/*
+ * Header for class org_eclipse_cdt_utils_spawner_Spawner
+ */
+
+
+#if DEBUGIT
+static void print_array(char **c_array)
+{
+ if (c_array) {
+ char **p = c_array;
+ for (; *p; p++) {
+ if (*p) {
+ fprintf(stderr, " %s", *p);
+ }
+ }
+ } else {
+ fprintf(stderr, "null");
+ }
+ fprintf(stderr, "\n");
+}
+#endif
+
+
+static char **alloc_c_array(JNIEnv * env, jobjectArray j_array)
+{
+ int i;
+ jint c_array_size = (*env)->GetArrayLength(env, j_array);
+ char **c_array = calloc(c_array_size + 1, sizeof(*c_array));
+
+ if (c_array == NULL)
+ return NULL;
+
+ for (i = 0; i < c_array_size; i++) {
+ jstring j_str =
+ (jstring) (*env)->GetObjectArrayElement(env, j_array, i);
+ const char *c_str = (*env)->GetStringUTFChars(env, j_str, NULL);
+ c_array[i] = (char *) strdup(c_str);
+ (*env)->ReleaseStringUTFChars(env, j_str, c_str);
+ (*env)->DeleteLocalRef(env, j_str);
+ }
+
+ return c_array;
+}
+
+
+static void free_c_array(char **c_array)
+{
+ if (c_array) {
+ char **p = c_array;
+ for (; *p; p++) {
+ if (*p) {
+ free(*p);
+ }
+ }
+ free(c_array);
+ }
+}
+
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: exec2
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[ILorg/eclipse/cdt/utils/pty/PTY;)I
+ */
+JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_spawner_Spawner_exec2
+ (JNIEnv *env, jobject jobj, jobjectArray jcmd, jobjectArray jenv, jstring jdir, jintArray jchannels,
+ jstring jslaveName, jint masterFD, jboolean console)
+{
+ jint *channels = (*env)->GetIntArrayElements(env, jchannels, 0);
+ const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);
+ const char *pts_name = (*env)->GetStringUTFChars(env, jslaveName, NULL);
+ char **cmd = NULL;
+ char **envp = NULL;
+ int fd[3];
+ pid_t pid = -1;
+
+ if (channels == NULL)
+ goto bail_out;
+
+ cmd = alloc_c_array(env, jcmd);
+ if (cmd == NULL)
+ goto bail_out;
+
+ envp = alloc_c_array(env, jenv);
+ if (envp == NULL)
+ goto bail_out;
+
+#if DEBUGIT
+ fprintf(stderr, "command:");
+ print_array(cmd);
+ fprintf(stderr, "Envp:");
+ print_array(envp);
+ fprintf(stderr, "dirpath: %s\n", dirpath);
+ fprintf(stderr, "pts_name: %s\n", pts_name);
+#endif
+
+ pid = exec_pty(cmd[0], cmd, envp, dirpath, fd, pts_name, masterFD, console);
+ if (pid < 0)
+ goto bail_out;
+
+ channels[0] = fd[0];
+ channels[1] = fd[1];
+ channels[2] = fd[2];
+
+ bail_out:
+ (*env)->ReleaseIntArrayElements(env, jchannels, channels, 0);
+ (*env)->ReleaseStringUTFChars(env, jdir, dirpath);
+ (*env)->ReleaseStringUTFChars(env, jslaveName, pts_name);
+ if (cmd)
+ free_c_array(cmd);
+ if (envp)
+ free_c_array(envp);
+ return pid;
+}
+
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_Spawner_exec1(JNIEnv * env, jobject jobj,
+ jobjectArray jcmd,
+ jobjectArray jenv,
+ jstring jdir)
+{
+ const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);
+ char **cmd = NULL;
+ char **envp = NULL;
+ pid_t pid = -1;
+
+ cmd = alloc_c_array(env, jcmd);
+ if (cmd == NULL)
+ goto bail_out;
+
+ envp = alloc_c_array(env, jenv);
+ if (envp == NULL)
+ goto bail_out;
+
+#if DEBUGIT
+ fprintf(stderr, "command:");
+ print_array(cmd);
+ fprintf(stderr, "Envp:");
+ print_array(envp);
+ fprintf(stderr, "dirpath: %s\n", dirpath);
+#endif
+
+ pid = exec0(cmd[0], cmd, envp, dirpath, NULL);
+ if (pid < 0)
+ goto bail_out;
+
+ bail_out:
+ (*env)->ReleaseStringUTFChars(env, jdir, dirpath);
+ if (cmd)
+ free_c_array(cmd);
+ if (envp)
+ free_c_array(envp);
+ return pid;
+}
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: exec0
+ * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_Spawner_exec0(JNIEnv * env, jobject jobj,
+ jobjectArray jcmd,
+ jobjectArray jenv,
+ jstring jdir,
+ jintArray jchannels)
+{
+ jint *channels = (*env)->GetIntArrayElements(env, jchannels, 0);
+ const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);
+ char **cmd = NULL;
+ char **envp = NULL;
+ int fd[3];
+ pid_t pid = -1;
+
+ if (channels == NULL)
+ goto bail_out;
+
+ cmd = alloc_c_array(env, jcmd);
+ if (cmd == NULL)
+ goto bail_out;
+
+ envp = alloc_c_array(env, jenv);
+ if (envp == NULL)
+ goto bail_out;
+
+#if DEBUGIT
+ fprintf(stderr, "command:");
+ print_array(cmd);
+ fprintf(stderr, "Envp:");
+ print_array(envp);
+ fprintf(stderr, "dirpath: %s\n", dirpath);
+#endif
+
+ pid = exec0(cmd[0], cmd, envp, dirpath, fd);
+ if (pid < 0)
+ goto bail_out;
+
+ channels[0] = fd[0];
+ channels[1] = fd[1];
+ channels[2] = fd[2];
+
+ bail_out:
+ (*env)->ReleaseIntArrayElements(env, jchannels, channels, 0);
+ (*env)->ReleaseStringUTFChars(env, jdir, dirpath);
+ if (cmd)
+ free_c_array(cmd);
+ if (envp)
+ free_c_array(envp);
+ return pid;
+}
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: raise
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_Spawner_raise(JNIEnv * env, jobject jobj,
+ jint pid, jint sig)
+{
+ int status = -1;
+
+ switch (sig) {
+ case 0: /* NOOP */
+ status = kill(pid, 0);
+ break;
+
+ case 2: /* INTERRUPT */
+ status = kill(pid, SIGINT);
+ break;
+
+ case 9: /* KILL */
+ status = kill(pid, SIGKILL);
+ break;
+
+ case 15: /* TERM */
+ status = kill(pid, SIGTERM);
+ break;
+
+ default:
+ status = kill(pid, sig); /* WHAT ?? */
+ break;
+ }
+
+ return status;
+}
+
+
+
+/*
+ * Class: org_eclipse_cdt_utils_spawner_Spawner
+ * Method: waitFor
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor(JNIEnv * env,
+ jobject jobj, jint pid)
+{
+ return wait0(pid);
+}
diff --git a/com.google.eclipse.elt.pty.macosx/os/macosx/ppc/libgpty.jnilib b/com.google.eclipse.elt.pty.macosx/os/macosx/ppc/libgpty.jnilib
new file mode 100644
index 0000000..a7a98f9
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/os/macosx/ppc/libgpty.jnilib
Binary files differ
diff --git a/com.google.eclipse.elt.pty.macosx/os/macosx/ppc/libgspawner.jnilib b/com.google.eclipse.elt.pty.macosx/os/macosx/ppc/libgspawner.jnilib
new file mode 100644
index 0000000..96ae03d
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/os/macosx/ppc/libgspawner.jnilib
Binary files differ
diff --git a/com.google.eclipse.elt.pty.macosx/os/macosx/x86/libgpty.jnilib b/com.google.eclipse.elt.pty.macosx/os/macosx/x86/libgpty.jnilib
new file mode 100755
index 0000000..49ad7a7
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/os/macosx/x86/libgpty.jnilib
Binary files differ
diff --git a/com.google.eclipse.elt.pty.macosx/os/macosx/x86/libgspawner.jnilib b/com.google.eclipse.elt.pty.macosx/os/macosx/x86/libgspawner.jnilib
new file mode 100755
index 0000000..1377d5d
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/os/macosx/x86/libgspawner.jnilib
Binary files differ
diff --git a/com.google.eclipse.elt.pty.macosx/os/macosx/x86_64/libgpty.jnilib b/com.google.eclipse.elt.pty.macosx/os/macosx/x86_64/libgpty.jnilib
new file mode 100644
index 0000000..e0577a8
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/os/macosx/x86_64/libgpty.jnilib
Binary files differ
diff --git a/com.google.eclipse.elt.pty.macosx/os/macosx/x86_64/libgspawner.jnilib b/com.google.eclipse.elt.pty.macosx/os/macosx/x86_64/libgspawner.jnilib
new file mode 100644
index 0000000..06c3818
--- /dev/null
+++ b/com.google.eclipse.elt.pty.macosx/os/macosx/x86_64/libgspawner.jnilib
Binary files differ
diff --git a/com.google.eclipse.elt.pty/.classpath b/com.google.eclipse.elt.pty/.classpath
new file mode 100644
index 0000000..cbb71f6
--- /dev/null
+++ b/com.google.eclipse.elt.pty/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="utils"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/com.google.eclipse.elt.pty/.gitignore b/com.google.eclipse.elt.pty/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/com.google.eclipse.elt.pty/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/com.google.eclipse.elt.pty/.project b/com.google.eclipse.elt.pty/.project
new file mode 100644
index 0000000..3336d4a
--- /dev/null
+++ b/com.google.eclipse.elt.pty/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.google.eclipse.elt.pty</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ </natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.pty/.settings/org.eclipse.jdt.core.prefs b/com.google.eclipse.elt.pty/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..c537b63
--- /dev/null
+++ b/com.google.eclipse.elt.pty/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/com.google.eclipse.elt.pty/META-INF/MANIFEST.MF b/com.google.eclipse.elt.pty/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..a57dfb7
--- /dev/null
+++ b/com.google.eclipse.elt.pty/META-INF/MANIFEST.MF
@@ -0,0 +1,18 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.google.eclipse.elt.pty;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-Activator: com.google.eclipse.elt.pty.PtyPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: com.google.eclipse.elt.pty,
+ com.google.eclipse.elt.pty.internal,
+ com.google.eclipse.elt.pty.util,
+ org.eclipse.cdt.utils.pty,
+ org.eclipse.cdt.utils.spawner
+Require-Bundle: org.eclipse.core.resources,
+ org.eclipse.core.runtime;bundle-version="[3.7.0,4.0.0)",
+ com.ibm.icu;bundle-version="4.4.2"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
diff --git a/com.google.eclipse.elt.pty/build.properties b/com.google.eclipse.elt.pty/build.properties
new file mode 100644
index 0000000..b8073b7
--- /dev/null
+++ b/com.google.eclipse.elt.pty/build.properties
@@ -0,0 +1,19 @@
+###############################################################################
+# Copyright (c) 2003, 2009 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+bin.includes = plugin.properties,\
+ .,\
+ META-INF/
+source.. = src/,\
+ utils/
+
+jre.compilation.profile=JavaSE-1.6
+javacSource=1.6
+javacTarget=1.6
diff --git a/com.google.eclipse.elt.pty/plugin.properties b/com.google.eclipse.elt.pty/plugin.properties
new file mode 100644
index 0000000..8fb84bf
--- /dev/null
+++ b/com.google.eclipse.elt.pty/plugin.properties
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2003, 2011 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+# Anton Leherbauer (Wind River Systems)
+###############################################################################
+pluginName=CDT's Pseudo-terminal Support
+providerName=Eclipse CDT
+
+fragmentName.linux = CDT's Pseudo-terminal Support for Linux
+fragmentName.linux.x86 = CDT's Pseudo-terminal Support for Linux (x86)
+fragmentName.linux.x86_64 = CDT's Pseudo-terminal Support for Linux (x86_64)
+fragmentName.macosx = CDT's Pseudo-terminal Support for MacOS X
diff --git a/com.google.eclipse.elt.pty/src/com/google/eclipse/elt/pty/PtyPlugin.java b/com.google.eclipse.elt.pty/src/com/google/eclipse/elt/pty/PtyPlugin.java
new file mode 100644
index 0000000..a2f4a34
--- /dev/null
+++ b/com.google.eclipse.elt.pty/src/com/google/eclipse/elt/pty/PtyPlugin.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.pty;
+
+import java.util.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+import com.ibm.icu.text.MessageFormat;
+
+public class PtyPlugin extends Plugin {
+ public static final String PLUGIN_ID = "org.eclipse.cdt.core";
+
+ private static PtyPlugin plugin;
+ private static ResourceBundle resourceBundle;
+
+ static {
+ try {
+ resourceBundle = ResourceBundle.getBundle("com.google.eclipse.elt.pty.internal.PtyPluginResources");
+ } catch (MissingResourceException x) {
+ resourceBundle = null;
+ }
+ }
+
+ public PtyPlugin() {
+ plugin = this;
+ }
+
+ public static String getResourceString(String key) {
+ try {
+ return resourceBundle.getString(key);
+ } catch (MissingResourceException e) {
+ return "!" + key + "!";
+ } catch (NullPointerException e) {
+ return "#" + key + "#";
+ }
+ }
+
+ public static IWorkspace getWorkspace() {
+ return ResourcesPlugin.getWorkspace();
+ }
+
+ public static String getFormattedString(String key, String arg) {
+ return MessageFormat.format(getResourceString(key), new Object[] { arg });
+ }
+
+ public static String getFormattedString(String key, String[] args) {
+ return MessageFormat.format(getResourceString(key), (Object[]) args);
+ }
+
+ public static ResourceBundle getResourceBundle() {
+ return resourceBundle;
+ }
+
+ public static PtyPlugin getDefault() {
+ return plugin;
+ }
+
+ public static void log(Throwable e) {
+ String msg = e.getMessage();
+ if (msg == null) {
+ log("Error", e);
+ } else {
+ log("Error: " + msg, e);
+ }
+ }
+
+ public static void log(String message, Throwable e) {
+ log(createStatus(message, e));
+ }
+
+ public static IStatus createStatus(String message) {
+ return createStatus(message, null);
+ }
+
+ public static IStatus createStatus(String message, Throwable exception) {
+ return new Status(IStatus.ERROR, PLUGIN_ID, message, exception);
+ }
+
+ public static void log(IStatus status) {
+ getDefault().getLog().log(status);
+ }
+}
diff --git a/com.google.eclipse.elt.pty/src/com/google/eclipse/elt/pty/internal/PtyPluginResources.properties b/com.google.eclipse.elt.pty/src/com/google/eclipse/elt/pty/internal/PtyPluginResources.properties
new file mode 100644
index 0000000..62d2d05
--- /dev/null
+++ b/com.google.eclipse.elt.pty/src/com/google/eclipse/elt/pty/internal/PtyPluginResources.properties
@@ -0,0 +1,11 @@
+###############################################################################
+# Copyright (c) 2000, 2010, 2012 QNX Software Systems and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+Util.exception.cannotCreatePty=Cannot create pty
+Util.exception.cannotSetTerminalSize=Setting terminal size is not supported
+Util.error.cannotRun=Cannot run program "{0}": {1}
+Util.exception.closeError=close error
\ No newline at end of file
diff --git a/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/Platform.java b/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/Platform.java
new file mode 100644
index 0000000..cf9c871
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/Platform.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.pty.util;
+
+import java.io.*;
+
+import org.osgi.framework.Bundle;
+
+import com.google.eclipse.elt.pty.PtyPlugin;
+
+public final class Platform {
+ // This class duplicates all of the methods in org.eclipse.core.runtime.Platform
+ // that are used by the CDT. getOSArch() needs a few tweaks because the value returned
+ // by org.eclipse.core.runtime.Platform.getOSArch represents what the JVM thinks the
+ // architecture is. In some cases, we may actually be running on a 64-bit machine,
+ // but the JVM thinks it's running on a 32-bit machine. Without this change, the CDT
+ // will not handle 64-bit executables on some ppc64. This method could easily be
+ // extended to handle other platforms with similar issues.
+ //
+ // Unfortunately, the org.eclipse.core.runtime.Platform is final, so we cannot just
+ // extend it and and then override the getOSArch method, so getBundle and getOS just
+ // encapsulate calls to the same methods in org.eclipse.core.runtime.Platform.
+
+ public static final String OS_LINUX = org.eclipse.core.runtime.Platform.OS_LINUX;
+
+ private static String cachedArch = null;
+
+ public static Bundle getBundle(String symbolicName) {
+ return org.eclipse.core.runtime.Platform.getBundle(symbolicName);
+ }
+
+ public static String getOS() {
+ return org.eclipse.core.runtime.Platform.getOS();
+ }
+
+ public static String getOSArch() {
+ if (cachedArch == null) {
+ String arch = org.eclipse.core.runtime.Platform.getOSArch();
+ if (arch.equals(org.eclipse.core.runtime.Platform.ARCH_PPC)) {
+ // Determine if the platform is actually a ppc64 machine
+ Process unameProcess;
+ String cmd[] = { "uname", "-p" };
+ try {
+ unameProcess = Runtime.getRuntime().exec(cmd);
+ InputStreamReader inputStreamReader = new InputStreamReader(unameProcess.getInputStream());
+ BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+ String unameOutput = bufferedReader.readLine();
+ if (unameOutput != null) {
+ arch = unameOutput;
+ }
+ bufferedReader.close();
+ unameProcess.waitFor(); // otherwise the process becomes a zombie
+ } catch (IOException e) {
+ PtyPlugin.log(e);
+ } catch (InterruptedException exc) {
+ // restore interrupted flag
+ Thread.currentThread().interrupt();
+ }
+ } else if (arch.equals(org.eclipse.core.runtime.Platform.ARCH_X86)) {
+ // Determine if the platform is actually a x86_64 machine
+ Process unameProcess;
+ String cmd[];
+ if (org.eclipse.core.runtime.Platform.OS_WIN32.equals(getOS())) {
+ cmd = new String[] { "cmd", "/d", "/c", "set", "PROCESSOR_ARCHITECTURE" };
+ } else {
+ // We don't use "uname -p" since it returns "unknown" on some Linux systems.
+ cmd = new String[] { "uname", "-m" };
+ }
+ try {
+ unameProcess = Runtime.getRuntime().exec(cmd);
+ unameProcess.getOutputStream().close();
+ unameProcess.getErrorStream().close();
+ InputStreamReader inputStreamReader = new InputStreamReader(unameProcess.getInputStream());
+ BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+ String unameOutput = bufferedReader.readLine();
+ if (unameOutput != null && unameOutput.endsWith("64")) {
+ arch = org.eclipse.core.runtime.Platform.ARCH_X86_64;
+ }
+ bufferedReader.close();
+ unameProcess.waitFor(); // otherwise the process becomes a zombie
+ } catch (IOException e) {
+ PtyPlugin.log(e);
+ } catch (InterruptedException e) {
+ // restore interrupted flag
+ Thread.currentThread().interrupt();
+ }
+ }
+ cachedArch = arch;
+ }
+ return cachedArch;
+ }
+
+ private Platform() {}
+}
diff --git a/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/Processes.java b/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/Processes.java
new file mode 100644
index 0000000..70cc4c3
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/Processes.java
@@ -0,0 +1,41 @@
+package com.google.eclipse.elt.pty.util;
+
+import org.eclipse.cdt.utils.pty.PTY;
+import org.eclipse.cdt.utils.spawner.ProcessFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Utility methods related to processes.
+ */
+public final class Processes {
+ /**
+ * Starts a new process.
+ * @param command the command to execute.
+ * @param environment the environment variables.
+ * @param workingDirectory the working directory.
+ * @return the new process.
+ * @throws IOException if something went wrong.
+ */
+ public static Process executeInPty(
+ String[] command, Map<String, String> environment, File workingDirectory) throws IOException {
+ PTY pty = new PTY(true);
+ ProcessFactory processFactory = ProcessFactory.getFactory();
+ return processFactory.exec(command, toArray(environment), workingDirectory, pty);
+ }
+
+ private static String[] toArray(Map<String, String> environmentMap) {
+ List<String> environment = new ArrayList<String>();
+ for (Entry<String, String> entry : environmentMap.entrySet()) {
+ environment.add(entry.getKey() + "=" + entry.getValue());
+ }
+ return environment.toArray(new String[environment.size()]);
+ }
+
+ private Processes() {}
+}
diff --git a/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/spawner/EnvironmentReader.java b/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/spawner/EnvironmentReader.java
new file mode 100644
index 0000000..214cb0f
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/com/google/eclipse/elt/pty/util/spawner/EnvironmentReader.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package com.google.eclipse.elt.pty.util.spawner;
+
+import java.util.*;
+
+import org.eclipse.core.runtime.Platform;
+
+/**
+ * This class provides environment variables supplied as {@link Properties} class.
+ */
+public final class EnvironmentReader {
+ private static Properties envVars = null;
+ private static Properties envVarsNormalized = null;
+ private static ArrayList<String> rawVars = null;
+
+ private static synchronized void init() {
+ if (envVars == null) {
+ envVars = new Properties();
+ // on Windows environment variable names are case-insensitive
+ if (Platform.getOS().equals(Platform.OS_WIN32)) {
+ envVarsNormalized = new Properties();
+ } else {
+ envVarsNormalized = envVars;
+ }
+ rawVars = new ArrayList<String>();
+ Map<String, String> envMap = System.getenv();
+ for (String var : envMap.keySet()) {
+ String value = envMap.get(var);
+ envVars.setProperty(var, value);
+ if (envVarsNormalized != envVars) {
+ envVarsNormalized.setProperty(var.toUpperCase(), value);
+ }
+ rawVars.add(var + "=" + value);
+ }
+ rawVars.trimToSize();
+ }
+ }
+
+ public static Properties getEnvVars() {
+ init();
+ return (Properties) envVars.clone();
+ }
+
+ public static String getEnvVar(String key) {
+ init();
+ return envVarsNormalized.getProperty(key);
+ }
+
+ private EnvironmentReader() {}
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTY.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTY.java
new file mode 100644
index 0000000..a51a8e1
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTY.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Copyright (c) 2002, 2010 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.cdt.utils.pty;
+
+import java.io.IOException;
+
+import com.google.eclipse.elt.pty.PtyPlugin;
+
+/**
+ * PTY - pseudo terminal support.
+ */
+public class PTY {
+ static final String LIBRARY_NAME = "gpty";
+
+ final boolean console;
+ String slave;
+ PTYInputStream in;
+ PTYOutputStream out;
+ /*
+ * NOTE: Field is accessed by the native layer. Do not refactor!
+ */
+ int master;
+
+ private static boolean hasPTY;
+ private static boolean setTerminalSizeErrorAlreadyLogged;
+
+ /**
+ * The master fd is used on two streams. We need to wrap the fd so that when stream.close() is called the other stream
+ * is disabled.
+ */
+ public class MasterFD {
+ public int getFD() {
+ return master;
+ }
+
+ void setFD(int fd) {
+ master = fd;
+ }
+ }
+
+ /**
+ * Create PTY for use with Eclipse console. Identical to {@link PTY#PTY(boolean) PTY(true)}.
+ * @throws IOException if something goes wrong.
+ */
+ public PTY() throws IOException {
+ this(true);
+ }
+
+ /**
+ * Create pseudo-terminal.
+ *
+ * <p>
+ * The provided flag indicates whether the pseudo terminal is used with the interactive Eclipse console:
+ * <ul>
+ * <li>If {@code true} the terminal is configured with no echo and stderr is redirected to a pipe instead of the
+ * PTY.</li>
+ * <li>If {@code false} the terminal is configured with echo and stderr is connected to the PTY. This mode is
+ * best suited for use with a proper terminal emulation. Note that this mode might not be supported on all platforms.
+ * Known platforms which support this mode are: <code>linux-x86</code>, <code>linux-x86_64</code>,
+ * <code>solaris-sparc</code>, <code>macosx</code>.</li>
+ * </ul>
+ * </p>
+ *
+ * @param console whether terminal is used with Eclipse console
+ * @throws IOException if the PTY could not be created
+ * @since 5.2
+ */
+ public PTY(boolean console) throws IOException {
+ this.console = console;
+
+ if (hasPTY) {
+ slave = openMaster(console);
+ }
+
+ if (slave == null) {
+ throw new IOException(PtyPlugin.getResourceString("Util.exception.cannotCreatePty"));
+ }
+
+ in = new PTYInputStream(new MasterFD());
+ out = new PTYOutputStream(new MasterFD());
+ }
+
+ public String getSlaveName() {
+ return slave;
+ }
+
+ public MasterFD getMasterFD() {
+ return new MasterFD();
+ }
+
+ /**
+ * @return whether this pseudo terminal is for use with the Eclipse console.
+ */
+ public final boolean isConsole() {
+ return console;
+ }
+
+ public PTYOutputStream getOutputStream() {
+ return out;
+ }
+
+ public PTYInputStream getInputStream() {
+ return in;
+ }
+
+ /**
+ * Change terminal window size to given width and height.
+ * <p>
+ * This should only be used when the pseudo terminal is configured for use with a terminal emulation, i.e. when
+ * {@link #isConsole()} returns {@code false}.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This method may not be supported on all platforms. Known platforms which support this method
+ * are: {@code linux-x86}, {@code linux-x86_64}, {@code solaris-sparc}, {@code macosx}.
+ * </p>
+ * @param width the given width.
+ * @param height the given height.
+ */
+ public final void setTerminalSize(int width, int height) {
+ try {
+ change_window_size(master, width, height);
+ } catch (UnsatisfiedLinkError e) {
+ if (!setTerminalSizeErrorAlreadyLogged) {
+ setTerminalSizeErrorAlreadyLogged = true;
+ PtyPlugin.log(PtyPlugin.getResourceString("Util.exception.cannotSetTerminalSize"), e);
+ }
+ }
+ }
+
+ public static boolean isSupported() {
+ return hasPTY;
+ }
+
+ native String openMaster(boolean console);
+
+ native int change_window_size(int fdm, int width, int height);
+
+ static {
+ System.loadLibrary(LIBRARY_NAME);
+ hasPTY = true;
+ }
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTYInputStream.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTYInputStream.java
new file mode 100644
index 0000000..a2f1a1e
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTYInputStream.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.cdt.utils.pty;
+
+import java.io.*;
+
+import org.eclipse.cdt.utils.pty.PTY.MasterFD;
+
+class PTYInputStream extends InputStream {
+ MasterFD master;
+
+ /**
+ * From a Unix valid file descriptor set a Reader.
+ * @param fd file descriptor.
+ */
+ public PTYInputStream(MasterFD fd) {
+ master = fd;
+ }
+
+ /**
+ * Implementation of read for the InputStream.
+ *
+ * @exception IOException on error.
+ */
+ @Override public int read() throws IOException {
+ byte b[] = new byte[1];
+ if (1 != read(b, 0, 1)) {
+ return -1;
+ }
+ return b[0];
+ }
+
+ @Override public int read(byte[] buf, int off, int len) throws IOException {
+ if (buf == null) {
+ throw new NullPointerException();
+ }
+ if ((off < 0) || (off > buf.length) || (len < 0) || ((off + len) > buf.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (len == 0) {
+ return 0;
+ }
+ byte[] tmpBuf = new byte[len];
+ len = read0(master.getFD(), tmpBuf, len);
+ if (len <= 0) {
+ return -1;
+ }
+ System.arraycopy(tmpBuf, 0, buf, off, len);
+ return len;
+ }
+
+ @Override public void close() throws IOException {
+ if (master.getFD() == -1) {
+ return;
+ }
+ close0(master.getFD());
+ master.setFD(-1);
+ }
+
+ @Override protected void finalize() throws IOException {
+ close();
+ }
+
+ private native int read0(int fd, byte[] buf, int len) throws IOException;
+
+ private native int close0(int fd) throws IOException;
+
+ static {
+ System.loadLibrary(PTY.LIBRARY_NAME);
+ }
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTYOutputStream.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTYOutputStream.java
new file mode 100644
index 0000000..265fd26
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/pty/PTYOutputStream.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.cdt.utils.pty;
+
+import java.io.*;
+
+import org.eclipse.cdt.utils.pty.PTY.MasterFD;
+
+public class PTYOutputStream extends OutputStream {
+ MasterFD master;
+
+ /**
+ * From a Unix valid file descriptor set a Reader.
+ * @param fd file descriptor.
+ */
+ public PTYOutputStream(MasterFD fd) {
+ master = fd;
+ }
+
+ @Override public void write(byte[] b, int off, int len) throws IOException {
+ if (b == null) {
+ throw new NullPointerException();
+ } else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return;
+ }
+ byte[] tmpBuf = new byte[len];
+ System.arraycopy(b, off, tmpBuf, off, len);
+ write0(master.getFD(), tmpBuf, len);
+ }
+
+ @Override public void write(int b) throws IOException {
+ byte[] buf = new byte[1];
+ buf[0] = (byte) b;
+ write(buf, 0, 1);
+ }
+
+ @Override public void close() throws IOException {
+ if (master.getFD() == -1) {
+ return;
+ }
+ int status = close0(master.getFD());
+ if (status == -1) {
+ throw new IOException("Close error");
+ }
+ master.setFD(-1);
+ }
+
+ @Override protected void finalize() throws IOException {
+ close();
+ }
+
+ private native int write0(int fd, byte[] b, int len) throws IOException;
+
+ private native int close0(int fd) throws IOException;
+
+ static {
+ System.loadLibrary(PTY.LIBRARY_NAME);
+ }
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/ProcessFactory.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/ProcessFactory.java
new file mode 100644
index 0000000..d01c706
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/ProcessFactory.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.cdt.utils.spawner;
+
+import java.io.*;
+
+import org.eclipse.cdt.utils.pty.PTY;
+
+import com.google.eclipse.elt.pty.PtyPlugin;
+
+public class ProcessFactory {
+ static private ProcessFactory instance;
+
+ private boolean hasSpawner;
+ private final Runtime runtime;
+
+ private ProcessFactory() {
+ hasSpawner = false;
+ String OS = System.getProperty("os.name").toLowerCase();
+ runtime = Runtime.getRuntime();
+ // Spawner does not work for Windows 98 fallback
+ if (OS != null && OS.equals("windows 98")) {
+ hasSpawner = false;
+ } else {
+ System.loadLibrary(Spawner.LIBRARY_NAME);
+ hasSpawner = true;
+ }
+ }
+
+ public static ProcessFactory getFactory() {
+ if (instance == null) {
+ instance = new ProcessFactory();
+ }
+ return instance;
+ }
+
+ public Process exec(String cmd) throws IOException {
+ if (hasSpawner) {
+ return new Spawner(cmd);
+ }
+ return runtime.exec(cmd);
+ }
+
+ public Process exec(String[] cmdarray) throws IOException {
+ if (hasSpawner) {
+ return new Spawner(cmdarray);
+ }
+ return runtime.exec(cmdarray);
+ }
+
+ public Process exec(String[] cmdarray, String[] envp) throws IOException {
+ if (hasSpawner) {
+ return new Spawner(cmdarray, envp);
+ }
+ return runtime.exec(cmdarray, envp);
+ }
+
+ public Process exec(String cmd, String[] envp) throws IOException {
+ if (hasSpawner) {
+ return new Spawner(cmd, envp);
+ }
+ return runtime.exec(cmd, envp);
+ }
+
+ public Process exec(String cmd, String[] envp, File dir) throws IOException {
+ if (hasSpawner) {
+ return new Spawner(cmd, envp, dir);
+ }
+ return runtime.exec(cmd, envp, dir);
+ }
+
+ public Process exec(String cmdarray[], String[] envp, File dir) throws IOException {
+ if (hasSpawner) {
+ return new Spawner(cmdarray, envp, dir);
+ }
+ return runtime.exec(cmdarray, envp, dir);
+ }
+
+ public Process exec(String cmdarray[], String[] envp, File dir, PTY pty) throws IOException {
+ if (hasSpawner) {
+ return new Spawner(cmdarray, envp, dir, pty);
+ }
+ throw new UnsupportedOperationException(PtyPlugin.getResourceString("Util.exception.cannotCreatePty"));
+ }
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/Spawner.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/Spawner.java
new file mode 100644
index 0000000..bf63ab6
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/Spawner.java
@@ -0,0 +1,438 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.cdt.utils.spawner;
+
+import java.io.*;
+import java.util.StringTokenizer;
+
+import org.eclipse.cdt.utils.pty.PTY;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.osgi.util.NLS;
+
+import com.google.eclipse.elt.pty.PtyPlugin;
+
+public class Spawner extends Process {
+ static final String LIBRARY_NAME = "gspawner";
+
+ public int NOOP = 0;
+ public int HUP = 1;
+ public int KILL = 9;
+ public int TERM = 15;
+
+ /**
+ * On Windows, what this does is far from easy to explain. Some of the logic is in the JNI code, some in the
+ * spawner.exe code.
+ *
+ * <ul>
+ * <li>If the process this is being raised against was launched by us (the Spawner)
+ * <ul>
+ * <li>If the process is a cygwin program (has the cygwin1.dll loaded), then issue a 'kill -SIGINT'. If the 'kill'
+ * utility isn't available, send the process a CTRL-C
+ * <li>If the process is <i>not</i> a cygwin program, send the process a CTRL-C
+ * </ul>
+ * <li>If the process this is being raised against was <i>not</i> launched by us, use DebugBreakProcess to interrupt
+ * it (sending a CTRL-C is easy only if we share a console with the target process)
+ * </ul>
+ *
+ * On non-Windows, raising this just raises a POSIX SIGINT
+ *
+ */
+ public int INT = 2;
+
+ /**
+ * A fabricated signal number for use on Windows only. Tells the starter program to send a CTRL-C regardless of
+ * whether the process is a Cygwin one or not.
+ *
+ * @since 5.2
+ */
+ public int CTRLC = 1000; // arbitrary high number to avoid collision
+
+ int pid = 0;
+ int status;
+ final int[] fChannels = new int[3];
+ boolean isDone;
+ OutputStream out;
+ InputStream in;
+ InputStream err;
+ private PTY fPty;
+
+ public Spawner(String command, boolean bNoRedirect) throws IOException {
+ StringTokenizer tokenizer = new StringTokenizer(command);
+ String[] cmdarray = new String[tokenizer.countTokens()];
+ for (int n = 0; tokenizer.hasMoreTokens(); n++) {
+ cmdarray[n] = tokenizer.nextToken();
+ }
+ if (bNoRedirect) {
+ exec_detached(cmdarray, new String[0], ".");
+ } else {
+ exec(cmdarray, new String[0], ".");
+ }
+ }
+
+ protected Spawner(String[] command, String[] environment, File workingDirectory) throws IOException {
+ String dirpath = ".";
+ if (workingDirectory != null) {
+ dirpath = workingDirectory.getAbsolutePath();
+ }
+ exec(command, environment, dirpath);
+ }
+
+ protected Spawner(String[] cmdarray, String[] envp, File dir, PTY pty) throws IOException {
+ String dirpath = ".";
+ if (dir != null) {
+ dirpath = dir.getAbsolutePath();
+ }
+ fPty = pty;
+ exec_pty(cmdarray, envp, dirpath, pty);
+ }
+
+ protected Spawner(String command) throws IOException {
+ this(command, null);
+ }
+
+ protected Spawner(String[] command) throws IOException {
+ this(command, null);
+ }
+
+ protected Spawner(String[] command, String[] environment) throws IOException {
+ this(command, environment, null);
+ }
+
+ protected Spawner(String command, String[] environment) throws IOException {
+ this(command, environment, null);
+ }
+
+ protected Spawner(String command, String[] environment, File workingDirectory) throws IOException {
+ StringTokenizer tokenizer = new StringTokenizer(command);
+ String[] cmdarray = new String[tokenizer.countTokens()];
+ for (int n = 0; tokenizer.hasMoreTokens(); n++) {
+ cmdarray[n] = tokenizer.nextToken();
+ }
+ String dirpath = ".";
+ if (workingDirectory != null) {
+ dirpath = workingDirectory.getAbsolutePath();
+ }
+ exec(cmdarray, environment, dirpath);
+ }
+
+ @Override protected void finalize() throws Throwable {
+ closeUnusedStreams();
+ }
+
+ /**
+ * See java.lang.Process#getInputStream (); The client is responsible for closing the stream explicitly.
+ **/
+ @Override public synchronized InputStream getInputStream() {
+ if (null == in) {
+ if (fPty != null) {
+ in = fPty.getInputStream();
+ } else {
+ in = new SpawnerInputStream(fChannels[1]);
+ }
+ }
+ return in;
+ }
+
+ /**
+ * See java.lang.Process#getOutputStream (); The client is responsible for closing the stream explicitly.
+ **/
+ @Override public synchronized OutputStream getOutputStream() {
+ if (null == out) {
+ if (fPty != null) {
+ out = fPty.getOutputStream();
+ } else {
+ out = new SpawnerOutputStream(fChannels[0]);
+ }
+ }
+ return out;
+ }
+
+ /**
+ * See java.lang.Process#getErrorStream (); The client is responsible for closing the stream explicitly.
+ **/
+ @Override public synchronized InputStream getErrorStream() {
+ if (null == err) {
+ if (fPty != null && !fPty.isConsole()) {
+ // If PTY is used and it's not in "Console" mode, then stderr is
+ // redirected to the PTY's output stream. Therefore, return a
+ // dummy stream for error stream.
+ err = new InputStream() {
+ @Override public int read() throws IOException {
+ return -1;
+ }
+ };
+ } else {
+ err = new SpawnerInputStream(fChannels[2]);
+ }
+ }
+ return err;
+ }
+
+ /**
+ * See java.lang.Process#waitFor ();
+ **/
+ @Override public synchronized int waitFor() throws InterruptedException {
+ while (!isDone) {
+ wait();
+ }
+
+ // For situations where the user does not call destroy(),
+ // we try to kill the streams that were not used here.
+ // We check for streams that were not created, we create
+ // them to attach to the pipes, and then we close them
+ // to release the pipes.
+ // Streams that were created by the client need to be
+ // closed by the client itself.
+ //
+ // But 345164
+ closeUnusedStreams();
+ return status;
+ }
+
+ /**
+ * See java.lang.Process#exitValue ();
+ **/
+ @Override public synchronized int exitValue() {
+ if (!isDone) {
+ throw new IllegalThreadStateException("Process not Terminated");
+ }
+ return status;
+ }
+
+ /**
+ * See java.lang.Process#destroy ();
+ *
+ * Clients are responsible for explicitly closing any streams that they have requested through getErrorStream(),
+ * getInputStream() or getOutputStream()
+ **/
+ @Override public synchronized void destroy() {
+ // Sends the TERM
+ terminate();
+
+ // Close the streams on this side.
+ //
+ // We only close the streams that were
+ // never used by any client.
+ // So, if the stream was not created yet,
+ // we create it ourselves and close it
+ // right away, so as to release the pipe.
+ // Note that even if the stream was never
+ // created, the pipe has been allocated in
+ // native code, so we need to create the
+ // stream and explicitly close it.
+ //
+ // We don't close streams the clients have
+ // created because we don't know when the
+ // client will be finished using them.
+ // It is up to the client to close those
+ // streams.
+ //
+ // But 345164
+ closeUnusedStreams();
+
+ // Grace before using the heavy gone.
+ if (!isDone) {
+ try {
+ wait(1000);
+ } catch (InterruptedException e) {
+ }
+ }
+ if (!isDone) {
+ kill();
+ }
+ }
+
+ public int interrupt() {
+ return raise(pid, INT);
+ }
+
+ public int interruptCTRLC() {
+ if (Platform.getOS().equals(Platform.OS_WIN32)) {
+ return raise(pid, CTRLC);
+ }
+ return interrupt();
+ }
+
+ public int hangup() {
+ return raise(pid, HUP);
+ }
+
+ public int kill() {
+ return raise(pid, KILL);
+ }
+
+ public int terminate() {
+ return raise(pid, TERM);
+ }
+
+ public boolean isRunning() {
+ return (raise(pid, NOOP) == 0);
+ }
+
+ private void exec(String[] cmdarray, String[] envp, String dirpath) throws IOException {
+ String command = cmdarray[0];
+ SecurityManager s = System.getSecurityManager();
+ if (s != null) {
+ s.checkExec(command);
+ }
+ if (envp == null) {
+ envp = new String[0];
+ }
+ Reaper reaper = new Reaper(cmdarray, envp, dirpath);
+ reaper.setDaemon(true);
+ reaper.start();
+ // Wait until the subprocess is started or error.
+ synchronized (this) {
+ while (pid == 0) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ // Check for errors.
+ if (pid == -1) {
+ throw new IOException(reaper.getErrorMessage());
+ }
+ }
+
+ private void exec_pty(String[] command, String[] environment, String workingDirectory, PTY pty) throws IOException {
+ String cmd = command[0];
+ SecurityManager s = System.getSecurityManager();
+ if (s != null) {
+ s.checkExec(cmd);
+ }
+ if (environment == null) {
+ environment = new String[0];
+ }
+ final String slaveName = pty.getSlaveName();
+ final int masterFD = pty.getMasterFD().getFD();
+ final boolean console = pty.isConsole();
+ // int fdm = pty.get
+ Reaper reaper = new Reaper(command, environment, workingDirectory) {
+ @Override int execute(String[] cmd, String[] env, String dir, int[] channels) throws IOException {
+ return exec2(cmd, env, dir, channels, slaveName, masterFD, console);
+ }
+ };
+ reaper.setDaemon(true);
+ reaper.start();
+ // Wait until the subprocess is started or error.
+ synchronized (this) {
+ while (pid == 0) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ if (pid == -1) {
+ throw new IOException("Exec_tty error:" + reaper.getErrorMessage());
+ }
+ }
+
+ public void exec_detached(String[] cmdarray, String[] envp, String dirpath) throws IOException {
+ String command = cmdarray[0];
+ SecurityManager s = System.getSecurityManager();
+ if (s != null) {
+ s.checkExec(command);
+ }
+ if (envp == null) {
+ envp = new String[0];
+ }
+ pid = exec1(cmdarray, envp, dirpath);
+ if (pid == -1) {
+ throw new IOException("Exec error");
+ }
+ fChannels[0] = -1;
+ fChannels[1] = -1;
+ fChannels[2] = -1;
+ }
+
+ private synchronized void closeUnusedStreams() {
+ try {
+ if (null == err) {
+ getErrorStream().close();
+ }
+ } catch (IOException e) {}
+ try {
+ if (null == in) {
+ getInputStream().close();
+ }
+ } catch (IOException e) {}
+ try {
+ if (null == out) {
+ getOutputStream().close();
+ }
+ } catch (IOException e) {}
+ }
+
+ native int exec0(String[] cmdarray, String[] envp, String dir, int[] chan) throws IOException;
+
+ native int exec1(String[] cmdarray, String[] envp, String dir) throws IOException;
+
+ native int exec2(String[] cmdarray, String[] envp, String dir, int[] chan, String slaveName, int masterFD,
+ boolean console) throws IOException;
+
+ public native int raise(int processID, int sig);
+
+ native int waitFor(int processID);
+
+ static {
+ System.loadLibrary(LIBRARY_NAME);
+ }
+
+ // Spawn a thread to handle the forking and waiting.
+ // We do it this way because on linux the SIGCHLD is send to the one thread. So do the forking and then wait in the
+ // same thread.
+ class Reaper extends Thread {
+ String[] command;
+ String[] environment;
+ String dirPath;
+ volatile Throwable exception;
+
+ public Reaper(String[] command, String[] environment, String dirPath) {
+ super("Spawner Reaper");
+ this.command = command;
+ this.environment = environment;
+ this.dirPath = dirPath;
+ exception = null;
+ }
+
+ int execute(String[] cmdarray, String[] envp, String dir, int[] channels) throws IOException {
+ return exec0(cmdarray, envp, dir, channels);
+ }
+
+ @Override public void run() {
+ try {
+ pid = execute(command, environment, dirPath, fChannels);
+ } catch (Exception e) {
+ pid = -1;
+ exception = e;
+ }
+ // Tell spawner that the process started.
+ synchronized (Spawner.this) {
+ Spawner.this.notifyAll();
+ }
+ if (pid != -1) {
+ // Sync with spawner and notify when done.
+ status = waitFor(pid);
+ synchronized (Spawner.this) {
+ isDone = true;
+ Spawner.this.notifyAll();
+ }
+ }
+ }
+
+ public String getErrorMessage() {
+ final String reason = exception != null ? exception.getMessage() : "Unknown reason";
+ return NLS.bind(PtyPlugin.getResourceString("Util.error.cannotRun"), command[0], reason);
+ }
+ }
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/SpawnerInputStream.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/SpawnerInputStream.java
new file mode 100644
index 0000000..37f5566
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/SpawnerInputStream.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.cdt.utils.spawner;
+
+import java.io.*;
+
+import com.google.eclipse.elt.pty.PtyPlugin;
+
+class SpawnerInputStream extends InputStream {
+ private int fd;
+
+ /**
+ * From a Unix valid file descriptor set a Reader.
+ * @param fd file descriptor.
+ */
+ public SpawnerInputStream(int fd) {
+ this.fd = fd;
+ }
+
+ @Override public int read() throws IOException {
+ byte b[] = new byte[1];
+ if (1 != read(b, 0, 1)) {
+ return -1;
+ }
+ return b[0];
+ }
+
+ @Override public int read(byte[] buf, int off, int len) throws IOException {
+ if (buf == null) {
+ throw new NullPointerException();
+ } else if ((off < 0) || (off > buf.length) || (len < 0) || ((off + len) > buf.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+ byte[] tmpBuf = off > 0 ? new byte[len] : buf;
+
+ len = read0(fd, tmpBuf, len);
+ if (len <= 0) {
+ return -1;
+ }
+
+ if (tmpBuf != buf) {
+ System.arraycopy(tmpBuf, 0, buf, off, len);
+ }
+ return len;
+ }
+
+ @Override public void close() throws IOException {
+ if (fd == -1) {
+ return;
+ }
+ int status = close0(fd);
+ if (status == -1) {
+ throw new IOException(PtyPlugin.getResourceString("Util.exception.closeError"));
+ }
+ fd = -1;
+ }
+
+ @Override public int available() throws IOException {
+ try {
+ return available0(fd);
+ } catch (UnsatisfiedLinkError e) {
+ // for those platforms that do not implement available0
+ return super.available();
+ }
+ }
+
+ @Override protected void finalize() throws IOException {
+ close();
+ }
+
+ private native int read0(int fileDesc, byte[] buf, int len) throws IOException;
+
+ private native int close0(int fileDesc) throws IOException;
+
+ private native int available0(int fileDesc) throws IOException;
+
+ static {
+ System.loadLibrary(Spawner.LIBRARY_NAME);
+ }
+}
diff --git a/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/SpawnerOutputStream.java b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/SpawnerOutputStream.java
new file mode 100644
index 0000000..7ecc61e
--- /dev/null
+++ b/com.google.eclipse.elt.pty/utils/org/eclipse/cdt/utils/spawner/SpawnerOutputStream.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.cdt.utils.spawner;
+
+import java.io.*;
+
+public class SpawnerOutputStream extends OutputStream {
+ private int fd;
+
+ /**
+ * From a Unix valid file descriptor set a Reader.
+ * @param fd file descriptor.
+ */
+ public SpawnerOutputStream(int fd) {
+ this.fd = fd;
+ }
+
+ @Override public void write(byte[] b, int off, int len) throws IOException {
+ if (b == null) {
+ throw new NullPointerException();
+ }
+ if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (len == 0) {
+ return;
+ }
+ byte[] tmpBuf = new byte[len];
+ System.arraycopy(b, off, tmpBuf, off, len);
+ write0(fd, tmpBuf, len);
+ }
+
+ @Override public void write(int b) throws IOException {
+ byte[] buf = new byte[1];
+ buf[0] = (byte) b;
+ write(buf, 0, 1);
+ }
+
+ @Override public void close() throws IOException {
+ if (fd == -1) {
+ return;
+ }
+ int status = close0(fd);
+ if (status == -1) {
+ throw new IOException("close error");
+ }
+ fd = -1;
+ }
+
+ @Override protected void finalize() throws IOException {
+ close();
+ }
+
+ private native int write0(int fd, byte[] b, int len) throws IOException;
+
+ private native int close0(int fd);
+
+ static {
+ System.loadLibrary(Spawner.LIBRARY_NAME);
+ }
+}
diff --git a/com.google.eclipse.elt.view/.classpath b/com.google.eclipse.elt.view/.classpath
new file mode 100644
index 0000000..ad32c83
--- /dev/null
+++ b/com.google.eclipse.elt.view/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/com.google.eclipse.elt.view/.project b/com.google.eclipse.elt.view/.project
new file mode 100644
index 0000000..8264399
--- /dev/null
+++ b/com.google.eclipse.elt.view/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.google.eclipse.elt.view</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/com.google.eclipse.elt.view/.settings/org.eclipse.jdt.core.prefs b/com.google.eclipse.elt.view/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..c537b63
--- /dev/null
+++ b/com.google.eclipse.elt.view/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/com.google.eclipse.elt.view/META-INF/MANIFEST.MF b/com.google.eclipse.elt.view/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..70e286f
--- /dev/null
+++ b/com.google.eclipse.elt.view/META-INF/MANIFEST.MF
@@ -0,0 +1,15 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Terminal
+Bundle-SymbolicName: com.google.eclipse.elt.view;singleton:=true
+Bundle-Version: 1.1.0.qualifier
+Bundle-Activator: com.google.eclipse.elt.view.Activator
+Bundle-Vendor: Google, Inc.
+Require-Bundle: com.google.eclipse.elt.emulator;bundle-version="1.1.0",
+ com.google.eclipse.elt.pty;bundle-version="1.1.0",
+ org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.debug.core,
+ org.eclipse.jface.text
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
diff --git a/com.google.eclipse.elt.view/build.properties b/com.google.eclipse.elt.view/build.properties
new file mode 100644
index 0000000..6c480f3
--- /dev/null
+++ b/com.google.eclipse.elt.view/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ icons/
diff --git a/com.google.eclipse.elt.view/icons/change_title.gif b/com.google.eclipse.elt.view/icons/change_title.gif
new file mode 100644
index 0000000..efc77a7
--- /dev/null
+++ b/com.google.eclipse.elt.view/icons/change_title.gif
Binary files differ
diff --git a/com.google.eclipse.elt.view/icons/new_terminal.gif b/com.google.eclipse.elt.view/icons/new_terminal.gif
new file mode 100644
index 0000000..80201e0
--- /dev/null
+++ b/com.google.eclipse.elt.view/icons/new_terminal.gif
Binary files differ
diff --git a/com.google.eclipse.elt.view/icons/scroll_lock.gif b/com.google.eclipse.elt.view/icons/scroll_lock.gif
new file mode 100644
index 0000000..68fd6cf
--- /dev/null
+++ b/com.google.eclipse.elt.view/icons/scroll_lock.gif
Binary files differ
diff --git a/com.google.eclipse.elt.view/icons/terminal.gif b/com.google.eclipse.elt.view/icons/terminal.gif
new file mode 100644
index 0000000..557d001
--- /dev/null
+++ b/com.google.eclipse.elt.view/icons/terminal.gif
Binary files differ
diff --git a/com.google.eclipse.elt.view/plugin.xml b/com.google.eclipse.elt.view/plugin.xml
new file mode 100644
index 0000000..8ae47d4
--- /dev/null
+++ b/com.google.eclipse.elt.view/plugin.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension point="org.eclipse.ui.views">
+ <view
+ allowMultiple="true"
+ category="org.eclipse.ui"
+ class="com.google.eclipse.elt.view.ui.TerminalView"
+ icon="icons/terminal.gif"
+ id="com.google.eclipse.terminal.local.localTerminalView"
+ name="Terminal"
+ restorable="true">
+ </view>
+ </extension>
+ <extension point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension targetID="*">
+ <view
+ id="com.google.eclipse.terminal.local.localTerminalView"
+ minimized="false"
+ relationship="stack"
+ relative="org.eclipse.ui.console.ConsoleView">
+ </view>
+ </perspectiveExtension>
+ </extension>
+ <extension point="org.eclipse.ui.preferencePages">
+ <page
+ category="com.google.eclipse.terminal.local.page.root"
+ class="com.google.eclipse.elt.view.preferences.ColorsAndFontsPreferencePage"
+ id="com.google.eclipse.terminal.local.page.colorsAndFonts"
+ name="Colors and Fonts">
+ </page>
+ <page
+ class="com.google.eclipse.elt.view.preferences.RootPreferencePage"
+ id="com.google.eclipse.terminal.local.page.root"
+ name="Terminal">
+ </page>
+ </extension>
+ <extension point="org.eclipse.core.runtime.preferences">
+ <initializer class="com.google.eclipse.elt.view.preferences.PreferenceInitializer">
+ </initializer>
+ </extension>
+ <extension point="org.eclipse.ui.commands">
+ <command
+ categoryId="org.eclipse.ui.category.views"
+ id="com.google.eclipse.terminal.local.open"
+ name="Open Terminal Here">
+ </command>
+ </extension>
+ <extension point="org.eclipse.ui.commandImages">
+ <image
+ commandId="com.google.eclipse.terminal.local.open"
+ icon="icons/terminal.gif">
+ </image>
+ </extension>
+ <extension point="org.eclipse.ui.menus">
+ <menuContribution
+ allPopups="false"
+ locationURI="popup:org.eclipse.ui.popup.any?after=additions">
+ <command
+ commandId="com.google.eclipse.terminal.local.open"
+ style="push">
+ <visibleWhen checkEnabled="false">
+ <and>
+ <with variable="selection">
+ <count value="1">
+ </count>
+ </with>
+ <iterate>
+ <adapt type="org.eclipse.core.resources.IResource">
+ </adapt>
+ </iterate>
+ </and>
+ </visibleWhen>
+ </command>
+ </menuContribution>
+ </extension>
+ <extension point="org.eclipse.ui.handlers">
+ <handler
+ class="com.google.eclipse.elt.view.command.OpenTerminalCommand"
+ commandId="com.google.eclipse.terminal.local.open">
+ </handler>
+ </extension>
+ <extension point="org.eclipse.ui.contexts">
+ <context
+ description="In Terminal"
+ id="com.google.eclipse.terminal.local.context.localTerminal"
+ name="Terminal"
+ parentId="org.eclipse.ui.contexts.window">
+ </context>
+ </extension>
+ <extension point="org.eclipse.ui.commands">
+ <category
+ id="com.google.eclipse.terminal.local.commands.category"
+ name="Terminal">
+ </category>
+ <command
+ categoryId="com.google.eclipse.terminal.local.commands.category"
+ id="com.google.eclipse.terminal.local.copy"
+ name="Copy">
+ </command>
+ <command
+ categoryId="com.google.eclipse.terminal.local.commands.category"
+ id="com.google.eclipse.terminal.local.paste"
+ name="Paste">
+ </command>
+ </extension>
+ <extension point="org.eclipse.ui.bindings">
+ <key
+ commandId="com.google.eclipse.terminal.local.copy"
+ contextId="com.google.eclipse.terminal.local.context.localTerminal"
+ platform="carbon"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="M1+C">
+ </key>
+ <key
+ commandId="com.google.eclipse.terminal.local.copy"
+ contextId="com.google.eclipse.terminal.local.context.localTerminal"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="M1+M2+C">
+ </key>
+ <key
+ commandId="com.google.eclipse.terminal.local.paste"
+ contextId="com.google.eclipse.terminal.local.context.localTerminal"
+ platform="carbon"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="M1+V">
+ </key>
+ <key
+ commandId="com.google.eclipse.terminal.local.paste"
+ contextId="com.google.eclipse.terminal.local.context.localTerminal"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ sequence="M1+M2+V">
+ </key>
+ </extension>
+</plugin>
\ No newline at end of file
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/Activator.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/Activator.java
new file mode 100644
index 0000000..b5d42ee
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/Activator.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view;
+
+import static org.eclipse.core.runtime.IStatus.*;
+
+import static com.google.eclipse.elt.view.ImageKeys.*;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.*;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class Activator extends AbstractUIPlugin {
+ public static final String PLUGIN_ID = "com.google.eclipse.terminal.local";
+
+ private static Activator plugin;
+
+ @Override public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ }
+
+ @Override public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ @Override protected void initializeImageRegistry(ImageRegistry registry) {
+ addImage(registry, CHANGE_TITLE, "icons/change_title.gif");
+ addImage(registry, NEW_TERMINAL, "icons/new_terminal.gif");
+ addImage(registry, SCROLL_LOCK, "icons/scroll_lock.gif");
+ }
+
+ private void addImage(ImageRegistry registry, String key, String path) {
+ URL imageUrl = instance().getBundle().getEntry(path);
+ registry.put(key, ImageDescriptor.createFromURL(imageUrl));
+
+ }
+
+ public static void log(String message, Throwable cause) {
+ log(new Status(ERROR, PLUGIN_ID, OK, message, cause));
+ }
+
+ public static void log(IStatus status) {
+ instance().getLog().log(status);
+ }
+
+ public static ImageDescriptor imageDescriptor(String key) {
+ return instance().getImageRegistry().getDescriptor(key);
+ }
+
+ public static Activator instance() {
+ return plugin;
+ }
+
+ public static IPreferenceStore preferenceStore() {
+ return instance().getPreferenceStore();
+ }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ImageKeys.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ImageKeys.java
new file mode 100644
index 0000000..661c02e
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ImageKeys.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public final class ImageKeys {
+ public static final String CHANGE_TITLE = "changeTitle";
+ public static final String NEW_TERMINAL = "newTerminal";
+ public static final String SCROLL_LOCK = "scrollLock";
+
+ private ImageKeys() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/command/OpenTerminalCommand.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/command/OpenTerminalCommand.java
new file mode 100644
index 0000000..3b83cda
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/command/OpenTerminalCommand.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.command;
+
+import static com.google.eclipse.elt.view.ui.TerminalView.openTerminalView;
+
+import org.eclipse.core.commands.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class OpenTerminalCommand extends AbstractHandler {
+
+ @Override public Object execute(ExecutionEvent event) {
+ ISelection selection = HandlerUtil.getCurrentSelection(event);
+ IResource target = resourceFrom(selection);
+ if (target != null) {
+ if (!(target instanceof IContainer)) {
+ target = target.getParent();
+ }
+ openTerminalView(target.getLocation());
+ }
+ return null;
+ }
+
+ private IResource resourceFrom(ISelection selection) {
+ if (!(selection instanceof IStructuredSelection)) {
+ return null;
+ }
+ Object o = ((IStructuredSelection) selection).getFirstElement();
+ if (!(o instanceof IAdaptable)) {
+ return null;
+ }
+ return (IResource) ((IAdaptable) o).getAdapter(IResource.class);
+ }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/LifeCycleListener.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/LifeCycleListener.java
new file mode 100644
index 0000000..7c7273f
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/LifeCycleListener.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.connector;
+
+/**
+ * Listens for events related to the life cycle of a pseudo-terminal process.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public interface LifeCycleListener {
+ /**
+ * Notification that the execution pseudo-terminal process has finished.
+ */
+ void executionFinished();
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/LocalTerminalConnector.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/LocalTerminalConnector.java
new file mode 100644
index 0000000..0979c4a
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/LocalTerminalConnector.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.connector;
+
+import static org.eclipse.core.runtime.IStatus.*;
+
+import static com.google.eclipse.elt.emulator.provisional.api.TerminalState.*;
+import static com.google.eclipse.elt.pty.util.Platform.*;
+import static com.google.eclipse.elt.view.Activator.*;
+import static com.google.eclipse.elt.view.connector.Messages.*;
+import static com.google.eclipse.elt.view.connector.PseudoTerminal.isPlatformSupported;
+import static com.google.eclipse.elt.view.util.Platform.*;
+
+import java.io.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.debug.core.IStreamListener;
+import org.eclipse.debug.core.model.IStreamMonitor;
+import org.eclipse.debug.internal.core.StreamsProxy;
+import org.eclipse.osgi.util.NLS;
+
+import com.google.eclipse.elt.emulator.connector.TerminalConnector;
+import com.google.eclipse.elt.emulator.provisional.api.*;
+import com.google.eclipse.elt.emulator.provisional.api.provider.TerminalConnectorDelegate;
+
+/**
+ * Connector to local terminal.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@SuppressWarnings("restriction") // StreamsProxy is internal API
+public class LocalTerminalConnector extends TerminalConnectorDelegate implements LifeCycleListener {
+ private static final String ID = "com.google.eclipse.terminal.local.core.connector";
+
+ public static ITerminalConnector createLocalTerminalConnector(final String encoding) {
+ TerminalConnector.Factory factory = new TerminalConnector.Factory(){
+ @Override public TerminalConnectorDelegate makeConnector() {
+ return new LocalTerminalConnector(encoding);
+ }
+ };
+ TerminalConnector connector = new TerminalConnector(factory, ID, localTerminalName);
+ String errorMessage = connector.getInitializationErrorMessage();
+ if (errorMessage != null) {
+ throw new IllegalStateException(errorMessage);
+ }
+ return connector;
+ }
+
+ private IPath workingDirectory;
+ private PseudoTerminal pseudoTerminal;
+
+ private StreamsProxy streamsProxy;
+ private OutputStream terminalToRemoteStream;
+
+ private final String encoding;
+
+ private LocalTerminalConnector(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Verifies that PTY support is available on this platform.
+ * @throws CoreException if PTY support is <strong>not</strong> available on this platform.
+ * @see TerminalConnectorDelegate#initialize()
+ */
+ @Override public void initialize() throws CoreException {
+ if (!isPlatformSupported()) {
+ String message = NLS.bind(errorNoPseudoTerminalSupport, getOS(), getOSArch());
+ throw new CoreException(new Status(WARNING, PLUGIN_ID, message));
+ }
+ }
+
+ @Override protected void connect() {
+ terminalControl.setState(CONNECTING);
+ File workingDirectory = workingDirectory();
+ pseudoTerminal = new PseudoTerminal(workingDirectory);
+ pseudoTerminal.addLifeCycleListener(this);
+ try {
+ pseudoTerminal.launch();
+ streamsProxy = new StreamsProxy(pseudoTerminal.systemProcess(), encoding);
+ terminalToRemoteStream = new BufferedOutputStream(new TerminalOutputStream(streamsProxy, encoding), 1024);
+ addListeners(terminalControl, streamsProxy.getOutputStreamMonitor(), streamsProxy.getErrorStreamMonitor());
+ if (streamsProxy != null) {
+ terminalControl.setState(CONNECTED);
+ return;
+ }
+ } catch (Throwable t) {
+ log(new Status(INFO, PLUGIN_ID, OK, "Unable to start terminal", t));
+ }
+ terminalControl.setState(CLOSED);
+ }
+
+ private File workingDirectory() {
+ IPath path = (workingDirectory != null) ? workingDirectory : userHomeDirectory();
+ if (path == null) {
+ return null;
+ }
+ File file = path.toFile();
+ return (file.isDirectory()) ? file : null;
+ }
+
+ private void addListeners(ITerminalControl control, IStreamMonitor...monitors) throws UnsupportedEncodingException {
+ for (IStreamMonitor monitor : monitors) {
+ addListener(monitor, new TerminalOutputListener(control, encoding));
+ }
+ }
+
+ private void addListener(IStreamMonitor monitor, IStreamListener listener) {
+ monitor.addListener(listener);
+ listener.streamAppended(monitor.getContents(), monitor);
+ }
+
+ @Override public OutputStream getTerminalToRemoteStream() {
+ return terminalToRemoteStream;
+ }
+
+ /**
+ * Returns the system's default shell location as the settings summary.
+ * @return the system's default shell location as the settings summary.
+ */
+ @Override public String getSettingsSummary() {
+ return defaultShell().toString();
+ }
+
+ /**
+ * Notifies the pseudo-terminal that the size of the terminal has changed.
+ * @param newWidth the new terminal width (in columns.)
+ * @param newHeight the new terminal height (in lines.)
+ */
+ @Override public void setTerminalSize(int newWidth, int newHeight) {
+ if (pseudoTerminal != null) {
+ pseudoTerminal.updateSize(newWidth, newHeight);
+ }
+ }
+
+ public void setWorkingDirectory(IPath workingDirectory) {
+ this.workingDirectory = workingDirectory;
+ }
+
+ @Override protected void onDisconnect() {
+ pseudoTerminal.disconnect();
+ }
+
+ @Override public void executionFinished() {
+ terminalControl.setState(CLOSED);
+ if (streamsProxy != null) {
+ streamsProxy.close();
+ }
+ }
+
+ public void addLifeCycleListener(LifeCycleListener listener) {
+ pseudoTerminal.addLifeCycleListener(listener);
+ }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/Messages.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/Messages.java
new file mode 100644
index 0000000..53ad98e
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/Messages.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.connector;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class Messages extends NLS {
+ public static String localTerminalName;
+ public static String errorNoPseudoTerminalSupport;
+
+ static {
+ Class<Messages> type = Messages.class;
+ NLS.initializeMessages(type.getName(), type);
+ }
+
+ private Messages() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/Messages.properties b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/Messages.properties
new file mode 100644
index 0000000..e9586a6
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/Messages.properties
@@ -0,0 +1,2 @@
+localTerminalName=Terminal
+errorNoPseudoTerminalSupport=Pseudo-terminal support is not available on your host ({0}.{1})
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/PseudoTerminal.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/PseudoTerminal.java
new file mode 100644
index 0000000..6082aff
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/PseudoTerminal.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.connector;
+
+import static java.lang.Thread.currentThread;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+
+import static com.google.eclipse.elt.view.util.Platform.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+
+import org.eclipse.cdt.utils.pty.PTY;
+import org.eclipse.cdt.utils.spawner.ProcessFactory;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class PseudoTerminal {
+ private final List<LifeCycleListener> lifeCycleListeners = new ArrayList<LifeCycleListener>();
+ private final ExecutorService executor = newSingleThreadExecutor();
+
+ private final File workingDirectory;
+
+ private Process process;
+ private PTY pty;
+
+ private int width;
+ private int height;
+
+ PseudoTerminal(File workingDirectory) {
+ this.workingDirectory = workingDirectory;
+ }
+
+ void launch() throws IOException {
+ ProcessFactory factory = ProcessFactory.getFactory();
+ pty = new PTY(false);
+ process = factory.exec(command(), environment(), workingDirectory, pty);
+ executor.execute(new Runnable() {
+ @Override public void run() {
+ try {
+ process.waitFor();
+ } catch (InterruptedException ignored) {
+ currentThread().interrupt();
+ } finally {
+ notifyExecutionFinished();
+ }
+ }
+ });
+ }
+
+ private String[] command() {
+ return new String[] { defaultShell().getAbsolutePath(), "-i" };
+ }
+
+ private void notifyExecutionFinished() {
+ for (LifeCycleListener listener : lifeCycleListeners) {
+ listener.executionFinished();
+ }
+ }
+
+ void addLifeCycleListener(LifeCycleListener listener) {
+ lifeCycleListeners.add(listener);
+ }
+
+ Process systemProcess() {
+ return process;
+ }
+
+ void updateSize(int newWidth, int newHeight) {
+ if (pty != null && (newWidth != width || newHeight != height)) {
+ pty.setTerminalSize(width, height);
+ width = newWidth;
+ height = newHeight;
+ }
+ }
+
+ static boolean isPlatformSupported() {
+ return PTY.isSupported();
+ }
+
+ void disconnect() {
+ if (process != null) {
+ try {
+ process.exitValue();
+ } catch (IllegalThreadStateException e) {
+ process.destroy();
+ }
+ }
+ }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/TerminalOutputListener.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/TerminalOutputListener.java
new file mode 100644
index 0000000..f99952f
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/TerminalOutputListener.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.connector;
+
+import java.io.*;
+
+import org.eclipse.debug.core.IStreamListener;
+import org.eclipse.debug.core.model.IStreamMonitor;
+
+import com.google.eclipse.elt.emulator.provisional.api.ITerminalControl;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class TerminalOutputListener implements IStreamListener {
+ private final PrintStream printStream;
+
+ TerminalOutputListener(ITerminalControl control, String encoding) throws UnsupportedEncodingException {
+ printStream = new PrintStream(control.getRemoteToTerminalOutputStream(), true, encoding);
+ }
+
+ @Override public void streamAppended(String text, IStreamMonitor monitor) {
+ if (text.contains("\u001b[1A\u001b[K")) {
+ // clean = text.replace("\u001b[1A\u001b[K", "\u001b[K");
+ // TODO(alruiz) figure out why 1+ lines deleted in blaze build.
+ }
+ printStream.print(text);
+ }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/TerminalOutputStream.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/TerminalOutputStream.java
new file mode 100644
index 0000000..928d0fe
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/connector/TerminalOutputStream.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.connector;
+
+import java.io.*;
+
+import org.eclipse.debug.core.model.IStreamsProxy;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class TerminalOutputStream extends OutputStream {
+ private final IStreamsProxy streamsProxy;
+ private final String encoding;
+
+ TerminalOutputStream(IStreamsProxy streamsProxy, String encoding) {
+ this.streamsProxy = streamsProxy;
+ this.encoding = encoding;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ streamsProxy.write(new String(new byte[] { (byte) (b & 0xFF) }, encoding));
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ String input = new String(b, off, len, encoding);
+ streamsProxy.write(input);
+ }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/AbstractPreferencesChangeListener.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/AbstractPreferencesChangeListener.java
new file mode 100644
index 0000000..52b0c2d
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/AbstractPreferencesChangeListener.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import static com.google.eclipse.elt.view.preferences.PreferenceNames.*;
+
+import org.eclipse.jface.util.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public abstract class AbstractPreferencesChangeListener implements IPropertyChangeListener {
+ @Override public final void propertyChange(PropertyChangeEvent event) {
+ String property = event.getProperty();
+ if (BUFFER_LINE_COUNT.equals(property)) {
+ onBufferLineCountChanged();
+ }
+ if (BACKGROUND_COLOR.equals(property) || FOREGROUND_COLOR.equals(property)) {
+ onColorChanged();
+ }
+ if (CUSTOM_FONT_DATA.equals(property)) {
+ onFontChanged();
+ }
+ if (USE_BLINKING_CURSOR.equals(property)) {
+ onUseBlinkingCursorChanged();
+ }
+ }
+
+ protected abstract void onBufferLineCountChanged();
+
+ protected abstract void onColorChanged();
+
+ protected abstract void onFontChanged();
+
+ protected abstract void onUseBlinkingCursorChanged();
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorSettingPreviewText.txt b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorSettingPreviewText.txt
new file mode 100644
index 0000000..b434966
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorSettingPreviewText.txt
@@ -0,0 +1,8 @@
+$ mkdir ~/Hello-World
+$ cd ~/Hello-World
+$ git init
+$ touch README
+$ git add README
+$ git commit -m 'first commit'
+$ git remote add origin https://code.google.com/p/elt/
+$ git push -u origin master
\ No newline at end of file
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorsAndFontsPreferencePage.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorsAndFontsPreferencePage.java
new file mode 100644
index 0000000..683c616
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorsAndFontsPreferencePage.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import static org.eclipse.jface.layout.GridDataFactory.fillDefaults;
+import static org.eclipse.jface.preference.ColorSelector.PROP_COLORCHANGE;
+import static org.eclipse.jface.preference.PreferenceConverter.*;
+import static org.eclipse.jface.resource.JFaceResources.TEXT_FONT;
+import static org.eclipse.ui.dialogs.PreferencesUtil.createPreferenceDialogOn;
+
+import static com.google.eclipse.elt.view.Activator.*;
+import static com.google.eclipse.elt.view.preferences.Messages.*;
+import static com.google.eclipse.elt.view.preferences.PreferenceNames.*;
+
+import java.io.InputStream;
+import java.util.Scanner;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.preference.*;
+import org.eclipse.jface.resource.*;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.source.projection.ProjectionViewer;
+import org.eclipse.jface.util.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ColorsAndFontsPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
+ private ProjectionViewer previewer;
+ private ColorSelector foregroundColorSelector;
+ private ColorSelector backgroundColorSelector;
+ private Button btnUseTextFont;
+ private Button btnUseCustomFont;
+ private Label lblFontData;
+ private Button btnChangeFont;
+
+ private FontData fontData;
+
+ private IPropertyChangeListener textFontChangeListener;
+
+ @Override public void init(IWorkbench workbench) {
+ setPreferenceStore(preferenceStore());
+ }
+
+ @Override protected Control createContents(Composite parent) {
+ Composite contents = new Composite(parent, SWT.NONE);
+ contents.setLayout(new GridLayout(1, false));
+
+ Label lblDescription = new Label(contents, SWT.NONE);
+ lblDescription.setText(colorsAndFontsTitle);
+
+ GridDataFactory gridDataFactory = fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).span(1, 1);
+
+ Composite controls = new Composite(contents, SWT.NONE);
+ gridDataFactory.applyTo(controls);
+ controls.setLayout(new GridLayout(1, false));
+
+ Group grpColors = new Group(controls, SWT.NONE);
+ gridDataFactory.applyTo(grpColors);
+ grpColors.setLayout(new GridLayout(2, false));
+
+ Label lblBackground = new Label(grpColors, SWT.NONE);
+ lblBackground.setText(backgroundPrompt);
+
+ backgroundColorSelector = new ColorSelector(grpColors);
+
+ Label lblForeground = new Label(grpColors, SWT.NONE);
+ lblForeground.setText(foregroundPrompt);
+
+ foregroundColorSelector = new ColorSelector(grpColors);
+
+ Group grpFont = new Group(controls, SWT.NONE);
+ gridDataFactory.applyTo(grpFont);
+ grpFont.setLayout(new GridLayout(2, false));
+
+ SelectionListener fontButtonSelectionListener = new SelectionAdapter() {
+ @Override public void widgetSelected(SelectionEvent e) {
+ boolean useTextFont = btnUseTextFont.getSelection();
+ btnChangeFont.setEnabled(!useTextFont);
+ if (useTextFont) {
+ updateFontDataWithTextFont();
+ }
+ }
+ };
+
+ btnUseTextFont = new Button(grpFont, SWT.RADIO);
+ gridDataFactory.span(2, 1).applyTo(btnUseTextFont);
+ btnUseTextFont.setText(useTextFont);
+ btnUseTextFont.addSelectionListener(fontButtonSelectionListener);
+
+ Link changeTextFontLink = new Link(grpFont, SWT.NONE);
+ changeTextFontLink.setText(textFontLink);
+ GridData gridData = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
+ gridData.widthHint = 150; // only expand further if anyone else requires it
+ gridData.horizontalSpan = 2;
+ changeTextFontLink.setLayoutData(gridData);
+ changeTextFontLink.addSelectionListener(new SelectionAdapter() {
+ @Override public void widgetSelected(SelectionEvent e) {
+ if ("org.eclipse.ui.preferencePages.ColorsAndFonts".equals(e.text)) {
+ createPreferenceDialogOn(getShell(), e.text, null, TEXT_FONT);
+ }
+ }
+ });
+
+ btnUseCustomFont = new Button(grpFont, SWT.RADIO);
+ gridDataFactory.applyTo(btnUseCustomFont);
+ btnUseCustomFont.setText(useCustomFont);
+ btnUseCustomFont.addSelectionListener(fontButtonSelectionListener);
+
+ lblFontData = new Label(grpFont, SWT.NONE);
+ gridDataFactory.span(1, 1).applyTo(lblFontData);
+ lblFontData.setText("");
+
+ btnChangeFont = new Button(grpFont, SWT.NONE);
+ btnChangeFont.setText(change);
+ btnChangeFont.addSelectionListener(new SelectionAdapter() {
+ @Override public void widgetSelected(SelectionEvent e) {
+ FontDialog fontDialog = new FontDialog(getShell());
+ fontDialog.setFontList(new FontData[] { fontData });
+ FontData newFontData = fontDialog.open();
+ if (newFontData != null) {
+ updateFontData(newFontData);
+ }
+ }
+ });
+
+ Label lblPreview = new Label(contents, SWT.NONE);
+ lblPreview.setText(previewPrompt);
+
+ previewer = new ProjectionViewer(contents, null, null, false, SWT.V_SCROLL | SWT.H_SCROLL);
+ previewer.setEditable(false);
+ previewer.setDocument(new Document(loadContentsFrom("ColorSettingPreviewText.txt")));
+
+ StyledText previewerText = previewer.getTextWidget();
+ gridDataFactory.align(SWT.FILL, SWT.FILL).grab(true, true);
+ gridDataFactory.applyTo(previewerText);
+ Cursor arrowCursor = previewerText.getDisplay().getSystemCursor(SWT.CURSOR_ARROW);
+ previewerText.setCursor(arrowCursor);
+
+ backgroundColorSelector.addListener(new ColorChangeListener() {
+ @Override void onColorChanged(RGB newValue) {
+ validateInput();
+ if (isValid()) {
+ previewer.getTextWidget().setBackground(newColor(newValue));
+ }
+ }
+ });
+
+ foregroundColorSelector.addListener(new ColorChangeListener() {
+ @Override void onColorChanged(RGB newValue) {
+ validateInput();
+ if (isValid()) {
+ previewer.setTextColor(newColor(newValue));
+ }
+ }
+ });
+
+ textFontChangeListener = new IPropertyChangeListener() {
+ @Override public void propertyChange(PropertyChangeEvent event) {
+ if (TEXT_FONT.equals(event.getProperty()) && btnUseTextFont.getSelection()) {
+ updateFontDataWithTextFont();
+ }
+ }
+ };
+ JFaceResources.getFontRegistry().addListener(textFontChangeListener);
+
+ updateContents();
+ return contents;
+ }
+
+ private String loadContentsFrom(String fileName) {
+ String lineSeparator = System.getProperty("line.separator");
+ StringBuilder buffer = new StringBuilder();
+ Scanner scanner = null;
+ try {
+ InputStream inputStream = getClass().getResourceAsStream(fileName);
+ scanner = new Scanner(inputStream);
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine();
+ buffer.append(line).append(lineSeparator);
+ }
+ } catch (RuntimeException e) {
+ log(unableToLoadPreviewContent, e);
+ } finally {
+ if (scanner != null) {
+ scanner.close();
+ }
+ }
+ return buffer.toString();
+ }
+
+ private void validateInput() {
+ if (backgroundColorSelector.getColorValue().equals(foregroundColorSelector.getColorValue())) {
+ setErrorMessage(backgroundAndForegroundCannotBeTheSame);
+ setValid(false);
+ return;
+ }
+ pageIsValid();
+ }
+
+ private void pageIsValid() {
+ setErrorMessage(null);
+ setValid(true);
+ }
+
+ private void updateFontDataWithTextFont() {
+ updateFontData(JFaceResources.getTextFont().getFontData()[0]);
+ }
+
+ private void updateFontData(FontData newValue) {
+ fontData = newValue;
+ displayFont();
+ }
+
+ private void updateContents() {
+ RGB background = getColor(getPreferenceStore(), BACKGROUND_COLOR);
+ RGB foreground = getColor(getPreferenceStore(), FOREGROUND_COLOR);
+ fontData = getFontData(getPreferenceStore(), CUSTOM_FONT_DATA);
+ boolean useCustomFont = getPreferenceStore().getBoolean(USE_CUSTOM_FONT);
+ updateContents(background, foreground, useCustomFont);
+ }
+
+ @Override public boolean performOk() {
+ setValue(getPreferenceStore(), BACKGROUND_COLOR, backgroundColorSelector.getColorValue());
+ setValue(getPreferenceStore(), FOREGROUND_COLOR, foregroundColorSelector.getColorValue());
+ preferenceStore().setValue(USE_CUSTOM_FONT, btnUseCustomFont.getSelection());
+ setValue(getPreferenceStore(), CUSTOM_FONT_DATA, fontData);
+ return true;
+ }
+
+ @Override protected void performDefaults() {
+ RGB background = getDefaultColor(getPreferenceStore(), BACKGROUND_COLOR);
+ RGB foreground = getDefaultColor(getPreferenceStore(), FOREGROUND_COLOR);
+ fontData = getDefaultFontData(getPreferenceStore(), CUSTOM_FONT_DATA);
+ boolean useCustomFont = getPreferenceStore().getDefaultBoolean(USE_CUSTOM_FONT);
+ updateContents(background, foreground, useCustomFont);
+ }
+
+ private void updateContents(RGB background, RGB foreground, boolean useCustomFont) {
+ backgroundColorSelector.setColorValue(background);
+ foregroundColorSelector.setColorValue(foreground);
+ btnUseTextFont.setSelection(!useCustomFont);
+ btnUseCustomFont.setSelection(useCustomFont);
+ btnChangeFont.setEnabled(useCustomFont);
+ previewer.getTextWidget().setBackground(newColor(background));
+ previewer.setTextColor(newColor(foreground));
+ displayFont();
+ }
+
+ private void displayFont() {
+ lblFontData.setText(StringConverter.asString(fontData));
+ previewer.getTextWidget().setFont(new Font(display(), fontData));
+ }
+
+ private Color newColor(RGB rgb) {
+ return new Color(display(), rgb);
+ }
+
+ private Display display() {
+ return getShell().getDisplay();
+ }
+
+ @Override public void dispose() {
+ if (textFontChangeListener != null) {
+ JFaceResources.getFontRegistry().removeListener(textFontChangeListener);
+ }
+ super.dispose();
+ }
+
+ private static abstract class ColorChangeListener implements IPropertyChangeListener {
+ @Override public final void propertyChange(PropertyChangeEvent event) {
+ if (PROP_COLORCHANGE.equals(event.getProperty())) {
+ RGB rgb = (RGB) event.getNewValue();
+ onColorChanged(rgb);
+ }
+ }
+
+ abstract void onColorChanged(RGB newValue);
+ }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorsAndFontsPreferences.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorsAndFontsPreferences.java
new file mode 100644
index 0000000..2f37661
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/ColorsAndFontsPreferences.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import static org.eclipse.jface.preference.PreferenceConverter.*;
+
+import static com.google.eclipse.elt.view.Activator.preferenceStore;
+import static com.google.eclipse.elt.view.preferences.PreferenceNames.*;
+
+import org.eclipse.swt.graphics.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ColorsAndFontsPreferences {
+ public static RGB background() {
+ return getColor(preferenceStore(), BACKGROUND_COLOR);
+ }
+
+ public static RGB foreground() {
+ return getColor(preferenceStore(), FOREGROUND_COLOR);
+ }
+
+ public static boolean useCustomFont() {
+ return preferenceStore().getBoolean(USE_CUSTOM_FONT);
+ }
+
+ public static FontData customFontData() {
+ return getFontData(preferenceStore(), CUSTOM_FONT_DATA);
+ }
+
+ public static boolean useBlinkingCursor() {
+ return preferenceStore().getBoolean(USE_BLINKING_CURSOR);
+ }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/GeneralPreferences.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/GeneralPreferences.java
new file mode 100644
index 0000000..21673d6
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/GeneralPreferences.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import static com.google.eclipse.elt.view.Activator.preferenceStore;
+import static com.google.eclipse.elt.view.preferences.PreferenceNames.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public final class GeneralPreferences {
+ public static int bufferLineCount() {
+ return preferenceStore().getInt(BUFFER_LINE_COUNT);
+ }
+
+ public static boolean closeViewOnExit() {
+ return preferenceStore().getBoolean(CLOSE_VIEW_ON_EXIT);
+ }
+
+ public static boolean warnOnClose() {
+ return preferenceStore().getBoolean(WARN_ON_CLOSE);
+ }
+
+ public static void warnOnClose(boolean newValue) {
+ preferenceStore().setValue(WARN_ON_CLOSE, newValue);
+ }
+
+ private GeneralPreferences() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/Messages.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/Messages.java
new file mode 100644
index 0000000..c74bd80
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/Messages.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class Messages extends NLS {
+ public static String backgroundAndForegroundCannotBeTheSame;
+ public static String backgroundPrompt;
+ public static String bufferLineCount;
+ public static String change;
+ public static String closeViewOnExit;
+ public static String colorsAndFontsTitle;
+ public static String foregroundPrompt;
+ public static String generalPreferencesTitle;
+ public static String invalidBufferLineCount;
+ public static String previewPrompt;
+ public static String textFontLink;
+ public static String unableToLoadPreviewContent;
+ public static String useBlinkingCursor;
+ public static String useCustomFont;
+ public static String useTextFont;
+ public static String warnOnClose;
+
+ static {
+ Class<Messages> type = Messages.class;
+ NLS.initializeMessages(type.getName(), type);
+ }
+
+ private Messages() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/Messages.properties b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/Messages.properties
new file mode 100644
index 0000000..55d4de9
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/Messages.properties
@@ -0,0 +1,17 @@
+backgroundAndForegroundCannotBeTheSame=Background and foreground colors cannot be the same
+backgroundPrompt=Background:
+bufferLineCount=Terminal buffer lines:
+change=Change...
+closeViewOnExit=Close view when terminal exits
+colorsAndFontsTitle=Terminal colors and fonts preferences.
+foregroundPrompt=Foreground:
+generalPreferencesTitle=General preferences.
+invalidBufferLineCount=Value should be an integer between {0} and {1}
+previewPrompt=Preview:
+textFontLink=Eclipse's "Text Font" can be configured on the <a href=\"org.eclipse.ui.preferencePages.ColorsAndFonts\">'Colors and Fonts'</a> preference page.
+unableToLoadPreviewContent=Unable to load preview content
+useBlinkingCursor=Use blinking cursor
+useCustomFont=Use custom font
+useTextFont=Use Eclipse's "Text Font"
+warnOnClose=Warn on close
+
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/PreferenceInitializer.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/PreferenceInitializer.java
new file mode 100644
index 0000000..c30f4d2
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/PreferenceInitializer.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import static com.google.eclipse.elt.view.Activator.preferenceStore;
+import static com.google.eclipse.elt.view.preferences.PreferenceNames.*;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.PreferenceConverter;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.swt.graphics.RGB;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class PreferenceInitializer extends AbstractPreferenceInitializer {
+ @Override public void initializeDefaultPreferences() {
+ preferenceStore().setDefault(BUFFER_LINE_COUNT, 1000);
+ preferenceStore().setDefault(CLOSE_VIEW_ON_EXIT, true);
+ preferenceStore().setDefault(WARN_ON_CLOSE, true);
+ setDefault(BACKGROUND_COLOR, new RGB(0, 0, 0));
+ setDefault(FOREGROUND_COLOR, new RGB(229, 229, 229));
+ preferenceStore().setDefault(USE_CUSTOM_FONT, false);
+ preferenceStore().setDefault(USE_BLINKING_CURSOR, true);
+ PreferenceConverter.setDefault(preferenceStore(), CUSTOM_FONT_DATA, JFaceResources.getTextFont().getFontData());
+ }
+
+ private void setDefault(String name, RGB value) {
+ PreferenceConverter.setDefault(preferenceStore(), name, value);
+ }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/PreferenceNames.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/PreferenceNames.java
new file mode 100644
index 0000000..e4b8136
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/PreferenceNames.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.preferences;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+final class PreferenceNames {
+ static final String BUFFER_LINE_COUNT = "bufferLineCount";
+ static final String CLOSE_VIEW_ON_EXIT = "exitViewOnExit";
+ static final String WARN_ON_CLOSE = "warnOnClose";
+ static final String BACKGROUND_COLOR = "backgroundColor";
+ static final String FOREGROUND_COLOR = "foregroundColor";
+ static final String USE_CUSTOM_FONT = "useCustomFont";
+ static final String CUSTOM_FONT_DATA = "customFontData";
+ static final String USE_BLINKING_CURSOR = "useBlinkingCursor";
+
+ private PreferenceNames() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/RootPreferencePage.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/RootPreferencePage.java
new file mode 100644
index 0000000..d13489a
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/preferences/RootPreferencePage.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.preferences;
+
+import static com.google.eclipse.elt.view.Activator.preferenceStore;
+import static com.google.eclipse.elt.view.preferences.Messages.*;
+import static com.google.eclipse.elt.view.preferences.PreferenceNames.*;
+
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class RootPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
+ public RootPreferencePage() {
+ }
+ private static final int MINIMUM_BUFFER_LINE_COUNT = 100;
+ private static final int MAXIMUM_BUFFER_LINE_COUNT = 50000;
+ private static final String INVALID_BUFFER_LINE_COUNT_MESSAGE =
+ NLS.bind(invalidBufferLineCount, MINIMUM_BUFFER_LINE_COUNT, MAXIMUM_BUFFER_LINE_COUNT);
+
+ private Text txtBufferLineCount;
+ private Button btnCloseViewOnExit;
+ private Button btnWarnOnClose;
+ private Button btnUseBlinkingCursor;
+
+ private int newBufferLineCount;
+
+ @Override public void init(IWorkbench workbench) {
+ setPreferenceStore(preferenceStore());
+ }
+
+ @Override protected Control createContents(Composite parent) {
+ Composite contents = new Composite(parent, SWT.NONE);
+ contents.setLayout(new GridLayout(2, false));
+
+ Label lblGeneralPreferences = new Label(contents, SWT.NONE);
+ lblGeneralPreferences.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1));
+ lblGeneralPreferences.setText(generalPreferencesTitle);
+
+ Label filler = new Label(contents, SWT.NONE);
+ filler.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+
+ Label lblBufferLineCount = new Label(contents, SWT.NONE);
+ lblBufferLineCount.setText(bufferLineCount);
+
+ txtBufferLineCount = new Text(contents, SWT.BORDER);
+ txtBufferLineCount.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ txtBufferLineCount.addModifyListener(new ModifyListener() {
+ @Override public void modifyText(ModifyEvent event) {
+ try {
+ newBufferLineCount = Integer.parseInt(txtBufferLineCount.getText());
+ } catch (NumberFormatException e) {
+ setInvalid(INVALID_BUFFER_LINE_COUNT_MESSAGE);
+ return;
+ }
+ if (newBufferLineCount < MINIMUM_BUFFER_LINE_COUNT || newBufferLineCount > MAXIMUM_BUFFER_LINE_COUNT) {
+ setInvalid(INVALID_BUFFER_LINE_COUNT_MESSAGE);
+ return;
+ }
+ setErrorMessage(null);
+ setValid(true);
+ }
+ });
+
+ btnCloseViewOnExit = new Button(contents, SWT.CHECK);
+ btnCloseViewOnExit.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+ btnCloseViewOnExit.setText(closeViewOnExit);
+
+ btnWarnOnClose = new Button(contents, SWT.CHECK);
+ btnWarnOnClose.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+ btnWarnOnClose.setText(warnOnClose);
+
+ btnUseBlinkingCursor = new Button(contents, SWT.CHECK);
+ btnUseBlinkingCursor.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+ btnUseBlinkingCursor.setText(useBlinkingCursor);
+
+ updateContents();
+ return contents;
+ }
+
+ private void setInvalid(String errorMessage) {
+ setErrorMessage(errorMessage);
+ setValid(false);
+ }
+
+ private void updateContents() {
+ txtBufferLineCount.setText(getPreferenceStore().getString(BUFFER_LINE_COUNT));
+ btnCloseViewOnExit.setSelection(getPreferenceStore().getBoolean(CLOSE_VIEW_ON_EXIT));
+ btnWarnOnClose.setSelection(getPreferenceStore().getBoolean(WARN_ON_CLOSE));
+ btnUseBlinkingCursor.setSelection(getPreferenceStore().getBoolean(USE_BLINKING_CURSOR));
+ }
+
+ @Override public boolean performOk() {
+ getPreferenceStore().setValue(BUFFER_LINE_COUNT, newBufferLineCount);
+ getPreferenceStore().setValue(CLOSE_VIEW_ON_EXIT, btnCloseViewOnExit.getSelection());
+ getPreferenceStore().setValue(WARN_ON_CLOSE, btnWarnOnClose.getSelection());
+ getPreferenceStore().setValue(USE_BLINKING_CURSOR, btnUseBlinkingCursor.getSelection());
+ return true;
+ }
+
+ @Override protected void performDefaults() {
+ txtBufferLineCount.setText(getPreferenceStore().getDefaultString(BUFFER_LINE_COUNT));
+ btnCloseViewOnExit.setSelection(getPreferenceStore().getDefaultBoolean(CLOSE_VIEW_ON_EXIT));
+ btnWarnOnClose.setSelection(getPreferenceStore().getDefaultBoolean(WARN_ON_CLOSE));
+ btnUseBlinkingCursor.setSelection(getPreferenceStore().getDefaultBoolean(USE_BLINKING_CURSOR));
+ }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/Messages.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/Messages.java
new file mode 100644
index 0000000..b841a0d
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/Messages.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.ui;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class Messages extends NLS {
+ public static String alwaysCloseWithoutWarn;
+ public static String changeTerminalTitle;
+ public static String closeTerminalQuestion;
+ public static String confirmCloseDialogTitle;
+ public static String defaultViewTitle;
+ public static String enterTerminalTitleDialogTitle;
+ public static String enterTerminalTitlePrompt;
+ public static String newLocalTerminal;
+ public static String scrollLock;
+
+ static {
+ Class<Messages> type = Messages.class;
+ NLS.initializeMessages(type.getName(), type);
+ }
+
+ private Messages() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/Messages.properties b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/Messages.properties
new file mode 100644
index 0000000..a2ebc48
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/Messages.properties
@@ -0,0 +1,9 @@
+alwaysCloseWithoutWarn=Always close without warn
+changeTerminalTitle=Change Title
+closeTerminalQuestion=Close terminal?
+confirmCloseDialogTitle=Confirm Close
+defaultViewTitle=Terminal
+enterTerminalTitleDialogTitle=Terminal Title
+enterTerminalTitlePrompt=Enter the new title for the terminal:
+newLocalTerminal=New Terminal
+scrollLock=Scroll Lock
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/PopupMenu.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/PopupMenu.java
new file mode 100644
index 0000000..1950397
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/PopupMenu.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.ui;
+
+import static org.eclipse.ui.actions.ActionFactory.*;
+
+import org.eclipse.jface.action.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.*;
+
+import com.google.eclipse.elt.emulator.actions.*;
+import com.google.eclipse.elt.emulator.core.VT100TerminalControl;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class PopupMenu {
+ private final AbstractTerminalAction copy;
+ private final AbstractTerminalAction paste;
+ private final AbstractTerminalAction selectAll;
+ private final AbstractTerminalAction clearAll;
+
+ @SuppressWarnings("deprecation")
+ PopupMenu(IViewSite viewSite, VT100TerminalControl terminalControl) {
+ PopupMenuManager menuManager = new PopupMenuManager();
+ copy = menuManager.add(new TerminalActionCopy(terminalControl));
+ copy.setActionDefinitionId("com.google.eclipse.terminal.local.copy");
+ paste = menuManager.add(new TerminalActionPaste(terminalControl));
+ paste.setActionDefinitionId("com.google.eclipse.terminal.local.paste");
+ menuManager.add(new Separator());
+ selectAll = menuManager.add(new TerminalActionSelectAll(terminalControl));
+ clearAll = menuManager.add(new TerminalActionClearAll(terminalControl));
+ IActionBars actionBars = viewSite.getActionBars();
+ actionBars.setGlobalActionHandler(COPY.getId(), copy);
+ actionBars.setGlobalActionHandler(PASTE.getId(), paste);
+ actionBars.setGlobalActionHandler(SELECT_ALL.getId(), selectAll);
+ IKeyBindingService keyBindingService = viewSite.getKeyBindingService();
+ keyBindingService.registerAction(copy);
+ keyBindingService.registerAction(paste);
+ menuManager.addMenuListener(new IMenuListener() {
+ @Override public void menuAboutToShow(IMenuManager manager) {
+ update();
+ }
+ });
+ Control control = terminalControl.getControl();
+ Menu menu = menuManager.createContextMenu(control);
+ control.setMenu(menu);
+ menu.addMenuListener(new MenuAdapter() {
+ @Override public void menuHidden(MenuEvent e) {
+ copy.updateAction(false);
+ }
+ });
+ }
+
+ void update() {
+ update(copy, paste, selectAll, clearAll);
+ }
+
+ private void update(AbstractTerminalAction...actions) {
+ for (AbstractTerminalAction action : actions) {
+ action.updateAction(true);
+ }
+ }
+
+ private static class PopupMenuManager extends MenuManager {
+ PopupMenuManager() {
+ super("#PopupMenu");
+ }
+
+ AbstractTerminalAction add(AbstractTerminalAction action) {
+ super.add(action);
+ return action;
+ }
+ }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/TerminalView.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/TerminalView.java
new file mode 100644
index 0000000..70c6714
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/TerminalView.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.ui;
+
+import static org.eclipse.core.runtime.Path.fromOSString;
+import static org.eclipse.core.runtime.Status.OK_STATUS;
+import static org.eclipse.jface.resource.JFaceResources.TEXT_FONT;
+import static org.eclipse.jface.window.Window.OK;
+import static org.eclipse.ui.IWorkbenchPage.VIEW_ACTIVATE;
+
+import static com.google.eclipse.elt.view.Activator.*;
+import static com.google.eclipse.elt.view.ImageKeys.*;
+import static com.google.eclipse.elt.view.preferences.ColorsAndFontsPreferences.*;
+import static com.google.eclipse.elt.view.preferences.GeneralPreferences.*;
+import static com.google.eclipse.elt.view.ui.Messages.*;
+import static com.google.eclipse.elt.view.util.Platform.userHomeDirectory;
+
+import java.util.UUID;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.dialogs.*;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.*;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.*;
+import org.eclipse.ui.contexts.*;
+import org.eclipse.ui.part.ViewPart;
+import org.eclipse.ui.progress.UIJob;
+
+import com.google.eclipse.elt.emulator.control.ITerminalListener;
+import com.google.eclipse.elt.emulator.provisional.api.TerminalState;
+import com.google.eclipse.elt.view.connector.LifeCycleListener;
+import com.google.eclipse.elt.view.preferences.AbstractPreferencesChangeListener;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class TerminalView extends ViewPart implements ISaveablePart2 {
+ private static final String SCROLL_LOCK_ENABLED = "scrollLock";
+ private static final String TITLE_STATE_TYPE = "title";
+ private static final String WORKING_DIRECTORY_STATE_TYPE = "workingDirectory";
+
+ private static final String VIEW_ID = "com.google.eclipse.terminal.local.localTerminalView";
+
+ private IPropertyChangeListener preferencesChangeListener;
+ private IPropertyChangeListener textFontChangeListener;
+ private IMemento savedState;
+ private TerminalWidget terminalWidget;
+ private IPath workingDirectory;
+
+ private Action newTerminalAction;
+ private Action scrollLockAction;
+
+ private boolean checkCanBeClosed;
+ private boolean forceClose;
+
+ private IContextActivation contextActivation;
+
+ public static void openTerminalView(IPath workingDirectory) {
+ openTerminalView(null, workingDirectory);
+ }
+
+ private static void openTerminalView(String id, IPath workingDirectory) {
+ IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ IPath safeWorkingDirectory = (workingDirectory != null) ? workingDirectory : userHomeDirectory();
+ try {
+ String directoryName = safeWorkingDirectory.lastSegment();
+ String secondaryId = (id != null) ? id : directoryName;
+ TerminalView view = (TerminalView) page.showView(VIEW_ID, secondaryId, VIEW_ACTIVATE);
+ view.setPartName(directoryName);
+ view.open(safeWorkingDirectory);
+ } catch (PartInitException e) {
+ log("Unable to create Terminal View", e);
+ }
+ }
+
+ @Override public void init(IViewSite site, IMemento memento) throws PartInitException {
+ super.init(site, memento);
+ savedState = memento;
+ }
+
+ @Override public void saveState(IMemento memento) {
+ saveState(memento, SCROLL_LOCK_ENABLED, String.valueOf(terminalWidget.isScrollLockEnabled()));
+ saveState(memento, TITLE_STATE_TYPE, getPartName());
+ saveState(memento, WORKING_DIRECTORY_STATE_TYPE, workingDirectory.toOSString());
+ }
+
+ private void saveState(IMemento memento, String type, String data) {
+ IMemento child = memento.createChild(type);
+ child.putTextData(data);
+ }
+
+ @Override public void createPartControl(Composite parent) {
+ terminalWidget = new TerminalWidget(parent, getViewSite());
+ terminalWidget.setLifeCycleListener(new LifeCycleListener() {
+ @Override public void executionFinished() {
+ closeViewOnExitIfPossible();
+ }
+ });
+ terminalWidget.setTerminalListener(new ITerminalListener() {
+ @Override public void setTerminalTitle(final String title) {
+ updatePartName(title);
+ }
+
+ @Override public void setState(TerminalState state) {}
+ });
+ IViewSite viewSite = getViewSite();
+ preferencesChangeListener = new AbstractPreferencesChangeListener() {
+ @Override protected void onBufferLineCountChanged() {
+ updateBufferLineCount();
+ }
+
+ @Override protected void onColorChanged() {
+ updateColors();
+ }
+
+ @Override protected void onFontChanged() {
+ updateFont();
+ }
+
+ @Override protected void onUseBlinkingCursorChanged() {
+ updateUsageOfBlinkingCursor();
+ }
+ };
+ preferenceStore().addPropertyChangeListener(preferencesChangeListener);
+ updateBufferLineCount();
+ updateColors();
+ updateUsageOfBlinkingCursor();
+ textFontChangeListener = new IPropertyChangeListener() {
+ @Override public void propertyChange(PropertyChangeEvent event) {
+ if (TEXT_FONT.equals(event.getProperty())) {
+ if (!useCustomFont()) {
+ setFont(JFaceResources.getTextFont());
+ }
+ }
+ }
+ };
+ JFaceResources.getFontRegistry().addListener(textFontChangeListener);
+ updateFont();
+ setupToolBarActions();
+ IContextService contextService = contextService();
+ if (contextService != null) {
+ contextActivation = contextService.activateContext("com.google.eclipse.terminal.local.context.localTerminal");
+ }
+ if (savedState != null) {
+ updateScrollLockUsingSavedState();
+ connectUsingSavedState();
+ return;
+ }
+ if (viewSite.getSecondaryId() == null) {
+ setPartName(defaultViewTitle);
+ open(userHomeDirectory());
+ }
+ enableScrollLock(scrollLockAction.isChecked());
+ }
+
+ private void closeViewOnExitIfPossible() {
+ if (closeViewOnExit() && terminalWidget != null && !terminalWidget.isDisposed()) {
+ // must run in UI thread.
+ forceClose = true;
+ terminalWidget.getDisplay().asyncExec(new Runnable() {
+ @Override public void run() {
+ IWorkbenchPartSite site = getSite();
+ site.getPage().hideView((IViewPart) site.getPart());
+ }
+ });
+ }
+ }
+
+ private void updateColors() {
+ terminalWidget.setColors(background(), foreground());
+ }
+
+ private void updateFont() {
+ setFont(terminalFont());
+ }
+
+ private void updateUsageOfBlinkingCursor() {
+ terminalWidget.setBlinkingCursor(useBlinkingCursor());
+ }
+
+ private Font terminalFont() {
+ if (useCustomFont()) {
+ return new Font(Display.getDefault(), customFontData());
+ }
+ return JFaceResources.getTextFont();
+ }
+
+ private void setFont(Font font) {
+ terminalWidget.setFont(font);
+ }
+
+ private void updateBufferLineCount() {
+ terminalWidget.setBufferLineCount(bufferLineCount());
+ }
+
+ private void setupToolBarActions() {
+ IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
+ toolBarManager.add(new ChangeViewNameAction());
+ toolBarManager.add(new Separator());
+ newTerminalAction = new NewTerminalAction();
+ toolBarManager.add(newTerminalAction);
+ scrollLockAction = new ScrollLockAction();
+ toolBarManager.add(scrollLockAction);
+ }
+
+ private void updateScrollLockUsingSavedState() {
+ boolean newValue = Boolean.valueOf(savedState(SCROLL_LOCK_ENABLED));
+ enableScrollLockAndUpdateAction(newValue);
+ }
+
+ private void enableScrollLockAndUpdateAction(boolean enabled) {
+ enableScrollLock(enabled);
+ scrollLockAction.setChecked(enabled);
+ }
+
+ private void enableScrollLock(boolean enabled) {
+ terminalWidget.enableScrollLock(enabled);
+ }
+
+ private void connectUsingSavedState() {
+ String title = savedState(TITLE_STATE_TYPE);
+ setPartName(title);
+ String savedWorkingDirectory = savedState(WORKING_DIRECTORY_STATE_TYPE);
+ if (savedWorkingDirectory != null) {
+ open(fromOSString(savedWorkingDirectory));
+ }
+ }
+
+ private String savedState(String type) {
+ IMemento child = savedState.getChild(type);
+ return (child != null) ? child.getTextData() : null;
+ }
+
+ private void open(IPath workingDirectory) {
+ if (terminalWidget.isConnected()) {
+ return;
+ }
+ this.workingDirectory = workingDirectory;
+ terminalWidget.setWorkingDirectory(workingDirectory);
+ terminalWidget.connect();
+ }
+
+ private void updatePartName(final String value) {
+ UIJob job = new UIJob("Update terminal view title") {
+ @Override public IStatus runInUIThread(IProgressMonitor monitor) {
+ setPartName(value);
+ return OK_STATUS;
+ }
+ };
+ job.schedule();
+ }
+
+ @Override public void setFocus() {
+ terminalWidget.setFocus();
+ }
+
+ @Override public void dispose() {
+ if (contextActivation != null) {
+ IContextService contextService = contextService();
+ if (contextService != null) {
+ contextService.deactivateContext(contextActivation);
+ }
+ }
+ if (preferencesChangeListener != null) {
+ preferenceStore().removePropertyChangeListener(preferencesChangeListener);
+ }
+ if (textFontChangeListener != null) {
+ JFaceResources.getFontRegistry().removeListener(textFontChangeListener);
+ }
+ super.dispose();
+ }
+
+ private IContextService contextService() {
+ return (IContextService) getSite().getService(IContextService.class);
+ }
+
+ @Override public boolean isDirty() {
+ if (checkCanBeClosed) {
+ checkCanBeClosed = false;
+ return true;
+ }
+ return false;
+ }
+
+ @Override public boolean isSaveOnCloseNeeded() {
+ if (forceClose) {
+ return false;
+ }
+ checkCanBeClosed = true;
+ return true;
+ }
+
+ @Override public int promptToSaveOnClose() {
+ if (warnOnClose()) {
+ boolean close = WarnOnCloseDialog.open(terminalWidget.getShell());
+ if (!close) {
+ return CANCEL;
+ }
+ }
+ return NO;
+ }
+
+ @Override public void doSave(IProgressMonitor monitor) {}
+
+ @Override public void doSaveAs() {}
+
+ @Override public boolean isSaveAsAllowed() {
+ return false;
+ }
+
+ private class NewTerminalAction extends Action {
+ NewTerminalAction() {
+ setImageDescriptor(imageDescriptor(NEW_TERMINAL));
+ setText(newLocalTerminal);
+ }
+
+ @Override public void run() {
+ openTerminalView(UUID.randomUUID().toString(), workingDirectory);
+ }
+ }
+
+ private class ScrollLockAction extends Action {
+ ScrollLockAction() {
+ super(scrollLock, AS_RADIO_BUTTON);
+ setChecked(false);
+ setImageDescriptor(imageDescriptor(SCROLL_LOCK));
+ }
+
+ @Override public void run() {
+ boolean newValue = !terminalWidget.isScrollLockEnabled();
+ enableScrollLockAndUpdateAction(newValue);
+ }
+ }
+
+ private class ChangeViewNameAction extends Action {
+ ChangeViewNameAction() {
+ setImageDescriptor(imageDescriptor(CHANGE_TITLE));
+ setText(changeTerminalTitle);
+ }
+
+ @Override public void run() {
+ Shell shell = getViewSite().getShell();
+ final String currentTitle = getPartName();
+ InputDialog input = new InputDialog(shell, enterTerminalTitleDialogTitle, enterTerminalTitlePrompt, currentTitle,
+ new IInputValidator() {
+ @Override public String isValid(String newText) {
+ if (newText == null || newText.isEmpty() || currentTitle.equals(newText)) {
+ return "";
+ }
+ return null;
+ }
+ });
+ input.setBlockOnOpen(true);
+ if (input.open() == OK) {
+ setPartName(input.getValue());
+ }
+ }
+ }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/TerminalWidget.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/TerminalWidget.java
new file mode 100644
index 0000000..14729d0
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/TerminalWidget.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.ui;
+
+import static com.google.eclipse.elt.emulator.provisional.api.TerminalState.CONNECTING;
+import static com.google.eclipse.elt.view.connector.LocalTerminalConnector.createLocalTerminalConnector;
+import static com.google.eclipse.elt.view.util.Encodings.DEFAULT_ENCODING;
+
+import java.io.UnsupportedEncodingException;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.layout.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.IViewSite;
+
+import com.google.eclipse.elt.emulator.control.ITerminalListener;
+import com.google.eclipse.elt.emulator.core.VT100TerminalControl;
+import com.google.eclipse.elt.emulator.provisional.api.*;
+import com.google.eclipse.elt.view.connector.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class TerminalWidget extends Composite {
+ private final TerminalListener terminalListener = new TerminalListener();
+
+ private final VT100TerminalControl terminalControl;
+
+ private LifeCycleListener lifeCycleListener;
+ private final String encoding = DEFAULT_ENCODING;
+
+ TerminalWidget(Composite parent, IViewSite viewSite) {
+ super(parent, SWT.NONE);
+ GridLayoutFactory.fillDefaults().spacing(0, 0).applyTo(this);
+ ITerminalConnector terminalConnector = createLocalTerminalConnector(encoding);
+ terminalControl = new VT100TerminalControl(terminalListener, this, new ITerminalConnector[] { terminalConnector });
+ terminalControl.setConnector(terminalConnector);
+ try {
+ terminalControl.setEncoding(encoding);
+ } catch (UnsupportedEncodingException e) {
+ // TODO(alruiz): do something meaningful with the exception.
+ e.printStackTrace();
+ }
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(terminalControl.getRootControl());
+ terminalControl.setInvertedColors(true);
+ addDisposeListener(new DisposeListener() {
+ @Override public void widgetDisposed(DisposeEvent e) {
+ disposeTerminalControl();
+ }
+ });
+ final PopupMenu popupMenu = new PopupMenu(viewSite, terminalControl);
+ terminalTextControl().addFocusListener(new FocusAdapter() {
+ @Override public void focusGained(FocusEvent e) {
+ popupMenu.update();
+ }
+ });
+ }
+
+ private Control terminalTextControl() {
+ return terminalControl.getControl();
+ }
+
+ void connect() {
+ if (terminalControl.getState() == CONNECTING || terminalControl.isDisposed()) {
+ return;
+ }
+ terminalControl.connectTerminal();
+ attachLifeCycleListener();
+ }
+
+ private void attachLifeCycleListener() {
+ LocalTerminalConnector connector = localTerminalConnector();
+ if (connector != null && lifeCycleListener != null) {
+ connector.addLifeCycleListener(lifeCycleListener);
+ }
+ }
+
+ void disposeTerminalControl() {
+ if (!terminalControl.isDisposed()) {
+ terminalControl.disposeTerminal();
+ }
+ }
+
+ boolean isConnected() {
+ return terminalControl.isConnected();
+ }
+
+ void setLifeCycleListener(LifeCycleListener listener) {
+ lifeCycleListener = listener;
+ }
+
+ void setTerminalListener(ITerminalListener listener) {
+ terminalListener.delegate = listener;
+ }
+
+ void setWorkingDirectory(IPath workingDirectory) {
+ LocalTerminalConnector connector = localTerminalConnector();
+ if (connector != null) {
+ connector.setWorkingDirectory(workingDirectory);
+ }
+ }
+
+ private LocalTerminalConnector localTerminalConnector() {
+ Object connector = terminalControl.getTerminalConnector().getAdapter(LocalTerminalConnector.class);
+ return (LocalTerminalConnector) connector;
+ }
+
+ void setColors(RGB background, RGB foreground) {
+ terminalControl.setColors(background, foreground);
+ }
+
+ @Override public void setFont(Font font) {
+ terminalControl.setFont(font);
+ }
+
+ void setBufferLineCount(int lineCount) {
+ terminalControl.setBufferLineLimit(lineCount);
+ }
+
+ @Override public boolean setFocus() {
+ return terminalControl.setFocus();
+ }
+
+ boolean isScrollLockEnabled() {
+ return terminalControl.isScrollLockOn();
+ }
+
+ void enableScrollLock(boolean enabled) {
+ terminalControl.setScrollLockOn(enabled);
+ }
+
+ void setBlinkingCursor(boolean useBlinkingCursor) {
+ terminalControl.setBlinkingCursor(useBlinkingCursor);
+ }
+
+ private static class TerminalListener implements ITerminalListener {
+ ITerminalListener delegate;
+
+ @Override public void setState(TerminalState state) {
+ if (delegate != null) {
+ delegate.setState(state);
+ }
+ }
+
+ @Override public void setTerminalTitle(String title) {
+ if (delegate != null) {
+ delegate.setTerminalTitle(title);
+ }
+ }
+ }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/WarnOnCloseDialog.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/WarnOnCloseDialog.java
new file mode 100644
index 0000000..918d6d2
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/ui/WarnOnCloseDialog.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.ui;
+
+import static org.eclipse.jface.dialogs.IDialogConstants.*;
+
+import static com.google.eclipse.elt.view.preferences.GeneralPreferences.warnOnClose;
+import static com.google.eclipse.elt.view.ui.Messages.*;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class WarnOnCloseDialog extends MessageDialog {
+ private static final String[] BUTTON_LABELS = { YES_LABEL, NO_LABEL };
+
+ private Button btnCloseWithoutWarn;
+
+ static boolean open(Shell parent) {
+ WarnOnCloseDialog dialog = new WarnOnCloseDialog(parent);
+ return dialog.open() == OK;
+ }
+
+ private WarnOnCloseDialog(Shell parentShell) {
+ super(parentShell, confirmCloseDialogTitle, null, closeTerminalQuestion, QUESTION, BUTTON_LABELS, 0);
+ }
+
+ @Override protected Control createCustomArea(Composite parent) {
+ btnCloseWithoutWarn = new Button(parent, SWT.CHECK);
+ btnCloseWithoutWarn.setText(alwaysCloseWithoutWarn);
+ btnCloseWithoutWarn.setSelection(!warnOnClose());
+ return btnCloseWithoutWarn;
+ }
+
+ @Override protected void buttonPressed(int buttonId) {
+ boolean closeWithoutWarn = btnCloseWithoutWarn.getSelection();
+ warnOnClose(!closeWithoutWarn);
+ super.buttonPressed(buttonId);
+ }
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/util/Encodings.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/util/Encodings.java
new file mode 100644
index 0000000..6b6082f
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/util/Encodings.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.util;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public final class Encodings {
+ public static final String DEFAULT_ENCODING = "UTF-8";
+
+ private Encodings() {}
+}
diff --git a/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/util/Platform.java b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/util/Platform.java
new file mode 100644
index 0000000..2f50a5f
--- /dev/null
+++ b/com.google.eclipse.elt.view/src/com/google/eclipse/elt/view/util/Platform.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2012 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.elt.view.util;
+
+import static java.util.Collections.singletonMap;
+
+import static org.eclipse.core.runtime.Platform.*;
+
+import java.io.File;
+import java.util.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.debug.core.*;
+
+/**
+ * Utility methods related to the underlying platform.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public final class Platform {
+ // TODO investigate if "TERM" should be "xterm-color"
+ private static final Map<String, String> TERM_ANSI = singletonMap("TERM", "ansi");
+ private static final String ENVIRONMENT_VARIABLE_FORMAT = "%s=%s";
+
+ /**
+ * Returns the system's default shell.
+ * @return the system's default shell.
+ */
+ public static File defaultShell() {
+ String shell = System.getenv("SHELL");
+ if (shell == null) {
+ shell = (runningOnWindows()) ? "C:\\Windows\\System32\\cmd.exe" : "/bin/sh";
+ }
+ return new File(shell);
+ }
+
+ private static boolean runningOnWindows() {
+ return OS_WIN32.equals(getOS());
+ }
+
+ /**
+ * Returns an array of environment variables. Each entry is of the form "<code>name=value</code>".
+ * @return an array of environment variables.
+ */
+ public static String[] environment() {
+ Map<String, String> environment = new HashMap<String, String>();
+ environment.putAll(TERM_ANSI);
+ environment.putAll(nativeEnvironment());
+ List<String> variables = new ArrayList<String>(environment.size());
+ for (Map.Entry<String, String> entry : environment.entrySet()) {
+ String variable = String.format(ENVIRONMENT_VARIABLE_FORMAT, entry.getKey(), entry.getValue());
+ variables.add(variable);
+ }
+ return variables.toArray(new String[variables.size()]);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Map<String, String> nativeEnvironment() {
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ return launchManager.getNativeEnvironmentCasePreserved();
+ }
+
+ /**
+ * Returns the path of the user home directory.
+ * @return the path of the user home directory or {@code null} if it cannot be found.
+ */
+ public static IPath userHomeDirectory() {
+ String path = System.getProperty("user.home", "/");
+ File userHome = new File(path);
+ return (userHome.isDirectory()) ? new Path(path) : null;
+ }
+
+ private Platform() {}
+}
diff --git a/update-site/artifacts.jar b/update-site/artifacts.jar
deleted file mode 100644
index ffbcb76..0000000
--- a/update-site/artifacts.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/content.jar b/update-site/content.jar
deleted file mode 100644
index 5ab9309..0000000
--- a/update-site/content.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/features/com.google.eclipse.terminal.local_1.1.0.201208061420.jar b/update-site/features/com.google.eclipse.terminal.local_1.1.0.201208061420.jar
deleted file mode 100644
index 366e1f4..0000000
--- a/update-site/features/com.google.eclipse.terminal.local_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.cdt.core.linux.x86_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.cdt.core.linux.x86_1.1.0.201208061420.jar
deleted file mode 100644
index a1fc56d..0000000
--- a/update-site/plugins/com.google.eclipse.cdt.core.linux.x86_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.cdt.core.linux.x86_64_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.cdt.core.linux.x86_64_1.1.0.201208061420.jar
deleted file mode 100644
index 70f2687..0000000
--- a/update-site/plugins/com.google.eclipse.cdt.core.linux.x86_64_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.cdt.core.linux_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.cdt.core.linux_1.1.0.201208061420.jar
deleted file mode 100644
index 24fca0d..0000000
--- a/update-site/plugins/com.google.eclipse.cdt.core.linux_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.cdt.core.macosx_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.cdt.core.macosx_1.1.0.201208061420.jar
deleted file mode 100644
index eee97a8..0000000
--- a/update-site/plugins/com.google.eclipse.cdt.core.macosx_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.cdt.core_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.cdt.core_1.1.0.201208061420.jar
deleted file mode 100644
index 18a9256..0000000
--- a/update-site/plugins/com.google.eclipse.cdt.core_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.terminal.local_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.terminal.local_1.1.0.201208061420.jar
deleted file mode 100644
index 4920500..0000000
--- a/update-site/plugins/com.google.eclipse.terminal.local_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ
diff --git a/update-site/plugins/com.google.eclipse.tm.terminal_1.1.0.201208061420.jar b/update-site/plugins/com.google.eclipse.tm.terminal_1.1.0.201208061420.jar
deleted file mode 100644
index f62fafa..0000000
--- a/update-site/plugins/com.google.eclipse.tm.terminal_1.1.0.201208061420.jar
+++ /dev/null
Binary files differ