• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.LabelProvider;
36 import org.eclipse.jface.viewers.ListViewer;
37 import org.eclipse.jface.viewers.SelectionChangedEvent;
38 import org.eclipse.jface.viewers.StructuredSelection;
39 import org.eclipse.jface.viewers.TreeViewer;
40 import org.eclipse.jface.viewers.Viewer;
41 import org.eclipse.jface.viewers.ViewerFilter;
42 import org.eclipse.jface.window.Window;
43 import org.eclipse.swt.SWT;
44 import org.eclipse.swt.events.SelectionAdapter;
45 import org.eclipse.swt.events.SelectionEvent;
46 import org.eclipse.swt.graphics.Font;
47 import org.eclipse.swt.graphics.GC;
48 import org.eclipse.swt.graphics.Image;
49 import org.eclipse.swt.graphics.Point;
50 import org.eclipse.swt.graphics.Rectangle;
51 import org.eclipse.swt.layout.GridData;
52 import org.eclipse.swt.layout.GridLayout;
53 import org.eclipse.swt.widgets.Canvas;
54 import org.eclipse.swt.widgets.Composite;
55 import org.eclipse.swt.widgets.Event;
56 import org.eclipse.swt.widgets.FileDialog;
57 import org.eclipse.swt.widgets.Label;
58 import org.eclipse.swt.widgets.Listener;
59 import org.eclipse.swt.widgets.Menu;
60 import org.eclipse.swt.widgets.Scale;
61 import org.eclipse.swt.widgets.ScrollBar;
62 import org.eclipse.swt.widgets.Shell;
63 import org.eclipse.swt.widgets.Spinner;
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.part.ViewPart;
70 
71 import java.io.FileInputStream;
72 import java.io.FileNotFoundException;
73 import java.nio.ByteOrder;
74 
75 public class GLFramesView extends ViewPart implements Runnable {
76     public static final ByteOrder TARGET_BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
77 
78     private boolean mRunning = false;
79     private Thread mThread;
80 
81     MessageQueue messageQueue;
82     SparseArray<DebugContext> debugContexts = new SparseArray<DebugContext>();
83 
84     TabFolder tabFolder;
85     TabItem tabItemText, tabItemImage, tabItemBreakpointOption;
86     TabItem tabItemShaderEditor, tabContextViewer;
87 
88     private ListViewer mViewer; // ListViewer / TableViewer
89     private Scale mFrameScale; // scale max cannot overlap min, so max is array size
90     private Spinner mFrameNumberspinner;
91     private TreeViewer mContextViewer;
92     private BreakpointOption mBreakpointOption;
93     private ShaderEditor mShaderEditor;
94     Canvas canvas;
95     private Text mText;
96     private Action mActionConnect; // connect / disconnect
97 
98     private Action mActionAutoScroll;
99     private Action mActionFilter;
100     Action actionPort;
101 
102     private Action mActionContext; // for toggling contexts
103     DebugContext current = null;
104 
105     private Point mOrigin = new Point(0, 0); // for smooth scrolling canvas
106     private String[] mTextFilters = null;
107 
108     private static class ViewContentProvider extends LabelProvider implements IStructuredContentProvider {
109         private Frame mFrame = null;
110 
111         @Override
inputChanged(Viewer v, Object oldInput, Object newInput)112         public void inputChanged(Viewer v, Object oldInput, Object newInput) {
113             mFrame = (Frame) newInput;
114         }
115 
116         @Override
dispose()117         public void dispose() {
118         }
119 
120         @Override
getElements(Object parent)121         public Object[] getElements(Object parent) {
122             return mFrame.get().toArray();
123         }
124 
125         @Override
getText(Object obj)126         public String getText(Object obj) {
127             MessageData msgData = (MessageData) obj;
128             return msgData.text;
129         }
130 
131         @Override
getImage(Object obj)132         public Image getImage(Object obj) {
133             MessageData msgData = (MessageData) obj;
134             return msgData.getImage();
135         }
136     }
137 
138     private class Filter extends ViewerFilter {
139         @Override
select(Viewer viewer, Object parentElement, Object element)140         public boolean select(Viewer viewer, Object parentElement,
141                 Object element) {
142             MessageData msgData = (MessageData) element;
143             if (null == mTextFilters)
144                 return true;
145             for (int i = 0; i < mTextFilters.length; i++)
146                 if (msgData.text.contains(mTextFilters[i]))
147                     return true;
148             return false;
149         }
150     }
151 
createLeftPane(Composite parent)152     private void createLeftPane(Composite parent) {
153         Composite composite = new Composite(parent, 0);
154 
155         GridLayout gridLayout = new GridLayout();
156         gridLayout.numColumns = 3;
157         composite.setLayout(gridLayout);
158         composite.setLayoutData(new GridData(GridData.FILL_BOTH));
159 
160         // Frame: -------|slider|--------- [ Spinner ]
161         Label l = new Label(composite, SWT.NONE);
162         l.setText("Frame:");
163 
164         mFrameScale = new Scale(composite, SWT.BORDER | SWT.HORIZONTAL);
165         mFrameScale.setMinimum(0);
166         mFrameScale.setMaximum(1);
167         mFrameScale.setSelection(0);
168         mFrameScale.addSelectionListener(new SelectionAdapter() {
169             @Override
170             public void widgetSelected(SelectionEvent e) {
171                 if (current == null) {
172                     return;
173                 }
174                 int selectedFrame = mFrameScale.getSelection();
175                 mFrameNumberspinner.setSelection(selectedFrame);
176                 selectFrame(selectedFrame);
177             }
178         });
179 
180         GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
181         mFrameScale.setLayoutData(gridData);
182 
183         mFrameNumberspinner = new Spinner(composite, SWT.BORDER);
184         mFrameNumberspinner.setMinimum(0);
185         mFrameNumberspinner.setMaximum(1);
186         mFrameNumberspinner.setSelection(0);
187         mFrameNumberspinner.addSelectionListener(new SelectionAdapter() {
188             @Override
189             public void widgetSelected(SelectionEvent e) {
190                 if (current == null) {
191                     return;
192                 }
193                 int selectedFrame = mFrameNumberspinner.getSelection();
194                 mFrameScale.setSelection(selectedFrame);
195                 selectFrame(selectedFrame);
196             }
197         });
198 
199         mViewer = new ListViewer(composite, SWT.DEFAULT);
200         mViewer.getList().setFont(new Font(mViewer.getList().getDisplay(),
201                 "Courier", 10, SWT.BOLD));
202         ViewContentProvider contentProvider = new ViewContentProvider();
203         mViewer.setContentProvider(contentProvider);
204         mViewer.setLabelProvider(contentProvider);
205         mViewer.setFilters(new ViewerFilter[] {
206                 new Filter()
207         });
208 
209         gridData = new GridData(GridData.FILL_BOTH);
210         gridData.horizontalSpan = 3;
211         mViewer.getControl().setLayoutData(gridData);
212     }
213 
selectFrame(int frameNumber)214     private void selectFrame(int frameNumber) {
215         if (frameNumber == current.frameCount()) {
216             return; // scale maximum cannot overlap minimum
217         }
218 
219         Frame frame = current.getFrame(frameNumber);
220         mViewer.setInput(frame);
221     }
222 
223     @Override
createPartControl(Composite parent)224     public void createPartControl(Composite parent) {
225         createLeftPane(parent);
226 
227         tabFolder = new TabFolder(parent, SWT.BORDER);
228 
229         mText = new Text(tabFolder, SWT.NO_BACKGROUND | SWT.READ_ONLY
230                 | SWT.V_SCROLL | SWT.H_SCROLL);
231 
232         tabItemText = new TabItem(tabFolder, SWT.NONE);
233         tabItemText.setText("Text");
234         tabItemText.setControl(mText);
235 
236         canvas = new Canvas(tabFolder, SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE
237                 | SWT.V_SCROLL | SWT.H_SCROLL);
238         tabItemImage = new TabItem(tabFolder, SWT.NONE);
239         tabItemImage.setText("Image");
240         tabItemImage.setControl(canvas);
241 
242         mBreakpointOption = new BreakpointOption(this, tabFolder);
243         tabItemBreakpointOption = new TabItem(tabFolder, SWT.NONE);
244         tabItemBreakpointOption.setText("Breakpoint Option");
245         tabItemBreakpointOption.setControl(mBreakpointOption);
246 
247         mShaderEditor = new ShaderEditor(this, tabFolder);
248         tabItemShaderEditor = new TabItem(tabFolder, SWT.NONE);
249         tabItemShaderEditor.setText("Shader Editor");
250         tabItemShaderEditor.setControl(mShaderEditor);
251 
252         mContextViewer = new TreeViewer(tabFolder);
253         ContextViewProvider contextViewProvider = new ContextViewProvider(this);
254         mContextViewer.addSelectionChangedListener(contextViewProvider);
255         mContextViewer.setContentProvider(contextViewProvider);
256         mContextViewer.setLabelProvider(contextViewProvider);
257         tabContextViewer = new TabItem(tabFolder, SWT.NONE);
258         tabContextViewer.setText("Context Viewer");
259         tabContextViewer.setControl(mContextViewer.getTree());
260 
261         final ScrollBar hBar = canvas.getHorizontalBar();
262         hBar.addListener(SWT.Selection, new Listener() {
263             @Override
264             public void handleEvent(Event e) {
265                 if (null == canvas.getBackgroundImage())
266                     return;
267                 Image image = canvas.getBackgroundImage();
268                 int hSelection = hBar.getSelection();
269                 int destX = -hSelection - mOrigin.x;
270                 Rectangle rect = image.getBounds();
271                 canvas.scroll(destX, 0, 0, 0, rect.width, rect.height, false);
272                 mOrigin.x = -hSelection;
273             }
274         });
275         final ScrollBar vBar = canvas.getVerticalBar();
276         vBar.addListener(SWT.Selection, new Listener() {
277             @Override
278             public void handleEvent(Event e) {
279                 if (null == canvas.getBackgroundImage())
280                     return;
281                 Image image = canvas.getBackgroundImage();
282                 int vSelection = vBar.getSelection();
283                 int destY = -vSelection - mOrigin.y;
284                 Rectangle rect = image.getBounds();
285                 canvas.scroll(0, destY, 0, 0, rect.width, rect.height, false);
286                 mOrigin.y = -vSelection;
287             }
288         });
289         canvas.addListener(SWT.Resize, new Listener() {
290             @Override
291             public void handleEvent(Event e) {
292                 if (null == canvas.getBackgroundImage())
293                     return;
294                 Image image = canvas.getBackgroundImage();
295                 Rectangle rect = image.getBounds();
296                 Rectangle client = canvas.getClientArea();
297                 hBar.setMaximum(rect.width);
298                 vBar.setMaximum(rect.height);
299                 hBar.setThumb(Math.min(rect.width, client.width));
300                 vBar.setThumb(Math.min(rect.height, client.height));
301                 int hPage = rect.width - client.width;
302                 int vPage = rect.height - client.height;
303                 int hSelection = hBar.getSelection();
304                 int vSelection = vBar.getSelection();
305                 if (hSelection >= hPage) {
306                     if (hPage <= 0)
307                         hSelection = 0;
308                     mOrigin.x = -hSelection;
309                 }
310                 if (vSelection >= vPage) {
311                     if (vPage <= 0)
312                         vSelection = 0;
313                     mOrigin.y = -vSelection;
314                 }
315                 canvas.redraw();
316             }
317         });
318         canvas.addListener(SWT.Paint, new Listener() {
319             @Override
320             public void handleEvent(Event e) {
321                 if (null == canvas.getBackgroundImage())
322                     return;
323                 Image image = canvas.getBackgroundImage();
324                 GC gc = e.gc;
325                 gc.drawImage(image, mOrigin.x, mOrigin.y);
326                 Rectangle rect = image.getBounds();
327                 Rectangle client = canvas.getClientArea();
328                 int marginWidth = client.width - rect.width;
329                 if (marginWidth > 0) {
330                     gc.fillRectangle(rect.width, 0, marginWidth, client.height);
331                 }
332                 int marginHeight = client.height - rect.height;
333                 if (marginHeight > 0) {
334                     gc.fillRectangle(0, rect.height, client.width, marginHeight);
335                 }
336             }
337         });
338 
339         hookContextMenu();
340         hookSelectionChanged();
341         contributeToActionBars();
342 
343         messageQueue = new MessageQueue(this, new ProcessMessage[] {
344                 mBreakpointOption, mShaderEditor
345         });
346     }
347 
hookContextMenu()348     private void hookContextMenu() {
349         MenuManager menuMgr = new MenuManager("#PopupMenu");
350         menuMgr.setRemoveAllWhenShown(true);
351         menuMgr.addMenuListener(new IMenuListener() {
352             @Override
353             public void menuAboutToShow(IMenuManager manager) {
354                 GLFramesView.this.fillContextMenu(manager);
355             }
356         });
357         Menu menu = menuMgr.createContextMenu(mViewer.getControl());
358         mViewer.getControl().setMenu(menu);
359         getSite().registerContextMenu(menuMgr, mViewer);
360     }
361 
contributeToActionBars()362     private void contributeToActionBars() {
363         IActionBars bars = getViewSite().getActionBars();
364         fillLocalToolBar(bars.getToolBarManager());
365     }
366 
fillContextMenu(IMenuManager manager)367     private void fillContextMenu(IMenuManager manager) {
368         // Other plug-ins can contribute there actions here
369         manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
370     }
371 
fillLocalToolBar(final IToolBarManager manager)372     private void fillLocalToolBar(final IToolBarManager manager) {
373         mActionConnect = new Action("Connect", Action.AS_PUSH_BUTTON) {
374             @Override
375             public void run() {
376                 if (!mRunning)
377                     changeContext(null); // viewer will switch to newest context
378                 connectDisconnect();
379             }
380         };
381         manager.add(mActionConnect);
382 
383         manager.add(new Action("Open File", Action.AS_PUSH_BUTTON) {
384             @Override
385             public void run() {
386                 if (!mRunning) {
387                     changeContext(null); // viewer will switch to newest context
388                     openFile();
389                 }
390             }
391         });
392 
393         final Shell shell = this.getViewSite().getShell();
394         mActionAutoScroll = new Action("Auto Scroll", Action.AS_CHECK_BOX) {
395             @Override
396             public void run() {
397             }
398         };
399         mActionAutoScroll.setChecked(true);
400         manager.add(mActionAutoScroll);
401 
402         mActionFilter = new Action("*", Action.AS_DROP_DOWN_MENU) {
403             @Override
404             public void run() {
405                 InputDialog dialog = new InputDialog(
406                         shell, "Contains Filter",
407                         "case sensitive substring or *",
408                         mActionFilter.getText(), null);
409                 if (Window.OK == dialog.open()) {
410                     mActionFilter.setText(dialog.getValue());
411                     manager.update(true);
412                     mTextFilters = dialog.getValue().split("\\|");
413                     if (mTextFilters.length == 1 && mTextFilters[0].equals("*"))
414                         mTextFilters = null;
415                     mViewer.refresh();
416                 }
417 
418             }
419         };
420         manager.add(mActionFilter);
421 
422         manager.add(new Action("CaptureDraw", Action.AS_DROP_DOWN_MENU) {
423             @Override
424             public void run() {
425                 int contextId = 0;
426                 if (current != null)
427                     contextId = current.contextId;
428                 InputDialog inputDialog = new InputDialog(shell,
429                         "Capture glDrawArrays/Elements",
430                         "Enter number of glDrawArrays/Elements to glReadPixels for "
431                                 + "context 0x" + Integer.toHexString(contextId) +
432                                 "\n(0x0 is any context)", "9001", null);
433                 if (inputDialog.open() != Window.OK)
434                     return;
435                 Message.Builder builder = Message.newBuilder();
436                 builder.setContextId(contextId);
437                 builder.setType(Type.Response);
438                 builder.setExpectResponse(false);
439                 builder.setFunction(Function.SETPROP);
440                 builder.setProp(Prop.CaptureDraw);
441                 builder.setArg0(Integer.parseInt(inputDialog.getValue()));
442                 messageQueue.addCommand(builder.build());
443             }
444         });
445 
446         manager.add(new Action("CaptureSwap", Action.AS_DROP_DOWN_MENU) {
447             @Override
448             public void run() {
449                 int contextId = 0;
450                 if (current != null)
451                     contextId = current.contextId;
452                 InputDialog inputDialog = new InputDialog(shell,
453                         "Capture eglSwapBuffers",
454                         "Enter number of eglSwapBuffers to glReadPixels for "
455                                 + "context 0x" + Integer.toHexString(contextId) +
456                                 "\n(0x0 is any context)", "9001", null);
457                 if (inputDialog.open() != Window.OK)
458                     return;
459                 Message.Builder builder = Message.newBuilder();
460                 builder.setContextId(contextId);
461                 builder.setType(Type.Response);
462                 builder.setExpectResponse(false);
463                 builder.setFunction(Function.SETPROP);
464                 builder.setProp(Prop.CaptureSwap);
465                 builder.setArg0(Integer.parseInt(inputDialog.getValue()));
466                 messageQueue.addCommand(builder.build());
467             }
468         });
469 
470         manager.add(new Action("SYSTEM_TIME_THREAD", Action.AS_DROP_DOWN_MENU) {
471             @Override
472             public void run() {
473                 final String[] timeModes = {
474                         "SYSTEM_TIME_REALTIME", "SYSTEM_TIME_MONOTONIC", "SYSTEM_TIME_PROCESS",
475                         "SYSTEM_TIME_THREAD"
476                 };
477                 int i = java.util.Arrays.asList(timeModes).indexOf(this.getText());
478                 i = (i + 1) % timeModes.length;
479                 Message.Builder builder = Message.newBuilder();
480                 builder.setContextId(0); // FIXME: proper context id
481                 builder.setType(Type.Response);
482                 builder.setExpectResponse(false);
483                 builder.setFunction(Message.Function.SETPROP);
484                 builder.setProp(Prop.TimeMode);
485                 builder.setArg0(i);
486                 messageQueue.addCommand(builder.build());
487                 this.setText(timeModes[i]);
488                 manager.update(true);
489             }
490         });
491 
492         mActionContext = new Action("Context: 0x", Action.AS_DROP_DOWN_MENU) {
493             @Override
494             public void run() {
495                 if (debugContexts.size() < 2)
496                     return;
497                 final String idStr = this.getText().substring(
498                                           "Context: 0x".length());
499                 if (idStr.length() == 0)
500                     return;
501                 final int contextId = Integer.parseInt(idStr, 16);
502                 int index = debugContexts.indexOfKey(contextId);
503                 index = (index + 1) % debugContexts.size();
504                 changeContext(debugContexts.valueAt(index));
505             }
506         };
507         manager.add(mActionContext);
508 
509         actionPort = new Action("5039", Action.AS_DROP_DOWN_MENU) {
510             @Override
511             public void run() {
512                 InputDialog dialog = new InputDialog(shell, "Port", "Debugger port",
513                         actionPort.getText(), null);
514                 if (Window.OK == dialog.open()) {
515                     actionPort.setText(dialog.getValue());
516                     manager.update(true);
517                 }
518             }
519         };
520         manager.add(actionPort);
521 
522         manager.add(new Action("CodeGen Frame", Action.AS_PUSH_BUTTON) {
523             @Override
524             public void run() {
525                 if (current != null) {
526                     new CodeGen().codeGenFrame((Frame) mViewer.getInput());
527                     // need to reload current frame
528                     mViewer.setInput(current.getFrame(mFrameScale.getSelection()));
529                 }
530             }
531         });
532 
533         manager.add(new Action("CodeGen Frames", Action.AS_PUSH_BUTTON) {
534             @Override
535             public void run() {
536                 if (current != null) {
537                     new CodeGen().codeGenFrames(current, mFrameScale.getSelection() + 1,
538                             getSite().getShell());
539                     // need to reload current frame
540                     mViewer.setInput(current.getFrame(mFrameScale.getSelection()));
541                 }
542             }
543         });
544     }
545 
openFile()546     private void openFile() {
547         FileDialog dialog = new FileDialog(getSite().getShell(), SWT.OPEN);
548         dialog.setText("Open");
549         dialog.setFilterExtensions(new String[] {
550                 "*.gles2dbg"
551         });
552         String filePath = dialog.open();
553         if (filePath == null)
554             return;
555         FileInputStream file = null;
556         try {
557             file = new FileInputStream(filePath);
558         } catch (FileNotFoundException e) {
559             e.printStackTrace();
560             return;
561         }
562         mRunning = true;
563         messageQueue.start(TARGET_BYTE_ORDER, file);
564         mThread = new Thread(this);
565         mThread.start();
566         mActionConnect.setText("Disconnect");
567         getViewSite().getActionBars().getToolBarManager().update(true);
568     }
569 
connectDisconnect()570     private void connectDisconnect() {
571         if (!mRunning) {
572             mRunning = true;
573             messageQueue.start(TARGET_BYTE_ORDER, null);
574             mThread = new Thread(this);
575             mThread.start();
576             mActionConnect.setText("Disconnect");
577         } else {
578             mRunning = false;
579             messageQueue.stop();
580             mActionConnect.setText("Connect");
581         }
582         this.getSite().getShell().getDisplay().syncExec(new Runnable() {
583             @Override
584             public void run() {
585                 getViewSite().getActionBars().getToolBarManager().update(true);
586             }
587         });
588     }
589 
messageDataSelected(final MessageData msgData)590     void messageDataSelected(final MessageData msgData) {
591         if (null == msgData)
592             return;
593         if (mFrameScale.getSelection() == mFrameScale.getMaximum())
594             return; // scale max cannot overlap min, so max is array size
595         final Frame frame = current.getFrame(mFrameScale.getSelection());
596         final Context context = frame.computeContext(msgData);
597         mContextViewer.setInput(context);
598         if (msgData.getImage() != null) {
599             canvas.setBackgroundImage(msgData.getImage());
600             tabFolder.setSelection(tabItemImage);
601             canvas.redraw();
602         } else if (null != msgData.shader) {
603             mText.setText(msgData.shader);
604             tabFolder.setSelection(tabItemText);
605         } else if (null != msgData.attribs) {
606             StringBuilder builder = new StringBuilder();
607             final int maxAttrib = msgData.msg.getArg7();
608             for (int i = 0; i < msgData.attribs[0].length / 4; i++) {
609                 if (msgData.indices != null) {
610                     builder.append(msgData.indices[i] & 0xffff);
611                     builder.append(": ");
612                 }
613                 for (int j = 0; j < maxAttrib; j++) {
614                     for (int k = 0; k < 4; k++)
615                         builder.append(String.format("%.3g ", msgData.attribs[j][i * 4 + k]));
616                     if (j < maxAttrib - 1)
617                         builder.append("|| ");
618                 }
619                 builder.append('\n');
620             }
621             mText.setText(builder.toString());
622             tabFolder.setSelection(tabItemText);
623         }
624     }
625 
hookSelectionChanged()626     private void hookSelectionChanged() {
627         mViewer.addSelectionChangedListener(new ISelectionChangedListener() {
628             @Override
629             public void selectionChanged(SelectionChangedEvent event) {
630                 StructuredSelection selection = (StructuredSelection) event
631                         .getSelection();
632                 if (null == selection)
633                     return;
634                 MessageData msgData = (MessageData) selection.getFirstElement();
635                 messageDataSelected(msgData);
636             }
637         });
638     }
639 
showError(final Exception e)640     public void showError(final Exception e) {
641         mViewer.getControl().getDisplay().syncExec(new Runnable() {
642             @Override
643             public void run() {
644                 MessageDialog.openError(mViewer.getControl().getShell(),
645                         "GL ES 2.0 Debugger Client", e.getMessage());
646             }
647         });
648     }
649 
650     /**
651      * Passing the focus request to the viewer's control.
652      */
653     @Override
setFocus()654     public void setFocus() {
655         mViewer.getControl().setFocus();
656     }
657 
658     @Override
run()659     public void run() {
660         int newMessages = 0;
661 
662         boolean shaderEditorUpdate = false;
663         while (mRunning) {
664             final Message oriMsg = messageQueue.removeCompleteMessage(0);
665             if (oriMsg == null && !messageQueue.isRunning())
666                 break;
667             if (newMessages > 60 || (newMessages > 0 && null == oriMsg)) {
668                 newMessages = 0;
669                 if (current != null && current.uiUpdate)
670                     getSite().getShell().getDisplay().syncExec(new Runnable() {
671                         @Override
672                         public void run() {
673                             if (mFrameScale.getSelection() == current.frameCount() - 1 ||
674                                     mFrameScale.getSelection() == current.frameCount() - 2)
675                             {
676                                 mViewer.refresh(false);
677                                 if (mActionAutoScroll.isChecked())
678                                     mViewer.getList().setSelection(
679                                             mViewer.getList().getItemCount() - 1);
680                             }
681                             setMaxFrameCount(current.frameCount());
682                         }
683                     });
684                 current.uiUpdate = false;
685 
686                 if (shaderEditorUpdate)
687                     this.getSite().getShell().getDisplay().syncExec(new Runnable() {
688                         @Override
689                         public void run() {
690                             mShaderEditor.updateUI();
691                         }
692                     });
693                 shaderEditorUpdate = false;
694             }
695             if (null == oriMsg) {
696                 try {
697                     Thread.sleep(1);
698                     continue;
699                 } catch (InterruptedException e) {
700                     showError(e);
701                 }
702             }
703             DebugContext debugContext = debugContexts.get(oriMsg.getContextId());
704             if (debugContext == null) {
705                 debugContext = new DebugContext(oriMsg.getContextId());
706                 debugContexts.put(oriMsg.getContextId(), debugContext);
707             }
708             debugContext.processMessage(oriMsg);
709             shaderEditorUpdate |= debugContext.currentContext.serverShader.uiUpdate;
710             debugContext.currentContext.serverShader.uiUpdate = false;
711             if (current == null && debugContext.frameCount() > 0)
712                 changeContext(debugContext);
713             newMessages++;
714         }
715         if (mRunning)
716             connectDisconnect(); // error occurred, disconnect
717     }
718 
setMaxFrameCount(int frameCount)719     private void setMaxFrameCount(int frameCount) {
720         mFrameScale.setMaximum(frameCount);
721         mFrameNumberspinner.setMaximum(frameCount);
722     }
723 
setSelectedFrame(int frameNumber)724     private void setSelectedFrame(int frameNumber) {
725         mFrameScale.setSelection(frameNumber);
726         mFrameNumberspinner.setSelection(frameNumber);
727     }
728 
729     /** can be called from non-UI thread */
changeContext(final DebugContext newContext)730     void changeContext(final DebugContext newContext) {
731         getSite().getShell().getDisplay().syncExec(new Runnable() {
732             @Override
733             public void run() {
734                 current = newContext;
735                 if (current != null) {
736                     setMaxFrameCount(current.frameCount());
737 
738                     int frame = Math.min(mFrameScale.getSelection(), current.frameCount() - 1);
739                     if (frame < 0) {
740                         frame = 0;
741                     }
742                     setSelectedFrame(frame);
743                     mViewer.setInput(current.getFrame(frame));
744                     mActionContext.setText("Context: 0x" + Integer.toHexString(current.contextId));
745                 } else {
746                     setMaxFrameCount(1);
747                     setSelectedFrame(0);
748 
749                     mViewer.setInput(null);
750                     mActionContext.setText("Context: 0x");
751                 }
752                 mShaderEditor.updateUI();
753                 getViewSite().getActionBars().getToolBarManager().update(true);
754             }
755         });
756     }
757 }
758