Home | History | Annotate | Download | only in gldebugger
      1 /*
      2  ** Copyright 2011, The Android Open Source Project
      3  **
      4  ** Licensed under the Apache License, Version 2.0 (the "License");
      5  ** you may not use this file except in compliance with the License.
      6  ** You may obtain a copy of the License at
      7  **
      8  **     http://www.apache.org/licenses/LICENSE-2.0
      9  **
     10  ** Unless required by applicable law or agreed to in writing, software
     11  ** distributed under the License is distributed on an "AS IS" BASIS,
     12  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  ** See the License for the specific language governing permissions and
     14  ** limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.gldebugger;
     18 
     19 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message;
     20 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message.Function;
     21 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message.Prop;
     22 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message.Type;
     23 import com.android.sdklib.util.SparseArray;
     24 
     25 import org.eclipse.jface.action.Action;
     26 import org.eclipse.jface.action.IMenuListener;
     27 import org.eclipse.jface.action.IMenuManager;
     28 import org.eclipse.jface.action.IToolBarManager;
     29 import org.eclipse.jface.action.MenuManager;
     30 import org.eclipse.jface.action.Separator;
     31 import org.eclipse.jface.dialogs.InputDialog;
     32 import org.eclipse.jface.dialogs.MessageDialog;
     33 import org.eclipse.jface.viewers.ISelectionChangedListener;
     34 import org.eclipse.jface.viewers.IStructuredContentProvider;
     35 import org.eclipse.jface.viewers.ITableLabelProvider;
     36 import org.eclipse.jface.viewers.LabelProvider;
     37 import org.eclipse.jface.viewers.ListViewer;
     38 import org.eclipse.jface.viewers.SelectionChangedEvent;
     39 import org.eclipse.jface.viewers.StructuredSelection;
     40 import org.eclipse.jface.viewers.TreeViewer;
     41 import org.eclipse.jface.viewers.Viewer;
     42 import org.eclipse.jface.viewers.ViewerFilter;
     43 import org.eclipse.jface.viewers.ViewerSorter;
     44 import org.eclipse.jface.window.Window;
     45 import org.eclipse.swt.SWT;
     46 import org.eclipse.swt.events.SelectionEvent;
     47 import org.eclipse.swt.events.SelectionListener;
     48 import org.eclipse.swt.graphics.Font;
     49 import org.eclipse.swt.graphics.GC;
     50 import org.eclipse.swt.graphics.Image;
     51 import org.eclipse.swt.graphics.Point;
     52 import org.eclipse.swt.graphics.Rectangle;
     53 import org.eclipse.swt.layout.GridData;
     54 import org.eclipse.swt.layout.GridLayout;
     55 import org.eclipse.swt.widgets.Canvas;
     56 import org.eclipse.swt.widgets.Composite;
     57 import org.eclipse.swt.widgets.Event;
     58 import org.eclipse.swt.widgets.FileDialog;
     59 import org.eclipse.swt.widgets.Listener;
     60 import org.eclipse.swt.widgets.Menu;
     61 import org.eclipse.swt.widgets.ScrollBar;
     62 import org.eclipse.swt.widgets.Shell;
     63 import org.eclipse.swt.widgets.Slider;
     64 import org.eclipse.swt.widgets.TabFolder;
     65 import org.eclipse.swt.widgets.TabItem;
     66 import org.eclipse.swt.widgets.Text;
     67 import org.eclipse.ui.IActionBars;
     68 import org.eclipse.ui.IWorkbenchActionConstants;
     69 import org.eclipse.ui.PlatformUI;
     70 import org.eclipse.ui.part.ViewPart;
     71 
     72 import java.io.FileInputStream;
     73 import java.io.FileNotFoundException;
     74 import java.nio.ByteOrder;
     75 
     76 /**
     77  * This sample class demonstrates how to plug-in a new workbench view. The view
     78  * shows data obtained from the model. The sample creates a dummy model on the
     79  * fly, but a real implementation would connect to the model available either in
     80  * this or another plug-in (e.g. the workspace). The view is connected to the
     81  * model using a content provider.
     82  * <p>
     83  * The view uses a label provider to define how model objects should be
     84  * presented in the view. Each view can present the same model objects using
     85  * different labels and icons, if needed. Alternatively, a single label provider
     86  * can be shared between views in order to ensure that objects of the same type
     87  * are presented in the same way everywhere.
     88  * <p>
     89  */
     90 
     91 public class SampleView extends ViewPart implements Runnable, SelectionListener {
     92     public static final ByteOrder targetByteOrder = ByteOrder.LITTLE_ENDIAN;
     93 
     94     boolean running = false;
     95     Thread thread;
     96     MessageQueue messageQueue;
     97     SparseArray<DebugContext> debugContexts = new SparseArray<DebugContext>();
     98 
     99     /** The ID of the view as specified by the extension. */
    100     public static final String ID = "glesv2debuggerclient.views.SampleView";
    101 
    102     TabFolder tabFolder;
    103     TabItem tabItemText, tabItemImage, tabItemBreakpointOption;
    104     TabItem tabItemShaderEditor, tabContextViewer;
    105     ListViewer viewer; // ListViewer / TableViewer
    106     Slider frameNum; // scale max cannot overlap min, so max is array size
    107     TreeViewer contextViewer;
    108     BreakpointOption breakpointOption;
    109     ShaderEditor shaderEditor;
    110     Canvas canvas;
    111     Text text;
    112     Action actionConnect; // connect / disconnect
    113 
    114     Action actionAutoScroll;
    115     Action actionFilter;
    116     Action actionPort;
    117 
    118     Action actContext; // for toggling contexts
    119     DebugContext current = null;
    120 
    121     Point origin = new Point(0, 0); // for smooth scrolling canvas
    122     String[] filters = null;
    123 
    124     class ViewContentProvider extends LabelProvider implements IStructuredContentProvider,
    125             ITableLabelProvider {
    126         Frame frame = null;
    127 
    128         public void inputChanged(Viewer v, Object oldInput, Object newInput) {
    129             frame = (Frame) newInput;
    130         }
    131 
    132         @Override
    133         public void dispose() {
    134         }
    135 
    136         public Object[] getElements(Object parent) {
    137             return frame.get().toArray();
    138         }
    139 
    140         @Override
    141         public String getText(Object obj) {
    142             MessageData msgData = (MessageData) obj;
    143             return msgData.text;
    144         }
    145 
    146         @Override
    147         public Image getImage(Object obj) {
    148             MessageData msgData = (MessageData) obj;
    149             return msgData.getImage();
    150         }
    151 
    152         public String getColumnText(Object obj, int index) {
    153             MessageData msgData = (MessageData) obj;
    154             if (index >= msgData.columns.length)
    155                 return null;
    156             return msgData.columns[index];
    157         }
    158 
    159         public Image getColumnImage(Object obj, int index) {
    160             if (index > -1)
    161                 return null;
    162             MessageData msgData = (MessageData) obj;
    163             return msgData.getImage();
    164         }
    165     }
    166 
    167     class NameSorter extends ViewerSorter {
    168         @Override
    169         public int compare(Viewer viewer, Object e1, Object e2) {
    170             MessageData m1 = (MessageData) e1;
    171             MessageData m2 = (MessageData) e2;
    172             return (int) ((m1.msg.getTime() - m2.msg.getTime()) * 100);
    173         }
    174     }
    175 
    176     class Filter extends ViewerFilter {
    177         @Override
    178         public boolean select(Viewer viewer, Object parentElement,
    179                 Object element) {
    180             MessageData msgData = (MessageData) element;
    181             if (null == filters)
    182                 return true;
    183             for (int i = 0; i < filters.length; i++)
    184                 if (msgData.text.contains(filters[i]))
    185                     return true;
    186             return false;
    187         }
    188     }
    189 
    190     public SampleView() {
    191 
    192     }
    193 
    194     public void createLeftPane(Composite parent) {
    195         Composite composite = new Composite(parent, 0);
    196 
    197         GridLayout gridLayout = new GridLayout();
    198         gridLayout.numColumns = 1;
    199         composite.setLayout(gridLayout);
    200 
    201         frameNum = new Slider(composite, SWT.BORDER | SWT.HORIZONTAL);
    202         frameNum.setMinimum(0);
    203         frameNum.setMaximum(1);
    204         frameNum.setSelection(0);
    205         frameNum.addSelectionListener(this);
    206 
    207         GridData gridData = new GridData();
    208         gridData.horizontalAlignment = SWT.FILL;
    209         gridData.grabExcessHorizontalSpace = true;
    210         gridData.verticalAlignment = SWT.FILL;
    211         frameNum.setLayoutData(gridData);
    212 
    213         // Table table = new Table(composite, SWT.H_SCROLL | SWT.V_SCROLL |
    214         // SWT.MULTI
    215         // | SWT.FULL_SELECTION);
    216         // TableLayout layout = new TableLayout();
    217         // table.setLayout(layout);
    218         // table.setLinesVisible(true);
    219         // table.setHeaderVisible(true);
    220         // String[] headings = {
    221         // "Name", "Elapsed (ms)", "Detail"
    222         // };
    223         // int[] weights = {
    224         // 50, 16, 60
    225         // };
    226         // int[] widths = {
    227         // 180, 90, 200
    228         // };
    229         // for (int i = 0; i < headings.length; i++) {
    230         // layout.addColumnData(new ColumnWeightData(weights[i], widths[i],
    231         // true));
    232         // TableColumn nameCol = new TableColumn(table, SWT.NONE, i);
    233         // nameCol.setText(headings[i]);
    234         // }
    235 
    236         // viewer = new TableViewer(table);
    237         viewer = new ListViewer(composite, SWT.DEFAULT);
    238         viewer.getList().setFont(new Font(viewer.getList().getDisplay(),
    239                 "Courier", 10, SWT.BOLD));
    240         ViewContentProvider contentProvider = new ViewContentProvider();
    241         viewer.setContentProvider(contentProvider);
    242         viewer.setLabelProvider(contentProvider);
    243         // viewer.setSorter(new NameSorter());
    244         viewer.setFilters(new ViewerFilter[] {
    245                 new Filter()
    246         });
    247 
    248         gridData = new GridData();
    249         gridData.horizontalAlignment = SWT.FILL;
    250         gridData.grabExcessHorizontalSpace = true;
    251         gridData.verticalAlignment = SWT.FILL;
    252         gridData.grabExcessVerticalSpace = true;
    253         viewer.getControl().setLayoutData(gridData);
    254     }
    255 
    256     /**
    257      * This is a callback that will allow us to create the viewer and initialize
    258      * it.
    259      */
    260     @Override
    261     public void createPartControl(Composite parent) {
    262         createLeftPane(parent);
    263 
    264         // Create the help context id for the viewer's control
    265         PlatformUI.getWorkbench().getHelpSystem()
    266                 .setHelp(viewer.getControl(), "GLESv2DebuggerClient.viewer");
    267 
    268         tabFolder = new TabFolder(parent, SWT.BORDER);
    269 
    270         text = new Text(tabFolder, SWT.NO_BACKGROUND | SWT.READ_ONLY
    271                 | SWT.V_SCROLL | SWT.H_SCROLL);
    272 
    273         tabItemText = new TabItem(tabFolder, SWT.NONE);
    274         tabItemText.setText("Text");
    275         tabItemText.setControl(text);
    276 
    277         canvas = new Canvas(tabFolder, SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE
    278                 | SWT.V_SCROLL | SWT.H_SCROLL);
    279         tabItemImage = new TabItem(tabFolder, SWT.NONE);
    280         tabItemImage.setText("Image");
    281         tabItemImage.setControl(canvas);
    282 
    283         breakpointOption = new BreakpointOption(this, tabFolder);
    284         tabItemBreakpointOption = new TabItem(tabFolder, SWT.NONE);
    285         tabItemBreakpointOption.setText("Breakpoint Option");
    286         tabItemBreakpointOption.setControl(breakpointOption);
    287 
    288         shaderEditor = new ShaderEditor(this, tabFolder);
    289         tabItemShaderEditor = new TabItem(tabFolder, SWT.NONE);
    290         tabItemShaderEditor.setText("Shader Editor");
    291         tabItemShaderEditor.setControl(shaderEditor);
    292 
    293         contextViewer = new TreeViewer(tabFolder);
    294         ContextViewProvider contextViewProvider = new ContextViewProvider(this);
    295         contextViewer.addSelectionChangedListener(contextViewProvider);
    296         contextViewer.setContentProvider(contextViewProvider);
    297         contextViewer.setLabelProvider(contextViewProvider);
    298         tabContextViewer = new TabItem(tabFolder, SWT.NONE);
    299         tabContextViewer.setText("Context Viewer");
    300         tabContextViewer.setControl(contextViewer.getTree());
    301 
    302         final ScrollBar hBar = canvas.getHorizontalBar();
    303         hBar.addListener(SWT.Selection, new Listener() {
    304             public void handleEvent(Event e) {
    305                 if (null == canvas.getBackgroundImage())
    306                     return;
    307                 Image image = canvas.getBackgroundImage();
    308                 int hSelection = hBar.getSelection();
    309                 int destX = -hSelection - origin.x;
    310                 Rectangle rect = image.getBounds();
    311                 canvas.scroll(destX, 0, 0, 0, rect.width, rect.height, false);
    312                 origin.x = -hSelection;
    313             }
    314         });
    315         final ScrollBar vBar = canvas.getVerticalBar();
    316         vBar.addListener(SWT.Selection, new Listener() {
    317             public void handleEvent(Event e) {
    318                 if (null == canvas.getBackgroundImage())
    319                     return;
    320                 Image image = canvas.getBackgroundImage();
    321                 int vSelection = vBar.getSelection();
    322                 int destY = -vSelection - origin.y;
    323                 Rectangle rect = image.getBounds();
    324                 canvas.scroll(0, destY, 0, 0, rect.width, rect.height, false);
    325                 origin.y = -vSelection;
    326             }
    327         });
    328         canvas.addListener(SWT.Resize, new Listener() {
    329             public void handleEvent(Event e) {
    330                 if (null == canvas.getBackgroundImage())
    331                     return;
    332                 Image image = canvas.getBackgroundImage();
    333                 Rectangle rect = image.getBounds();
    334                 Rectangle client = canvas.getClientArea();
    335                 hBar.setMaximum(rect.width);
    336                 vBar.setMaximum(rect.height);
    337                 hBar.setThumb(Math.min(rect.width, client.width));
    338                 vBar.setThumb(Math.min(rect.height, client.height));
    339                 int hPage = rect.width - client.width;
    340                 int vPage = rect.height - client.height;
    341                 int hSelection = hBar.getSelection();
    342                 int vSelection = vBar.getSelection();
    343                 if (hSelection >= hPage) {
    344                     if (hPage <= 0)
    345                         hSelection = 0;
    346                     origin.x = -hSelection;
    347                 }
    348                 if (vSelection >= vPage) {
    349                     if (vPage <= 0)
    350                         vSelection = 0;
    351                     origin.y = -vSelection;
    352                 }
    353                 canvas.redraw();
    354             }
    355         });
    356         canvas.addListener(SWT.Paint, new Listener() {
    357             public void handleEvent(Event e) {
    358                 if (null == canvas.getBackgroundImage())
    359                     return;
    360                 Image image = canvas.getBackgroundImage();
    361                 GC gc = e.gc;
    362                 gc.drawImage(image, origin.x, origin.y);
    363                 Rectangle rect = image.getBounds();
    364                 Rectangle client = canvas.getClientArea();
    365                 int marginWidth = client.width - rect.width;
    366                 if (marginWidth > 0) {
    367                     gc.fillRectangle(rect.width, 0, marginWidth, client.height);
    368                 }
    369                 int marginHeight = client.height - rect.height;
    370                 if (marginHeight > 0) {
    371                     gc.fillRectangle(0, rect.height, client.width, marginHeight);
    372                 }
    373             }
    374         });
    375 
    376         hookContextMenu();
    377         hookSelectionChanged();
    378         contributeToActionBars();
    379 
    380         messageQueue = new MessageQueue(this, new ProcessMessage[] {
    381                 breakpointOption, shaderEditor
    382         });
    383     }
    384 
    385     private void hookContextMenu() {
    386         MenuManager menuMgr = new MenuManager("#PopupMenu");
    387         menuMgr.setRemoveAllWhenShown(true);
    388         menuMgr.addMenuListener(new IMenuListener() {
    389             public void menuAboutToShow(IMenuManager manager) {
    390                 SampleView.this.fillContextMenu(manager);
    391             }
    392         });
    393         Menu menu = menuMgr.createContextMenu(viewer.getControl());
    394         viewer.getControl().setMenu(menu);
    395         getSite().registerContextMenu(menuMgr, viewer);
    396     }
    397 
    398     private void contributeToActionBars() {
    399         IActionBars bars = getViewSite().getActionBars();
    400         fillLocalPullDown(bars.getMenuManager());
    401         fillLocalToolBar(bars.getToolBarManager());
    402     }
    403 
    404     private void fillLocalPullDown(IMenuManager manager) {
    405         // manager.add(actionConnect);
    406         // manager.add(new Separator());
    407         // manager.add(actionDisconnect);
    408     }
    409 
    410     private void fillContextMenu(IMenuManager manager) {
    411         // manager.add(actionConnect);
    412         // manager.add(actionDisconnect);
    413         // Other plug-ins can contribute there actions here
    414         manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    415     }
    416 
    417     private void fillLocalToolBar(final IToolBarManager manager) {
    418         actionConnect = new Action("Connect", Action.AS_PUSH_BUTTON) {
    419             @Override
    420             public void run() {
    421                 if (!running)
    422                     changeContext(null); // viewer will switch to newest context
    423                 connectDisconnect();
    424             }
    425         };
    426         manager.add(actionConnect);
    427 
    428         manager.add(new Action("Open File", Action.AS_PUSH_BUTTON)
    429         {
    430             @Override
    431             public void run()
    432             {
    433                 if (!running)
    434                 {
    435                     changeContext(null); // viewer will switch to newest context
    436                     openFile();
    437                 }
    438             }
    439         });
    440 
    441         final Shell shell = this.getViewSite().getShell();
    442         actionAutoScroll = new Action("Auto Scroll", Action.AS_CHECK_BOX) {
    443             @Override
    444             public void run() {
    445             }
    446         };
    447         actionAutoScroll.setChecked(true);
    448         manager.add(actionAutoScroll);
    449 
    450         actionFilter = new Action("*", Action.AS_DROP_DOWN_MENU) {
    451             @Override
    452             public void run() {
    453                 org.eclipse.jface.dialogs.InputDialog dialog = new org.eclipse.jface.dialogs.InputDialog(
    454                         shell, "Contains Filter",
    455                         "case sensitive substring or *",
    456                         actionFilter.getText(), null);
    457                 if (Window.OK == dialog.open()) {
    458                     actionFilter.setText(dialog.getValue());
    459                     manager.update(true);
    460                     filters = dialog.getValue().split("\\|");
    461                     if (filters.length == 1 && filters[0].equals("*"))
    462                         filters = null;
    463                     viewer.refresh();
    464                 }
    465 
    466             }
    467         };
    468         manager.add(actionFilter);
    469 
    470         manager.add(new Action("CaptureDraw", Action.AS_DROP_DOWN_MENU)
    471         {
    472             @Override
    473             public void run()
    474             {
    475                 int contextId = 0;
    476                 if (current != null)
    477                     contextId = current.contextId;
    478                 InputDialog inputDialog = new InputDialog(shell,
    479                         "Capture glDrawArrays/Elements",
    480                         "Enter number of glDrawArrays/Elements to glReadPixels for "
    481                                 + "context 0x" + Integer.toHexString(contextId) +
    482                                 "\n(0x0 is any context)", "9001", null);
    483                 if (inputDialog.open() != Window.OK)
    484                     return;
    485                 Message.Builder builder = Message.newBuilder();
    486                 builder.setContextId(contextId);
    487                 builder.setType(Type.Response);
    488                 builder.setExpectResponse(false);
    489                 builder.setFunction(Function.SETPROP);
    490                 builder.setProp(Prop.CaptureDraw);
    491                 builder.setArg0(Integer.parseInt(inputDialog.getValue()));
    492                 messageQueue.addCommand(builder.build());
    493             }
    494         });
    495 
    496         manager.add(new Action("CaptureSwap", Action.AS_DROP_DOWN_MENU)
    497         {
    498             @Override
    499             public void run()
    500             {
    501                 int contextId = 0;
    502                 if (current != null)
    503                     contextId = current.contextId;
    504                 InputDialog inputDialog = new InputDialog(shell,
    505                         "Capture eglSwapBuffers",
    506                         "Enter number of eglSwapBuffers to glReadPixels for "
    507                                 + "context 0x" + Integer.toHexString(contextId) +
    508                                 "\n(0x0 is any context)", "9001", null);
    509                 if (inputDialog.open() != Window.OK)
    510                     return;
    511                 Message.Builder builder = Message.newBuilder();
    512                 builder.setContextId(contextId);
    513                 builder.setType(Type.Response);
    514                 builder.setExpectResponse(false);
    515                 builder.setFunction(Function.SETPROP);
    516                 builder.setProp(Prop.CaptureSwap);
    517                 builder.setArg0(Integer.parseInt(inputDialog.getValue()));
    518                 messageQueue.addCommand(builder.build());
    519             }
    520         });
    521 
    522         manager.add(new Action("SYSTEM_TIME_THREAD", Action.AS_DROP_DOWN_MENU)
    523         {
    524             @Override
    525             public void run()
    526             {
    527                 final String[] timeModes = {
    528                         "SYSTEM_TIME_REALTIME", "SYSTEM_TIME_MONOTONIC", "SYSTEM_TIME_PROCESS",
    529                         "SYSTEM_TIME_THREAD"
    530                 };
    531                 int i = java.util.Arrays.asList(timeModes).indexOf(this.getText());
    532                 i = (i + 1) % timeModes.length;
    533                 Message.Builder builder = Message.newBuilder();
    534                 builder.setContextId(0); // FIXME: proper context id
    535                 builder.setType(Type.Response);
    536                 builder.setExpectResponse(false);
    537                 builder.setFunction(Message.Function.SETPROP);
    538                 builder.setProp(Prop.TimeMode);
    539                 builder.setArg0(i);
    540                 messageQueue.addCommand(builder.build());
    541                 this.setText(timeModes[i]);
    542                 manager.update(true);
    543             }
    544         });
    545 
    546         actContext = new Action("Context: 0x", Action.AS_DROP_DOWN_MENU) {
    547             @Override
    548             public void run() {
    549                 if (debugContexts.size() < 2)
    550                     return;
    551                 final String idStr = this.getText().substring(
    552                                           "Context: 0x".length());
    553                 if (idStr.length() == 0)
    554                     return;
    555                 final int contextId = Integer.parseInt(idStr, 16);
    556                 int index = debugContexts.indexOfKey(contextId);
    557                 index = (index + 1) % debugContexts.size();
    558                 changeContext(debugContexts.valueAt(index));
    559             }
    560         };
    561         manager.add(actContext);
    562 
    563         actionPort = new Action("5039", Action.AS_DROP_DOWN_MENU)
    564         {
    565             @Override
    566             public void run() {
    567                 InputDialog dialog = new InputDialog(shell, "Port", "Debugger port",
    568                         actionPort.getText(), null);
    569                 if (Window.OK == dialog.open()) {
    570                     actionPort.setText(dialog.getValue());
    571                     manager.update(true);
    572                 }
    573             }
    574         };
    575         manager.add(actionPort);
    576 
    577         manager.add(new Action("CodeGen Frame", Action.AS_PUSH_BUTTON)
    578         {
    579             @Override
    580             public void run()
    581             {
    582                 if (current != null)
    583                 {
    584                     new CodeGen().codeGenFrame((Frame) viewer.getInput());
    585                     // need to reload current frame
    586                     viewer.setInput(current.getFrame(frameNum.getSelection()));
    587                 }
    588             }
    589         });
    590 
    591         manager.add(new Action("CodeGen Frames", Action.AS_PUSH_BUTTON)
    592         {
    593             @Override
    594             public void run()
    595             {
    596                 if (current != null)
    597                 {
    598                     new CodeGen().codeGenFrames(current, frameNum.getSelection() + 1,
    599                             getSite().getShell());
    600                     // need to reload current frame
    601                     viewer.setInput(current.getFrame(frameNum.getSelection()));
    602                 }
    603             }
    604         });
    605     }
    606 
    607     private void openFile() {
    608         FileDialog dialog = new FileDialog(getSite().getShell(), SWT.OPEN);
    609         dialog.setText("Open");
    610         dialog.setFilterExtensions(new String[] {
    611                 "*.gles2dbg"
    612         });
    613         String filePath = dialog.open();
    614         if (filePath == null)
    615             return;
    616         FileInputStream file = null;
    617         try {
    618             file = new FileInputStream(filePath);
    619         } catch (FileNotFoundException e) {
    620             e.printStackTrace();
    621             return;
    622         }
    623         running = true;
    624         messageQueue.start(targetByteOrder, file);
    625         thread = new Thread(this);
    626         thread.start();
    627         actionConnect.setText("Disconnect");
    628         getViewSite().getActionBars().getToolBarManager().update(true);
    629     }
    630 
    631     private void connectDisconnect() {
    632         if (!running) {
    633             running = true;
    634             messageQueue.start(targetByteOrder, null);
    635             thread = new Thread(this);
    636             thread.start();
    637             actionConnect.setText("Disconnect");
    638         } else {
    639             running = false;
    640             messageQueue.stop();
    641             actionConnect.setText("Connect");
    642         }
    643         this.getSite().getShell().getDisplay().syncExec(new Runnable() {
    644             public void run() {
    645                 getViewSite().getActionBars().getToolBarManager().update(true);
    646             }
    647         });
    648     }
    649 
    650     void messageDataSelected(final MessageData msgData) {
    651         if (null == msgData)
    652             return;
    653         if (frameNum.getSelection() == frameNum.getMaximum())
    654             return; // scale max cannot overlap min, so max is array size
    655         final Frame frame = current.getFrame(frameNum.getSelection());
    656         final Context context = frame.computeContext(msgData);
    657         contextViewer.setInput(context);
    658         if (msgData.getImage() != null) {
    659             canvas.setBackgroundImage(msgData.getImage());
    660             tabFolder.setSelection(tabItemImage);
    661             canvas.redraw();
    662         } else if (null != msgData.shader) {
    663             text.setText(msgData.shader);
    664             tabFolder.setSelection(tabItemText);
    665         } else if (null != msgData.attribs) {
    666             StringBuilder builder = new StringBuilder();
    667             final int maxAttrib = msgData.msg.getArg7();
    668             for (int i = 0; i < msgData.attribs[0].length / 4; i++) {
    669                 if (msgData.indices != null) {
    670                     builder.append(msgData.indices[i] & 0xffff);
    671                     builder.append(": ");
    672                 }
    673                 for (int j = 0; j < maxAttrib; j++) {
    674                     for (int k = 0; k < 4; k++)
    675                         builder.append(String.format("%.3g ", msgData.attribs[j][i * 4 + k]));
    676                     if (j < maxAttrib - 1)
    677                         builder.append("|| ");
    678                 }
    679                 builder.append('\n');
    680             }
    681             text.setText(builder.toString());
    682             tabFolder.setSelection(tabItemText);
    683         }
    684     }
    685 
    686     private void hookSelectionChanged() {
    687         viewer.addSelectionChangedListener(new ISelectionChangedListener() {
    688             public void selectionChanged(SelectionChangedEvent event) {
    689                 StructuredSelection selection = (StructuredSelection) event
    690                         .getSelection();
    691                 if (null == selection)
    692                     return;
    693                 MessageData msgData = (MessageData) selection.getFirstElement();
    694                 messageDataSelected(msgData);
    695             }
    696         });
    697     }
    698 
    699     public void showError(final Exception e) {
    700         viewer.getControl().getDisplay().syncExec(new Runnable() {
    701             public void run() {
    702                 MessageDialog.openError(viewer.getControl().getShell(),
    703                         "GL ES 2.0 Debugger Client", e.getMessage());
    704             }
    705         });
    706     }
    707 
    708     /**
    709      * Passing the focus request to the viewer's control.
    710      */
    711     @Override
    712     public void setFocus() {
    713         viewer.getControl().setFocus();
    714     }
    715 
    716     public void run() {
    717         int newMessages = 0;
    718 
    719         boolean shaderEditorUpdate = false;
    720         while (running) {
    721             final Message oriMsg = messageQueue.removeCompleteMessage(0);
    722             if (oriMsg == null && !messageQueue.isRunning())
    723                 break;
    724             if (newMessages > 60 || (newMessages > 0 && null == oriMsg)) {
    725                 newMessages = 0;
    726                 if (current != null && current.uiUpdate)
    727                     getSite().getShell().getDisplay().syncExec(new Runnable() {
    728                         public void run() {
    729                             if (frameNum.getSelection() == current.frameCount() - 1 ||
    730                                     frameNum.getSelection() == current.frameCount() - 2)
    731                             {
    732                                 viewer.refresh(false);
    733                                 if (actionAutoScroll.isChecked())
    734                                     viewer.getList().setSelection(
    735                                             viewer.getList().getItemCount() - 1);
    736                             }
    737                             frameNum.setMaximum(current.frameCount());
    738                         }
    739                     });
    740                 current.uiUpdate = false;
    741 
    742                 if (shaderEditorUpdate)
    743                     this.getSite().getShell().getDisplay().syncExec(new Runnable() {
    744                         public void run() {
    745                             shaderEditor.updateUI();
    746                         }
    747                     });
    748                 shaderEditorUpdate = false;
    749             }
    750             if (null == oriMsg) {
    751                 try {
    752                     Thread.sleep(1);
    753                     continue;
    754                 } catch (InterruptedException e) {
    755                     showError(e);
    756                 }
    757             }
    758             DebugContext debugContext = debugContexts.get(oriMsg.getContextId());
    759             if (debugContext == null) {
    760                 debugContext = new DebugContext(oriMsg.getContextId());
    761                 debugContexts.put(oriMsg.getContextId(), debugContext);
    762             }
    763             debugContext.processMessage(oriMsg);
    764             shaderEditorUpdate |= debugContext.currentContext.serverShader.uiUpdate;
    765             debugContext.currentContext.serverShader.uiUpdate = false;
    766             if (current == null && debugContext.frameCount() > 0)
    767                 changeContext(debugContext);
    768             newMessages++;
    769         }
    770         if (running)
    771             connectDisconnect(); // error occurred, disconnect
    772     }
    773 
    774     /** can be called from non-UI thread */
    775     void changeContext(final DebugContext newContext) {
    776         getSite().getShell().getDisplay().syncExec(new Runnable() {
    777             public void run() {
    778                 current = newContext;
    779                 if (current != null)
    780                 {
    781                     frameNum.setMaximum(current.frameCount());
    782                     if (frameNum.getSelection() >= current.frameCount())
    783                         if (current.frameCount() > 0)
    784                             frameNum.setSelection(current.frameCount() - 1);
    785                         else
    786                             frameNum.setSelection(0);
    787                     viewer.setInput(current.getFrame(frameNum.getSelection()));
    788                     actContext.setText("Context: 0x" + Integer.toHexString(current.contextId));
    789                 }
    790                 else
    791                 {
    792                     frameNum.setMaximum(1); // cannot overlap min
    793                     frameNum.setSelection(0);
    794                     viewer.setInput(null);
    795                     actContext.setText("Context: 0x");
    796                 }
    797                 shaderEditor.updateUI();
    798                 getViewSite().getActionBars().getToolBarManager().update(true);
    799             }
    800         });
    801     }
    802 
    803     public void widgetSelected(SelectionEvent e) {
    804         if (e.widget != frameNum)
    805             assert false;
    806         if (current == null)
    807             return;
    808         if (frameNum.getSelection() == current.frameCount())
    809             return; // scale maximum cannot overlap minimum
    810         Frame frame = current.getFrame(frameNum.getSelection());
    811         viewer.setInput(frame);
    812     }
    813 
    814     public void widgetDefaultSelected(SelectionEvent e) {
    815         widgetSelected(e);
    816     }
    817 }
    818