GLES2Dbg: use mesa glsl_compiler for syntax checking

Also switch to TabFolder for GUI

Change-Id: If7f5b7d8d826ada17579ca4d031f46d1238bba27
Signed-off-by: David Li <davidxli@google.com>
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/BreakpointOption.java b/tools/glesv2debugger/src/com/android/glesv2debugger/BreakpointOption.java
index 46fa382..0974526 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/BreakpointOption.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/BreakpointOption.java
@@ -77,7 +77,7 @@
         Message.Builder builder = Message.newBuilder();
         builder.setContextId(0); // FIXME: proper context id
         builder.setType(Type.Response);
-        builder.setExpectResponse(true);
+        builder.setExpectResponse(false);
         builder.setFunction(Function.SETPROP);
         builder.setProp(Prop.ExpectResponse);
         builder.setArg0(function.getNumber());
@@ -99,6 +99,10 @@
     private Function lastFunction = Function.NEG;
 
     public boolean ProcessMessage(final MessageQueue queue, final Message msg) throws IOException {
+        // use DefaultProcessMessage just to register the GL call
+        // but do not send response
+        if (msg.getType() == Type.BeforeCall || msg.getType() == Type.AfterCall)
+            queue.DefaultProcessMessage(msg, true, false);
         final Message.Builder builder = Message.newBuilder();
         builder.setContextId(msg.getContextId());
         builder.setType(Type.Response);
@@ -118,7 +122,7 @@
                 }
                 if (msg.getType() == Type.AfterCall)
                 {
-                    call = "s";
+                    call = "skip " + call;
                     builder.setFunction(Function.SKIP);
                 }
                 else if (msg.getType() == Type.BeforeCall)
@@ -130,6 +134,8 @@
                 {
                     assert msg.getType() == Type.AfterGeneratedCall;
                     assert msg.getFunction() == lastFunction;
+                    call = "skip" + call;
+                    builder.setFunction(Function.SKIP);
                 }
                 InputDialog inputDialog = new InputDialog(shell,
                             msg.getFunction().toString() + " " + msg.getType().toString(),
@@ -139,7 +145,12 @@
                 {
                     String s = inputDialog.getValue().substring(0, 1).toLowerCase();
                     if (s.startsWith("s"))
+                    {
                         builder.setFunction(Function.SKIP);
+                        // AfterCall is skipped, so push BeforeCall to complete
+                        if (msg.getType() == Type.BeforeCall)
+                            queue.CompletePartialMessage(msg.getContextId());
+                    }
                     else if (s.startsWith("c"))
                         builder.setFunction(Function.CONTINUE);
                     else if (s.startsWith("r"))
@@ -147,6 +158,7 @@
                         Button btn = buttonsBreak.get(msg.getFunction());
                         btn.setSelection(false);
                         SetBreakpoint(msg.getFunction(), false);
+                        builder.setExpectResponse(false);
                     }
                     else
                     {
@@ -154,9 +166,7 @@
                         lastFunction = builder.getFunction();
                     }
                 }
-                else
-                    assert false; // TODO: cancel behaviour
-                // TODO: add/modify/remove completed messages in queue
+                // else defaults to continue BeforeCall and skip AfterCall
             }
         });
         queue.SendMessage(builder.build());
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java
index 02cf976..fecc24a 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java
@@ -150,11 +150,20 @@
         return partials.get(contextId);
     }
 
