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 24 import org.eclipse.jface.dialogs.InputDialog; 25 import org.eclipse.jface.window.Window; 26 import org.eclipse.swt.SWT; 27 import org.eclipse.swt.custom.ScrolledComposite; 28 import org.eclipse.swt.events.SelectionEvent; 29 import org.eclipse.swt.events.SelectionListener; 30 import org.eclipse.swt.graphics.Point; 31 import org.eclipse.swt.layout.FillLayout; 32 import org.eclipse.swt.layout.GridLayout; 33 import org.eclipse.swt.layout.RowLayout; 34 import org.eclipse.swt.widgets.Button; 35 import org.eclipse.swt.widgets.Composite; 36 import org.eclipse.swt.widgets.Group; 37 import org.eclipse.swt.widgets.Shell; 38 39 import java.io.IOException; 40 41 public class BreakpointOption extends ScrolledComposite implements SelectionListener, 42 ProcessMessage { 43 44 GLFramesView mGLFramesView; 45 Button[] buttonsBreak = new Button[Function.values().length]; 46 /** cache of buttonsBreak[Function.getNumber()].getSelection */ 47 boolean[] breakpoints = new boolean[Function.values().length]; 48 49 BreakpointOption(GLFramesView view, Composite parent) { 50 super(parent, SWT.NO_BACKGROUND | SWT.V_SCROLL | SWT.H_SCROLL); 51 mGLFramesView = view; 52 53 Composite composite = new Composite(this, 0); 54 GridLayout layout = new GridLayout(); 55 layout.numColumns = 4; 56 composite.setLayout(layout); 57 this.setLayout(new FillLayout()); 58 59 for (int i = 0; i < Function.values().length; i++) { 60 Group group = new Group(composite, 0); 61 group.setLayout(new RowLayout()); 62 group.setText(Function.values()[i].toString()); 63 Button btn = new Button(group, SWT.CHECK); 64 btn.addSelectionListener(this); 65 btn.setText("Break"); 66 btn.setSelection(false); 67 breakpoints[Function.values()[i].getNumber()] = btn.getSelection(); 68 buttonsBreak[Function.values()[i].getNumber()] = btn; 69 } 70 71 Point size = composite.computeSize(SWT.DEFAULT, SWT.DEFAULT); 72 composite.setSize(size); 73 this.setContent(composite); 74 this.setExpandHorizontal(true); 75 this.setExpandVertical(true); 76 this.setMinSize(size); 77 this.layout(); 78 } 79 80 void setBreakpoint(final int contextId, final Function function, final boolean enabled) { 81 Message.Builder builder = Message.newBuilder(); 82 builder.setContextId(contextId); 83 builder.setType(Type.Response); 84 builder.setExpectResponse(false); 85 builder.setFunction(Function.SETPROP); 86 builder.setProp(Prop.ExpectResponse); 87 builder.setArg0(function.getNumber()); 88 builder.setArg1(enabled ? 1 : 0); 89 mGLFramesView.messageQueue.addCommand(builder.build()); 90 breakpoints[function.getNumber()] = enabled; 91 } 92 93 @Override 94 public void widgetSelected(SelectionEvent e) { 95 Button btn = (Button) e.widget; 96 Group group = (Group) btn.getParent(); 97 int contextId = 0; 98 if (mGLFramesView.current != null) 99 contextId = mGLFramesView.current.contextId; 100 setBreakpoint(contextId, Function.valueOf(group.getText()), btn.getSelection()); 101 } 102 103 @Override 104 public void widgetDefaultSelected(SelectionEvent e) { 105 } 106 107 private Function lastFunction = Function.NEG; 108 109 @Override 110 public boolean processMessage(final MessageQueue queue, final Message msg) throws IOException { 111 if (!breakpoints[msg.getFunction().getNumber()]) 112 return false; 113 // use DefaultProcessMessage just to register the GL call 114 // but do not send response 115 final int contextId = msg.getContextId(); 116 if (msg.getType() == Type.BeforeCall || msg.getType() == Type.AfterCall) 117 queue.defaultProcessMessage(msg, true, false); 118 final Message.Builder builder = Message.newBuilder(); 119 builder.setContextId(contextId); 120 builder.setType(Type.Response); 121 builder.setExpectResponse(true); 122 final Shell shell = mGLFramesView.getViewSite().getShell(); 123 final boolean send[] = new boolean[1]; 124 shell.getDisplay().syncExec(new Runnable() { 125 @Override 126 public void run() { 127 String call = MessageFormatter.format(msg, false); 128 call = call.substring(0, call.indexOf("(")) + ' ' + 129 msg.getFunction() + call.substring(call.indexOf("(")); 130 if (msg.hasData() && msg.getFunction() == Function.glShaderSource) 131 { 132 int index = call.indexOf("string=") + 7; 133 String ptr = call.substring(index, call.indexOf(',', index)); 134 call = call.replace(ptr, '"' + msg.getData().toStringUtf8() + '"'); 135 } 136 if (msg.getType() == Type.AfterCall) 137 { 138 call = "skip " + call; 139 builder.setFunction(Function.SKIP); 140 } 141 else if (msg.getType() == Type.BeforeCall) 142 { 143 call = "continue " + call; 144 builder.setFunction(Function.CONTINUE); 145 } 146 else 147 { 148 assert msg.getType() == Type.AfterGeneratedCall; 149 assert msg.getFunction() == lastFunction; 150 call = "skip " + call; 151 builder.setFunction(Function.SKIP); 152 } 153 InputDialog inputDialog = new InputDialog(shell, 154 msg.getFunction().toString() + " " + msg.getType().toString(), 155 "(s)kip, (c)continue, (r)emove bp or glFunction(...)", 156 call, null); 157 if (Window.OK == inputDialog.open()) 158 { 159 String s = inputDialog.getValue().substring(0, 1).toLowerCase(); 160 if (s.startsWith("s")) 161 { 162 builder.setFunction(Function.SKIP); 163 // AfterCall is skipped, so push BeforeCall to complete 164 if (queue.getPartialMessage(contextId) != null) 165 queue.completePartialMessage(contextId); 166 } 167 else if (s.startsWith("c")) 168 builder.setFunction(Function.CONTINUE); 169 else if (s.startsWith("r")) 170 { 171 Button btn = buttonsBreak[msg.getFunction().getNumber()]; 172 btn.setSelection(false); 173 setBreakpoint(msg.getContextId(), msg.getFunction(), false); 174 builder.setExpectResponse(false); 175 } 176 else 177 { 178 MessageParserEx.instance.parse(builder, inputDialog.getValue()); 179 lastFunction = builder.getFunction(); 180 builder.setExpectResponse(true); 181 // AfterCall is skipped, so push BeforeCall to complete 182 if (queue.getPartialMessage(contextId) != null) 183 queue.completePartialMessage(contextId); 184 } 185 } 186 // else defaults to continue BeforeCall and skip AfterCall 187 } 188 }); 189 queue.sendMessage(builder.build()); 190 return true; 191 } 192 } 193