+    // used to add BeforeCall to complete if it was skipped
+    void CompletePartialMessage(final int contextId) {
+        final Message msg = partials.remove(contextId);
+        assert msg != null;
+        assert msg.getType() == Type.BeforeCall;
+        synchronized (complete) {
+            complete.add(msg);
+        }
+    }
+
     // can be used by other message processor as default processor
     void DefaultProcessMessage(final Message msg, boolean expectResponse,
             boolean sendResponse)
             throws IOException {
-        assert !msg.getExpectResponse();
         final int contextId = msg.getContextId();
         final Message.Builder builder = Message.newBuilder();
         builder.setContextId(contextId);
@@ -230,6 +239,7 @@
 
     private void SendMessage(final DataOutputStream dos, final Message message)
             throws IOException {
+        assert message.getFunction() != Function.NEG;
         final byte[] data = message.toByteArray();
         dos.writeInt(data.length);
         dos.write(data);
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java b/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java
index adce634..eb1a6ee 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java
@@ -48,15 +48,15 @@
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.FillLayout;
 import org.eclipse.swt.widgets.Canvas;
 import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Menu;
 import org.eclipse.swt.widgets.ScrollBar;
 import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
 import org.eclipse.swt.widgets.Text;
 import org.eclipse.ui.IActionBars;
 import org.eclipse.ui.ISharedImages;
@@ -100,11 +100,12 @@
      */
     public static final String ID = "glesv2debuggerclient.views.SampleView";
 
-    LayoutComposite layoutComposite;
+    TabFolder tabFolder;
+    TabItem tabItemText, tabItemImage, tabItemBreakpointOption, tabItemShaderEditor;
     ListViewer viewer;
     BreakpointOption breakpointOption;
     ShaderEditor shaderEditor;
-    org.eclipse.swt.widgets.Canvas canvas;
+    Canvas canvas;
     Text text;
     Action actionConnect; // connect / disconnect
     Action doubleClickAction;
@@ -241,19 +242,33 @@
         PlatformUI.getWorkbench().getHelpSystem()
                 .setHelp(viewer.getControl(), "GLESv2DebuggerClient.viewer");
 
-        layoutComposite = new LayoutComposite(parent, 0);
-        layoutComposite.setLayout(new FillLayout());
+        // layoutComposite = new LayoutComposite(parent, 0);
+        // layoutComposite.setLayout(new FillLayout());
 
-        text = new Text(layoutComposite, SWT.NO_BACKGROUND | SWT.READ_ONLY
+        tabFolder = new TabFolder(parent, SWT.BORDER);
+
+        text = new Text(tabFolder, SWT.NO_BACKGROUND | SWT.READ_ONLY
                 | SWT.V_SCROLL | SWT.H_SCROLL);
-        text.setVisible(false);
 
-        canvas = new Canvas(layoutComposite, SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE
+        tabItemText = new TabItem(tabFolder, SWT.NONE);
+        tabItemText.setText("Text");
+        tabItemText.setControl(text);
+
+        canvas = new Canvas(tabFolder, SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE
                 | SWT.V_SCROLL | SWT.H_SCROLL);
-        canvas.setVisible(false);
+        tabItemImage = new TabItem(tabFolder, SWT.NONE);
+        tabItemImage.setText("Image");
+        tabItemImage.setControl(canvas);
 
-        breakpointOption = new BreakpointOption(this, layoutComposite);
-        shaderEditor = new ShaderEditor(this, layoutComposite);
+        breakpointOption = new BreakpointOption(this, tabFolder);
+        tabItemBreakpointOption = new TabItem(tabFolder, SWT.NONE);
+        tabItemBreakpointOption.setText("Breakpoint Option");
+        tabItemBreakpointOption.setControl(breakpointOption);
+
+        shaderEditor = new ShaderEditor(this, tabFolder);
+        tabItemShaderEditor = new TabItem(tabFolder, SWT.NONE);
+        tabItemShaderEditor.setText("Shader Editor");
+        tabItemShaderEditor.setControl(shaderEditor);
 
         final ScrollBar hBar = canvas.getHorizontalBar();
         hBar.addListener(SWT.Selection, new Listener() {
@@ -460,30 +475,6 @@
             }
         };
         manager.add(actionPort);
-
-        Action action = new Action("Breakpoints", Action.AS_CHECK_BOX)
-        {
-            @Override
-            public void run()
-            {
-                breakpointOption.setVisible(!breakpointOption.isVisible());
-                layoutComposite.layout(true);
-            }
-        };
-        action.setChecked(true);
-        manager.add(action);
-
-        action = new Action("Shaders", Action.AS_CHECK_BOX)
-        {
-            @Override
-            public void run()
-            {
-                shaderEditor.setVisible(!shaderEditor.isVisible());
-                layoutComposite.layout(true);
-            }
-        };
-        action.setChecked(true);
-        manager.add(action);
     }
 
     private void ConnectDisconnect() {
@@ -550,10 +541,8 @@
                     return;
                 if (null != msgData.image)
                 {
-                    text.setVisible(false);
-                    canvas.setVisible(true);
                     canvas.setBackgroundImage(msgData.image);
-                    canvas.getParent().layout();
+                    tabFolder.setSelection(tabItemImage);
                 }
                 else if (null != msgData.shader)
                 {
@@ -577,9 +566,7 @@
                     }
 
                     text.setText(builder.toString());
-                    text.setVisible(true);
-                    canvas.setVisible(false);
-                    text.getParent().layout();
+                    tabFolder.setSelection(tabItemText);
                 }
             }
 
@@ -675,20 +662,3 @@
         }
     }
 }
-
-class LayoutComposite extends Composite {
-    public LayoutComposite(Composite parent, int style) {
-        super(parent, style);
-    }
-
-    @Override
-    public Control[] getChildren() {
-        Control[] children = super.getChildren();
-        ArrayList<Control> controls = new ArrayList<Control>();
-        for (int i = 0; i < children.length; i++)
-            if (children[i].isVisible())
-                controls.add(children[i]);
-        children = new Control[controls.size()];
-        return controls.toArray(children);
-    }
-}
diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java b/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java
index 1694b7a..49bece3 100644
--- a/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java
+++ b/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java
@@ -28,22 +28,29 @@
 import org.eclipse.swt.custom.StyledText;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.List;
 import org.eclipse.swt.widgets.ToolBar;
 import org.eclipse.swt.widgets.ToolItem;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.util.ArrayList;
 
 public class ShaderEditor extends Composite implements SelectionListener, ExtendedModifyListener {
     SampleView sampleView;
 
     ToolBar toolbar;
-    ToolItem uploadShader, restoreShader;
+    ToolItem uploadShader, restoreShader, currentPrograms;
     List list;
     StyledText styledText;
 
@@ -51,8 +58,6 @@
 
     ArrayList<GLShader> shadersToUpload = new ArrayList<GLShader>();
 
-    ArrayList<Message> cmds = new ArrayList<Message>();
-
     ShaderEditor(SampleView sampleView, Composite parent) {
         super(parent, 0);
         this.sampleView = sampleView;
@@ -71,6 +76,9 @@
         restoreShader.setText("Original Shader");
         restoreShader.addSelectionListener(this);
 
+        currentPrograms = new ToolItem(toolbar, SWT.PUSH);
+        currentPrograms.setText("Current Programs: ");
+
         list = new List(this, SWT.V_SCROLL);
         list.setFont(new Font(parent.getDisplay(), "Courier", 10, 0));
         list.addSelectionListener(this);
@@ -93,7 +101,12 @@
 
     public void Update() {
         list.removeAll();
+        String progs = "Current Programs: ";
         for (Context context : sampleView.contexts.values()) {
+            if (context.serverShader.current != null) {
+                progs += context.serverShader.current.name + "(0x";
+                progs += Integer.toHexString(context.contextId) + ") ";
+            }
             for (GLShader shader : context.serverShader.privateShaders.values()) {
                 StringBuilder builder = new StringBuilder();
                 builder.append(String.format("%08X", context.contextId));
@@ -117,16 +130,62 @@
                 list.add(builder.toString());
             }
         }
+        // if (!progs.equals(currentPrograms.getText())) {
+        currentPrograms.setText(progs);
+
+        // }
+        toolbar.update();
     }
 
     void UploadShader() {
         current.source = styledText.getText();
 
+        try {
+            File file = File.createTempFile("shader",
+                    current.type == GLEnum.GL_VERTEX_SHADER ? ".vert" : ".frag");
+            FileWriter fileWriter = new FileWriter(file, false);
+            fileWriter.write(current.source);
+            fileWriter.close();
+
+            ProcessBuilder processBuilder = new ProcessBuilder(
+                    "./glsl_compiler", "--glsl-es", file.getAbsolutePath());
+            final Process process = processBuilder.start();
+            InputStream is = process.getInputStream();
+            InputStreamReader isr = new InputStreamReader(is);
+            BufferedReader br = new BufferedReader(isr);
+            String line;
+            String infolog = "";
+
+            styledText.setLineBackground(0, styledText.getLineCount(), null);
+
+            while ((line = br.readLine()) != null) {
+                infolog += line;
+                if (!line.startsWith("0:"))
+                    continue;
+                String[] details = line.split(":|\\(|\\)");
+                final int ln = Integer.parseInt(details[1]);
+                if (ln > 0) // usually line 0 means errors other than syntax
+                    styledText.setLineBackground(ln - 1, 1,
+                            new Color(Display.getCurrent(), 255, 230, 230));
+            }
+            if (infolog.length() > 0) {
+                MessageDialog.openWarning(getShell(),
+                        "Shader Syntax Error, Upload Aborted", infolog);
+                return;
+            }
+
+        } catch (IOException e) {
+            sampleView.showError(e);
+        }
+
         // add the initial command, which when read by server will set
         // expectResponse for the message loop and go into message exchange
         synchronized (shadersToUpload) {
-            if (shadersToUpload.size() > 0) {
-                MessageDialog.openWarning(this.getShell(), "",
+            for (GLShader shader : shadersToUpload) {
+                if (shader.context.context.contextId != current.context.context.contextId)
+                    continue;
+                MessageDialog.openWarning(this.getShell(), "Context 0x" +
+                        Integer.toHexString(current.context.context.contextId),
                         "Previous shader upload not complete, try again");
                 return;
             }
@@ -168,16 +227,22 @@
         synchronized (shadersToUpload) {
             if (shadersToUpload.size() == 0)
                 return false;
-            shader = shadersToUpload.get(0);
             boolean matchingContext = false;
-            for (Context ctx : shader.context.context.shares)
-                if (ctx.contextId == msg.getContextId()) {
-                    matchingContext = true;
+            for (int i = 0; i < shadersToUpload.size(); i++) {
+                shader = shadersToUpload.get(i);
+                for (Context ctx : shader.context.context.shares)
+                    if (ctx.contextId == contextId) {
+                        matchingContext = true;
+                        break;
+                    }
+                if (matchingContext)
+                {
+                    shadersToUpload.remove(i);
                     break;
                 }
+            }
             if (!matchingContext)
                 return false;
-            shadersToUpload.remove(0);
         }
 
         // glShaderSource was already sent to trigger set expectResponse