From 5e4b039d56d93520f36f0c775e3cdc95c2f03e04 Mon Sep 17 00:00:00 2001 From: gdisirio Date: Fri, 24 Aug 2012 09:19:05 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@4621 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- tools/eclipse/debug_support/.classpath | 7 + tools/eclipse/debug_support/.project | 28 + .../.settings/org.eclipse.jdt.core.prefs | 8 + tools/eclipse/debug_support/META-INF/MANIFEST.MF | 14 + tools/eclipse/debug_support/build.properties | 7 + tools/eclipse/debug_support/contexts.xml | 12 + tools/eclipse/debug_support/icons/sample.gif | Bin 0 -> 983 bytes tools/eclipse/debug_support/plugin.xml | 38 ++ .../tools/eclipse/debug/activator/Activator.java | 68 +++ .../tools/eclipse/debug/utils/DebugProxy.java | 562 ++++++++++++++++++++ .../eclipse/debug/utils/DebugProxyException.java | 14 + .../tools/eclipse/debug/utils/HexUtils.java | 94 ++++ .../tools/eclipse/debug/views/ChibiView.java | 565 +++++++++++++++++++++ .../src/org/eclipse/wb/swt/ResourceManager.java | 415 +++++++++++++++ .../src/org/eclipse/wb/swt/SWTResourceManager.java | 447 ++++++++++++++++ 15 files changed, 2279 insertions(+) create mode 100644 tools/eclipse/debug_support/.classpath create mode 100644 tools/eclipse/debug_support/.project create mode 100644 tools/eclipse/debug_support/.settings/org.eclipse.jdt.core.prefs create mode 100644 tools/eclipse/debug_support/META-INF/MANIFEST.MF create mode 100644 tools/eclipse/debug_support/build.properties create mode 100644 tools/eclipse/debug_support/contexts.xml create mode 100644 tools/eclipse/debug_support/icons/sample.gif create mode 100644 tools/eclipse/debug_support/plugin.xml create mode 100644 tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/activator/Activator.java create mode 100644 tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxy.java create mode 100644 tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxyException.java create mode 100644 tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/HexUtils.java create mode 100644 tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/views/ChibiView.java create mode 100644 tools/eclipse/debug_support/src/org/eclipse/wb/swt/ResourceManager.java create mode 100644 tools/eclipse/debug_support/src/org/eclipse/wb/swt/SWTResourceManager.java diff --git a/tools/eclipse/debug_support/.classpath b/tools/eclipse/debug_support/.classpath new file mode 100644 index 000000000..8a8f1668c --- /dev/null +++ b/tools/eclipse/debug_support/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tools/eclipse/debug_support/.project b/tools/eclipse/debug_support/.project new file mode 100644 index 000000000..eb8a22dc0 --- /dev/null +++ b/tools/eclipse/debug_support/.project @@ -0,0 +1,28 @@ + + + Tool Debug Support + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/tools/eclipse/debug_support/.settings/org.eclipse.jdt.core.prefs b/tools/eclipse/debug_support/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..c63fde790 --- /dev/null +++ b/tools/eclipse/debug_support/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +#Fri Jul 01 10:57:07 CEST 2011 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/tools/eclipse/debug_support/META-INF/MANIFEST.MF b/tools/eclipse/debug_support/META-INF/MANIFEST.MF new file mode 100644 index 000000000..2908664cf --- /dev/null +++ b/tools/eclipse/debug_support/META-INF/MANIFEST.MF @@ -0,0 +1,14 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: ChibiOS-RT_Debug_Support +Bundle-SymbolicName: org.chibios.tools.eclipse.debug; singleton:=true +Bundle-Version: 1.1.0 +Bundle-Activator: org.chibios.tools.eclipse.debug.activator.Activator +Bundle-Vendor: chibios.org +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.eclipse.cdt.debug.mi.core, + org.eclipse.debug.ui, + org.eclipse.cdt.debug.core +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Bundle-ClassPath: . diff --git a/tools/eclipse/debug_support/build.properties b/tools/eclipse/debug_support/build.properties new file mode 100644 index 000000000..255e23b61 --- /dev/null +++ b/tools/eclipse/debug_support/build.properties @@ -0,0 +1,7 @@ +source.. = src/ +output.. = bin/ +bin.includes = plugin.xml,\ + META-INF/,\ + .,\ + icons/,\ + contexts.xml diff --git a/tools/eclipse/debug_support/contexts.xml b/tools/eclipse/debug_support/contexts.xml new file mode 100644 index 000000000..02e26e45c --- /dev/null +++ b/tools/eclipse/debug_support/contexts.xml @@ -0,0 +1,12 @@ + + + This is the context help for the sample view with a table viewer. It was generated by a PDE template. + + + + + + + + + diff --git a/tools/eclipse/debug_support/icons/sample.gif b/tools/eclipse/debug_support/icons/sample.gif new file mode 100644 index 000000000..34fb3c9d8 Binary files /dev/null and b/tools/eclipse/debug_support/icons/sample.gif differ diff --git a/tools/eclipse/debug_support/plugin.xml b/tools/eclipse/debug_support/plugin.xml new file mode 100644 index 000000000..b6102196c --- /dev/null +++ b/tools/eclipse/debug_support/plugin.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/activator/Activator.java b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/activator/Activator.java new file mode 100644 index 000000000..0e3b8d1e9 --- /dev/null +++ b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/activator/Activator.java @@ -0,0 +1,68 @@ +package org.chibios.tools.eclipse.debug.activator; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.chibios.tools.eclipse.debug"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext + * ) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext + * ) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + + /** + * Returns an image descriptor for the image file at the given plug-in + * relative path + * + * @param path + * the path + * @return the image descriptor + */ + public static ImageDescriptor getImageDescriptor(String path) { + return imageDescriptorFromPlugin(PLUGIN_ID, path); + } +} diff --git a/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxy.java b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxy.java new file mode 100644 index 000000000..b673d9610 --- /dev/null +++ b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxy.java @@ -0,0 +1,562 @@ +package org.chibios.tools.eclipse.debug.utils; + +import java.util.HashMap; +import java.util.LinkedHashMap; + +import org.eclipse.cdt.debug.core.cdi.model.ICDITarget; +import org.eclipse.cdt.debug.internal.core.model.CDebugTarget; +import org.eclipse.cdt.debug.mi.core.MIException; +import org.eclipse.cdt.debug.mi.core.MIFormat; +import org.eclipse.cdt.debug.mi.core.MISession; +import org.eclipse.cdt.debug.mi.core.cdi.model.Target; +import org.eclipse.cdt.debug.mi.core.command.CommandFactory; +import org.eclipse.cdt.debug.mi.core.command.MIDataEvaluateExpression; +import org.eclipse.cdt.debug.mi.core.command.MIDataReadMemory; +import org.eclipse.cdt.debug.mi.core.output.MIDataEvaluateExpressionInfo; +import org.eclipse.cdt.debug.mi.core.output.MIDataReadMemoryInfo; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.model.IDebugTarget; + +@SuppressWarnings("restriction") +public class DebugProxy { + + private CommandFactory cmd_factory; + private MISession mi_session; + + protected final static String[] threadStates = { + "READY", + "CURRENT", + "SUSPENDED", + "WTSEM", + "WTMTX", + "WTCOND", + "SLEEPING", + "WTEXIT", + "WTOREVT", + "WTANDEVT", + "SNDMSGQ", + "SNDMSG", + "WTMSG", + "WTQUEUE", + "FINAL" + }; + + private void getSession(CDebugTarget target) + throws DebugProxyException { + ICDITarget[] targets = target.getCDISession().getTargets(); + ICDITarget cdi_target = null; + for (int i = 0; i < targets.length; i++) { + if (targets[i] instanceof Target) { + cdi_target = targets[i]; + break; + } + } + if (cdi_target == null) + throw new DebugProxyException("no CDI session found"); + mi_session = ((Target)cdi_target).getMISession(); + cmd_factory = mi_session.getCommandFactory(); + } + + public DebugProxy() + throws DebugProxyException { + IDebugTarget[] targets = DebugPlugin.getDefault().getLaunchManager().getDebugTargets(); + for (IDebugTarget target:targets) { + if(target instanceof CDebugTarget) { + getSession((CDebugTarget)target); + return; + } + } + } + + public DebugProxy(CDebugTarget target) + throws DebugProxyException { + getSession(target); + } + + public String evaluateExpression(String expression) + throws DebugProxyException { + if (mi_session.getMIInferior().isRunning()) + return null; + MIDataEvaluateExpression expr = cmd_factory.createMIDataEvaluateExpression(expression); + try { + mi_session.postCommand(expr); + MIDataEvaluateExpressionInfo info = expr.getMIDataEvaluateExpressionInfo(); + if (info != null) + return info.getExpression(); + } catch (MIException e) {} + throw new DebugProxyException("error evaluating the expression: '" + + expression + "'"); + } + + public long scanStack(long base, long end, long pattern) + throws DebugProxyException { + if (mi_session.getMIInferior().isRunning()) + return -1; + if (end > base) { + MIDataReadMemory mem = cmd_factory.createMIDataReadMemory(0, + Long.toString(base), + MIFormat.HEXADECIMAL, + 4, + 1, + (int)(end - base), + '.'); + try { + mi_session.postCommand(mem); + MIDataReadMemoryInfo info = mem.getMIDataReadMemoryInfo(); + if (info != null) { + long[] data = info.getMemories()[0].getData(); + int i = 0; + while ((i < data.length) && (data[i] == pattern)) + i++; + return i * 4; + } + } catch (MIException e) {} + throw new DebugProxyException("error reading memory at " + + base); + } + return 0; + } + + public String readCString(long address, int max) + throws DebugProxyException { + if (mi_session.getMIInferior().isRunning()) + return null; + MIDataReadMemory mem = cmd_factory.createMIDataReadMemory(0, + Long.toString(address), + MIFormat.HEXADECIMAL, + 1, + 1, + max, + '.'); + try { + mi_session.postCommand(mem); + MIDataReadMemoryInfo info = mem.getMIDataReadMemoryInfo(); + if (info != null) { + String s = info.getMemories()[0].getAscii(); + int i = s.indexOf('.'); + if (i >= 0) + return s.substring(0, s.indexOf('.')); + else + return s; + } + } catch (MIException e) {} + throw new DebugProxyException("error reading memory at " + + address); + } + + /** + * @brief Return the list of threads. + * @details The threads list is fetched from memory by scanning the + * registry. + * + * @return A @p LinkedHashMap object whose keys are the threads addresses + * as decimal strings, the value is an @p HashMap of the thread + * fields: + * - stack + * - stklimit + * - name + * - state + * - state_s + * - flags + * - prio + * - refs + * - time + * - wtobjp + * . + * Missing fields are set to "-". + * @retval null If the debugger encountered an error or + * the target is running. + * + * @throws DebugProxyException If the debugger is active but the registry + * is not found, not initialized or corrupted. + */ + public LinkedHashMap> readThreads() + throws DebugProxyException { + // rlist structure address. + String rlist; + try { + rlist = evaluateExpression("(uint32_t)&rlist"); + if (rlist == null) + return null; + } catch (DebugProxyException e) { + throw new DebugProxyException("ChibiOS/RT not found on target"); + } catch (Exception e) { + return null; + } + + // Scanning registry. + LinkedHashMap> lhm = + new LinkedHashMap>(10); + String current = rlist; + String previous = rlist; + while (true) { + + // Fetching next thread in the registry (newer link). This fetch fails + // if the register is not enabled in the kernel and the p_newer field + // does not exist. + try { + current = evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_newer"); + } catch (DebugProxyException e1) { + throw new DebugProxyException("ChibiOS/RT registry not enabled in kernel"); + } + + // This can happen if the kernel is not initialized yet or if the + // registry is corrupted. + if (current.compareTo("0") == 0) + throw new DebugProxyException("ChibiOS/RT registry integrity check failed, NULL pointer"); + + // TODO: integrity check on the pointer value (alignment, range). + + // The previous thread in the list is fetched as a integrity check. + String older = evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_older"); + if (older.compareTo("0") == 0) + throw new DebugProxyException("ChibiOS/RT registry integrity check failed, NULL pointer"); + if (previous.compareTo(older) != 0) + throw new DebugProxyException("ChibiOS/RT registry integrity check failed, double linked list violation"); + + // End of the linked list condition. + if (current.compareTo(rlist) == 0) + break; + + // Hash of threads fields. + HashMap map = new HashMap(16); + + // Fetch of the various fields in the Thread structure. Some fields + // are optional so are placed within try-catch. + long stklimit; + try { + stklimit = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_stklimit")); + map.put("stklimit", Long.toString(stklimit)); + } catch (DebugProxyException e) { + map.put("stklimit", "-"); + stklimit = -1; + } + + long stack; + try { + stack = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_ctx.r13")); + map.put("stack", Long.toString(stack)); + } catch (DebugProxyException e) { + try { + stack = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_ctx.sp")); + map.put("stack", Long.toString(stack)); + } catch (DebugProxyException ex) { + map.put("stack", "-"); + stack = -1; + } + } + + if ((stklimit < 0) || (stack < 0)) + map.put("stkunused", "-"); + else { + if ((stack < 0) || (stack < stklimit)) + map.put("stkunused", "overflow"); + else { + long stkunused = scanStack(stklimit, stack, 0x55555555); + map.put("stkunused", Long.toString(stkunused)); + } + } + + long n; + try { + n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_name")); + if (n == 0) + map.put("name", ""); + else + map.put("name", readCString(n, 16)); + } catch (DebugProxyException e) { + map.put("name", "-"); + } + + n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_state")); + map.put("state", Long.toString(n)); + if ((n >= 0) && (n < threadStates.length)) { + map.put("state_s", threadStates[(int)n]); + } + else + map.put("state_s", "unknown"); + + n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_flags")); + map.put("flags", Long.toString(n)); + + n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_prio")); + map.put("prio", Long.toString(n)); + + try { + n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_refs")); + map.put("refs", Long.toString(n)); + } catch (DebugProxyException e) { + map.put("refs", "-"); + } + + try { + n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_time")); + map.put("time", Long.toString(n)); + } catch (DebugProxyException e) { + map.put("time", "-"); + } + + try { + n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_u.wtobjp")); + map.put("wtobjp", Long.toString(n)); + } catch (DebugProxyException e) { + map.put("wtobjp", "-"); + } + + // Inserting the new thread map into the threads list. + lhm.put(current, map); + + previous = current; + } + return lhm; + } + + /** + * @brief Return the list of timers. + * @details The timers list is fetched from memory by scanning the + * @p vtlist structure. + * + * @return A @p LinkedHashMap object whose keys are the timers addresses + * as decimal strings, the value is an @p HashMap of the timers + * fields: + * - delta + * - func + * - par + * . + * @retval null If the debugger encountered an error or + * the target is running. + * + * @throws DebugProxyException If the debugger is active but the structure + * @p vtlist is not found, not initialized or + * corrupted. + */ + public LinkedHashMap> readTimers() + throws DebugProxyException { + // Delta list structure address. + String vtlist; + try { + vtlist = evaluateExpression("(uint32_t)&vtlist"); + if (vtlist == null) + return null; + } catch (DebugProxyException e) { + throw new DebugProxyException("ChibiOS/RT not found on target"); + } catch (Exception e) { + return null; + } + + // Scanning delta list. + LinkedHashMap> lhm = + new LinkedHashMap>(10); + String current = vtlist; + String previous = vtlist; + while (true) { + + // Fetching next timer in the delta list (vt_next link). + current = evaluateExpression("(uint32_t)((VirtualTimer *)" + current + ")->vt_next"); + + // This can happen if the kernel is not initialized yet or if the + // delta list is corrupted. + if (current.compareTo("0") == 0) + throw new DebugProxyException("ChibiOS/RT delta list integrity check failed, NULL pointer"); + + // TODO: integrity check on the pointer value (alignment, range). + + // The previous timer in the delta list is fetched as a integrity check. + String prev = evaluateExpression("(uint32_t)((VirtualTimer *)" + current + ")->vt_prev"); + if (prev.compareTo("0") == 0) + throw new DebugProxyException("ChibiOS/RT delta list integrity check failed, NULL pointer"); + if (previous.compareTo(prev) != 0) + throw new DebugProxyException("ChibiOS/RT delta list integrity check failed, double linked list violation"); + + // End of the linked list condition. + if (current.compareTo(vtlist) == 0) + break; + + // Hash of timers fields. + HashMap map = new HashMap(16); + + // Fetch of the various fields in the Thread structure. Some fields + // are optional so are placed within try-catch. + long n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((VirtualTimer *)" + current + ")->vt_time")); + map.put("delta", Long.toString(n)); + + n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((VirtualTimer *)" + current + ")->vt_func")); + map.put("func", Long.toString(n)); + + n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((VirtualTimer *)" + current + ")->vt_par")); + map.put("par", Long.toString(n)); + + // Inserting the new thread map into the threads list. + lhm.put(current, map); + + previous = current; + } + return lhm; + } + + /** + * @brief Return the list of trace buffer entries. + * @details The trace buffer is fetched from memory by scanning the + * @p dbg_trace_buffer array. + * + * @return A @p LinkedHashMap object whose keys are the timers addresses + * as decimal strings, the value is an @p HashMap of the timers + * fields: + * - time + * - tp + * - wtobjp + * - state + * - state_s + * . + * @retval null If the debugger encountered an error or + * the target is running. + * + * @throws DebugProxyException If the debugger is active but the structure + * @p dbg_trace_buffer is not found, not + * initialized or corrupted. + */ + public LinkedHashMap> readTraceBuffer() + throws DebugProxyException { + + // Trace buffer size. + String s; + try { + s = evaluateExpression("(uint32_t)dbg_trace_buffer.tb_size"); + if (s == null) + return null; + } catch (DebugProxyException e) { + throw new DebugProxyException("trace buffer not found on target"); + } catch (Exception e) { + return null; + } + + int tbsize = (int)HexUtils.parseNumber(s); + int tbrecsize = (int)HexUtils.parseNumber(evaluateExpression("(uint32_t)sizeof (ch_swc_event_t)")); + long tbstart = HexUtils.parseNumber(evaluateExpression("(uint32_t)dbg_trace_buffer.tb_buffer")); + long tbend = HexUtils.parseNumber(evaluateExpression("(uint32_t)&dbg_trace_buffer.tb_buffer[" + tbsize + "]")); + long tbptr = HexUtils.parseNumber(evaluateExpression("(uint32_t)dbg_trace_buffer.tb_ptr")); + + // Scanning the trace buffer from the oldest event to the newest. + LinkedHashMap> lhm = + new LinkedHashMap>(64); + int n = tbsize; + int i = -tbsize + 1; + while (n > 0) { + // Hash of timers fields. + HashMap map = new HashMap(16); + + String time = evaluateExpression("(uint32_t)(((ch_swc_event_t *)" + tbptr + ")->se_time)"); + map.put("time", time); + + String tp = evaluateExpression("(uint32_t)(((ch_swc_event_t *)" + tbptr + ")->se_tp)"); + map.put("tp", tp); + + String wtobjp = evaluateExpression("(uint32_t)(((ch_swc_event_t *)" + tbptr + ")->se_wtobjp)"); + map.put("wtobjp", wtobjp); + + long state = HexUtils.parseNumber(evaluateExpression("(uint32_t)(((ch_swc_event_t *)" + tbptr + ")->se_state)")); + map.put("state", Long.toString(state)); + if ((state >= 0) && (state < threadStates.length)) + map.put("state_s", threadStates[(int)state]); + else + map.put("state_s", "unknown"); + + // Inserting the new event map into the events list. + if (tp.compareTo("0") != 0) + lhm.put(Integer.toString(i), map); + + tbptr += tbrecsize; + if (tbptr >= tbend) + tbptr = tbstart; + n--; + i++; + } + return lhm; + } + + /** + * @brief Return the list of the system global variables. + * + * @return A @p LinkedHashMap object whose keys are the variable names and + * the values are the variable values. + * + * @retval null If the debugger encountered an error or + * the target is running. + * + * @throws DebugProxyException If the debugger is active but the structure + * @p dbg_trace_buffer is not found, not + * initialized or corrupted. + */ + public LinkedHashMap readGlobalVariables() + throws DebugProxyException { + + LinkedHashMap map = new LinkedHashMap(16); + + try { + String vt_systime = evaluateExpression("(uint32_t)vtlist.vt_systime"); + if (vt_systime == null) + return null; + map.put("vt_systime", vt_systime); + } catch (DebugProxyException e) { + throw new DebugProxyException("ChibiOS/RT not found on target"); + } catch (Exception e) { + return null; + } + + try { + long r_current = HexUtils.parseNumber(evaluateExpression("(uint32_t)rlist.r_current")); + if (r_current != 0) { + String name; + try { + long n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + r_current + ")->p_name")); + if (n == 0) + name = ""; + else + name = readCString(n, 16); + } catch (DebugProxyException e) { + name = "-"; + } + map.put("r_current", HexUtils.dword2HexString((int)r_current) + " \"" + name + "\""); + } + else + map.put("r_current", "0"); + } catch (DebugProxyException e) {} + + try { + String r_preempt = evaluateExpression("(uint32_t)rlist.r_preempt"); + map.put("r_preempt", r_preempt); + } catch (DebugProxyException e) {} + + try { + Long addr = HexUtils.parseNumber(evaluateExpression("(uint32_t)dbg_panic_msg")); + if (addr == 0) + map.put("dbg_panic_msg", ""); + else + map.put("dbg_panic_msg", readCString(addr, 32)); + } catch (DebugProxyException e) { + map.put("dbg_panic_msg", ""); + } + + try { + Long isr_cnt = HexUtils.parseNumber(evaluateExpression("(uint32_t)dbg_isr_cnt")); + if (isr_cnt == 0) + map.put("dbg_isr_cnt", "not within ISR"); + else + map.put("dbg_isr_cnt", "within ISR"); + } catch (DebugProxyException e) { + map.put("dbg_isr_cnt", ""); + } + + try { + Long lock_cnt = HexUtils.parseNumber(evaluateExpression("(uint32_t)dbg_lock_cnt")); + if (lock_cnt == 0) + map.put("dbg_lock_cnt", "not within lock"); + else + map.put("dbg_lock_cnt", "within lock"); + } catch (DebugProxyException e) { + map.put("dbg_lock_cnt", ""); + } + + return map; + } +} diff --git a/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxyException.java b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxyException.java new file mode 100644 index 000000000..ce4a0331f --- /dev/null +++ b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxyException.java @@ -0,0 +1,14 @@ +package org.chibios.tools.eclipse.debug.utils; + +public class DebugProxyException extends Exception { + + private static final long serialVersionUID = 6860700758297226746L; + + public DebugProxyException() { + super("Debug Proxy Exception"); + } + + public DebugProxyException(String s) { + super(s); + } +} diff --git a/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/HexUtils.java b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/HexUtils.java new file mode 100644 index 000000000..54727e573 --- /dev/null +++ b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/HexUtils.java @@ -0,0 +1,94 @@ +package org.chibios.tools.eclipse.debug.utils; + +public class HexUtils { + + protected final static String[] hexChars = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" + }; + + static public boolean isHexChar(char c) { + + if (Character.isDigit(c) || (c >= 'a') && (c <= 'f') || + (c >= 'A') && (c <= 'F')) + return true; + return false; + } + + static public boolean isHexString(String hstring) { + int l = hstring.length(); + + if ((l & 1) == 1) + return false; + + for (int i = 0; i < l; i++) + if (!isHexChar(hstring.charAt(i))) + return false; + return true; + } + + static public byte[] hexStringToByteArray(String hstring) { + + if (!isHexString(hstring)) + throw new NumberFormatException("not a hex string"); + + byte[] result = new byte[hstring.length() / 2]; + + for (int i = 0; i < hstring.length(); i += 2) { + String toParse = hstring.substring(i, i + 2); + + result[i / 2] = (byte)Integer.parseInt(toParse, 16); + } + return result; + } + + static public byte hexStringToByte(String hstring) { + + if (hstring.length() != 2) + throw new NumberFormatException("not a byte hex string"); + + return (byte)Integer.parseInt(hstring, 16); + } + + static public String byteArrayToHexString(byte[] data) { + StringBuffer out = new StringBuffer(data.length * 2); + + for (int i = 0; i < data.length; i++) { + out.append(hexChars[(data[i] >> 4) & 15]); + out.append(hexChars[data[i] & 15]); + } + return out.toString(); + } + + static public String byte2HexString(int b) { + + return hexChars[(b >> 4) & 15] + hexChars[b & 15]; + } + + static public String word2HexString(int w) { + + return hexChars[(w >> 12) & 15] + + hexChars[(w >> 8) & 15] + + hexChars[(w >> 4) & 15] + + hexChars[w & 15]; + } + + static public String dword2HexString(int w) { + + return hexChars[(w >> 28) & 15] + + hexChars[(w >> 24) & 15] + + hexChars[(w >> 20) & 15] + + hexChars[(w >> 16) & 15] + + hexChars[(w >> 12) & 15] + + hexChars[(w >> 8) & 15] + + hexChars[(w >> 4) & 15] + + hexChars[w & 15]; + } + + static public long parseNumber(String s) { + + if (s.toLowerCase().startsWith("0x")) + return Long.parseLong(s.substring(2), 16); + return Long.parseLong(s); + } +} + diff --git a/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/views/ChibiView.java b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/views/ChibiView.java new file mode 100644 index 000000000..4cc7fd48e --- /dev/null +++ b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/views/ChibiView.java @@ -0,0 +1,565 @@ +package org.chibios.tools.eclipse.debug.views; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map.Entry; +import java.util.Set; + +import org.chibios.tools.eclipse.debug.utils.DebugProxy; +import org.chibios.tools.eclipse.debug.utils.DebugProxyException; +import org.chibios.tools.eclipse.debug.utils.HexUtils; + +import org.eclipse.ui.internal.IWorkbenchThemeConstants; +import org.eclipse.ui.part.*; +import org.eclipse.ui.themes.ITheme; +import org.eclipse.jface.action.*; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.ui.*; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CTabFolder; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.TableItem; + +import org.eclipse.debug.core.DebugEvent; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IDebugEventSetListener; +import org.eclipse.cdt.debug.internal.core.model.CDebugTarget; +import org.eclipse.wb.swt.SWTResourceManager; +import org.eclipse.wb.swt.ResourceManager; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; + +/** + * This sample class demonstrates how to plug-in a new workbench view. The view + * shows data obtained from the model. The sample creates a dummy model on the + * fly, but a real implementation would connect to the model available either in + * this or another plug-in (e.g. the workspace). The view is connected to the + * model using a content provider. + *

+ * The view uses a label provider to define how model objects should be + * presented in the view. Each view can present the same model objects using + * different labels and icons, if needed. Alternatively, a single label provider + * can be shared between views in order to ensure that objects of the same type + * are presented in the same way everywhere. + *

+ */ + +@SuppressWarnings("restriction") +public class ChibiView extends ViewPart implements IDebugEventSetListener { + + /** + * The ID of the view as specified by the extension. + */ + public static final String ID = "org.chibios.tools.eclipse.debug.views.ChibiView"; + + private CTabFolder tabFolder; + private CTabItem tbtmGlobal; + private CTabItem tbtmThreads; + private CTabItem tbtmTimers; + private CTabItem tbtmTraceBuffer; + + private Action refreshAction; + private Table threadsTable; + private Table timersTable; + + private DebugProxy debugger; + private Table tbTable; + private Table globalTable; + + private ITheme theme; + + private FocusAdapter focus = new FocusAdapter() { + @Override + public void focusLost(FocusEvent e) { + setInactive(); + } + @Override + public void focusGained(FocusEvent e) { + setActive(); + } + }; + + /** + * The constructor. + */ + public ChibiView() { + + theme = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme(); + } + + private void setActive() { + tabFolder.setSelectionBackground( + new org.eclipse.swt.graphics.Color[] { + theme.getColorRegistry().get(IWorkbenchThemeConstants.ACTIVE_TAB_BG_START), + theme.getColorRegistry().get(IWorkbenchThemeConstants.ACTIVE_TAB_BG_END) + }, + new int[] {100}, + true); + tabFolder.setSelectionForeground(theme.getColorRegistry().get(IWorkbenchThemeConstants.ACTIVE_TAB_TEXT_COLOR)); + } + + private void setInactive() { + tabFolder.setSelectionBackground( + new org.eclipse.swt.graphics.Color[] { + theme.getColorRegistry().get(IWorkbenchThemeConstants.INACTIVE_TAB_BG_START), + theme.getColorRegistry().get(IWorkbenchThemeConstants.INACTIVE_TAB_BG_END) + }, + new int[] {theme.getInt(IWorkbenchThemeConstants.ACTIVE_TAB_PERCENT)}, + true); + tabFolder.setSelectionForeground(theme.getColorRegistry().get(IWorkbenchThemeConstants.INACTIVE_TAB_TEXT_COLOR)); + } + + /** + * This is a callback that will allow us to create the viewer and initialize + * it. + */ + public void createPartControl(Composite parent) { + + tabFolder = new CTabFolder(parent, SWT.BORDER | SWT.BOTTOM); + tabFolder.setFont(theme.getFontRegistry().get(IWorkbenchThemeConstants.TAB_TEXT_FONT)); + tabFolder.setBackground(theme.getColorRegistry().get(IWorkbenchThemeConstants.INACTIVE_TAB_BG_END)); + tabFolder.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + setActive(); + tabFolder.getSelection().getControl().setFocus(); + } + }); + tabFolder.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + tabFolder.getSelection().getControl().setFocus(); + } + }); + setInactive(); + tabFolder.setSimple(false); + + tbtmGlobal = new CTabItem(tabFolder, SWT.NONE); + tbtmGlobal.setText("Global"); + + globalTable = new Table(tabFolder, SWT.FULL_SELECTION); + globalTable.addFocusListener(focus); + globalTable.setFont(SWTResourceManager.getFont("Courier New", 8, SWT.NORMAL)); + tbtmGlobal.setControl(globalTable); + globalTable.setHeaderVisible(true); + + TableColumn tblclmnGlobalHidden = new TableColumn(globalTable, SWT.RIGHT); + tblclmnGlobalHidden.setWidth(0); + tblclmnGlobalHidden.setText(""); + + TableColumn tblclmnGlobalVariableName = new TableColumn(globalTable, SWT.LEFT); + tblclmnGlobalVariableName.setWidth(150); + tblclmnGlobalVariableName.setText("Variable"); + + TableColumn tblclmnGlobalVariableValue = new TableColumn(globalTable, SWT.LEFT); + tblclmnGlobalVariableValue.setWidth(300); + tblclmnGlobalVariableValue.setText("Value"); + + tbtmThreads = new CTabItem(tabFolder, SWT.NONE); + tbtmThreads.setText("Threads"); + + threadsTable = new Table(tabFolder, SWT.FULL_SELECTION); + threadsTable.addFocusListener(focus); + tbtmThreads.setControl(threadsTable); + threadsTable.setFont(SWTResourceManager.getFont("Courier New", 8, SWT.NORMAL)); + threadsTable.setHeaderVisible(true); + + TableColumn tblclmnThreadAddress = new TableColumn(threadsTable, SWT.RIGHT); + tblclmnThreadAddress.setWidth(72); + tblclmnThreadAddress.setText("Address"); + + TableColumn tblclmnThreadLimit = new TableColumn(threadsTable, SWT.RIGHT); + tblclmnThreadLimit.setWidth(72); + tblclmnThreadLimit.setText("StkLimit"); + + TableColumn tblclmnThreadStack = new TableColumn(threadsTable, SWT.RIGHT); + tblclmnThreadStack.setWidth(72); + tblclmnThreadStack.setText("Stack"); + + TableColumn tblclmnThreadUsed = new TableColumn(threadsTable, SWT.RIGHT); + tblclmnThreadUsed.setWidth(72); + tblclmnThreadUsed.setText("StkUnused"); + + TableColumn tblclmnThreadName = new TableColumn(threadsTable, SWT.LEFT); + tblclmnThreadName.setWidth(144); + tblclmnThreadName.setText("Name"); + + TableColumn tblclmnThreadState = new TableColumn(threadsTable, SWT.RIGHT); + tblclmnThreadState.setWidth(72); + tblclmnThreadState.setText("State"); + + TableColumn tblclmnThreadFlags = new TableColumn(threadsTable, SWT.RIGHT); + tblclmnThreadFlags.setWidth(40); + tblclmnThreadFlags.setText("Flgs"); + + TableColumn tblclmnThreadPriority = new TableColumn(threadsTable, SWT.RIGHT); + tblclmnThreadPriority.setWidth(40); + tblclmnThreadPriority.setText("Prio"); + + TableColumn tblclmnThreadRefs = new TableColumn(threadsTable, SWT.RIGHT); + tblclmnThreadRefs.setWidth(40); + tblclmnThreadRefs.setText("Refs"); + + TableColumn tblclmnThreadTime = new TableColumn(threadsTable, SWT.RIGHT); + tblclmnThreadTime.setWidth(64); + tblclmnThreadTime.setText("Time"); + + TableColumn tblclmnThreadShared = new TableColumn(threadsTable, SWT.LEFT); + tblclmnThreadShared.setWidth(72); + tblclmnThreadShared.setText("Obj/Msg"); + + tbtmTimers = new CTabItem(tabFolder, SWT.NONE); + tbtmTimers.setText("Timers"); + + timersTable = new Table(tabFolder, SWT.FULL_SELECTION); + timersTable.addFocusListener(focus); + tbtmTimers.setControl(timersTable); + timersTable.setFont(SWTResourceManager.getFont("Courier New", 8, SWT.NORMAL)); + timersTable.setHeaderVisible(true); + + TableColumn tblclmnTimerAddress = new TableColumn(timersTable, SWT.RIGHT); + tblclmnTimerAddress.setWidth(72); + tblclmnTimerAddress.setText("Address"); + + TableColumn tblclmnTimerTime = new TableColumn(timersTable, SWT.RIGHT); + tblclmnTimerTime.setWidth(72); + tblclmnTimerTime.setText("Time"); + + TableColumn tblclmnTimerDelta = new TableColumn(timersTable, SWT.RIGHT); + tblclmnTimerDelta.setWidth(72); + tblclmnTimerDelta.setText("Delta"); + + TableColumn tblclmnTimerCallback = new TableColumn(timersTable, SWT.RIGHT); + tblclmnTimerCallback.setWidth(72); + tblclmnTimerCallback.setText("Callback"); + + TableColumn tblclmnTimerParameter = new TableColumn(timersTable, SWT.LEFT); + tblclmnTimerParameter.setWidth(72); + tblclmnTimerParameter.setText("Param"); + + tbtmTraceBuffer = new CTabItem(tabFolder, SWT.NONE); + tbtmTraceBuffer.setText("TraceBuffer"); + + tbTable = new Table(tabFolder, SWT.FULL_SELECTION); + tbTable.addFocusListener(focus); + tbTable.setFont(SWTResourceManager.getFont("Courier New", 8, SWT.NORMAL)); + tbtmTraceBuffer.setControl(tbTable); + tbTable.setHeaderVisible(true); + + TableColumn tblclmnTraceBufferHidden = new TableColumn(tbTable, SWT.RIGHT); + tblclmnTraceBufferHidden.setWidth(0); + tblclmnTraceBufferHidden.setText(""); + + TableColumn tblclmnTraceBufferIndex = new TableColumn(tbTable, SWT.RIGHT); + tblclmnTraceBufferIndex.setWidth(48); + tblclmnTraceBufferIndex.setText("Event"); + + TableColumn tblclmnTraceBufferTime = new TableColumn(tbTable, SWT.RIGHT); + tblclmnTraceBufferTime.setWidth(64); + tblclmnTraceBufferTime.setText("Time"); + + TableColumn tblclmnTraceBufferPrevAddress = new TableColumn(tbTable, SWT.RIGHT); + tblclmnTraceBufferPrevAddress.setWidth(72); + tblclmnTraceBufferPrevAddress.setText("Previous"); + + TableColumn tblclmnTraceBufferPrevName = new TableColumn(tbTable, SWT.LEFT); + tblclmnTraceBufferPrevName.setWidth(144); + tblclmnTraceBufferPrevName.setText("Previous Name"); + + TableColumn tblclmnTraceBufferState = new TableColumn(tbTable, SWT.RIGHT); + tblclmnTraceBufferState.setWidth(72); + tblclmnTraceBufferState.setText("State"); + + TableColumn tblclmnTraceBufferShared = new TableColumn(tbTable, SWT.RIGHT); + tblclmnTraceBufferShared.setWidth(72); + tblclmnTraceBufferShared.setText("Obj/Msg"); + + TableColumn tblclmnTraceBufferCurrentAddress = new TableColumn(tbTable, SWT.RIGHT); + tblclmnTraceBufferCurrentAddress.setWidth(72); + tblclmnTraceBufferCurrentAddress.setText("Current"); + + TableColumn tblclmnTraceBufferCurrentName = new TableColumn(tbTable, SWT.LEFT); + tblclmnTraceBufferCurrentName.setWidth(144); + tblclmnTraceBufferCurrentName.setText("Current Name"); + + makeActions(); + hookContextMenu(); + contributeToActionBars(); + + tabFolder.setSelection(tbtmGlobal); + + DebugPlugin.getDefault().addDebugEventListener(this); + + try { + debugger = new DebugProxy(); + } catch (DebugProxyException e) {} + } + + /** + * @brief Handling events from the debugger. + */ + @Override + public void handleDebugEvents(DebugEvent[] events) { + for (DebugEvent event : events) { + switch (event.getKind()) { + case DebugEvent.CREATE: + Object source = event.getSource(); + if (source instanceof CDebugTarget) { + try { + debugger = new DebugProxy((CDebugTarget)source); + } catch (DebugProxyException e) {} + } + break; + } + } + } + + private void hookContextMenu() { + MenuManager menuMgr = new MenuManager("#PopupMenu"); + menuMgr.setRemoveAllWhenShown(true); + menuMgr.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + ChibiView.this.fillContextMenu(manager); + } + }); + } + + private void contributeToActionBars() { + IActionBars bars = getViewSite().getActionBars(); + fillLocalPullDown(bars.getMenuManager()); + fillLocalToolBar(bars.getToolBarManager()); + } + + private void fillLocalPullDown(IMenuManager manager) { + manager.add(refreshAction); +/* manager.add(new Separator()); + manager.add(refreshAction);*/ + } + + private void fillContextMenu(IMenuManager manager) { + manager.add(refreshAction); + // Other plug-ins can contribute there actions here + manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + } + + private void fillLocalToolBar(IToolBarManager manager) { + manager.add(refreshAction); + } + + private void fillGlobalTable() { + LinkedHashMap lhm; + + // If the debugger is not yet present then do nothing. + if (debugger == null) + return; + + // Reading the list of global variables, null can be returned if the debugger + // does not respond. + try { + lhm = debugger.readGlobalVariables(); + if (lhm == null) + return; + } catch (DebugProxyException e) { + showMessage("Error: " + e.getMessage() + "."); + return; + } + + globalTable.removeAll(); + + Set> set = lhm.entrySet(); + for (Entry entry : set) { + TableItem tableItem = new TableItem(globalTable, SWT.NONE); + tableItem.setText(new String[] { + "", + entry.getKey(), + entry.getValue() + }); + } + } + + private String makeHex(String s) { + try { + s = HexUtils.dword2HexString((int)HexUtils.parseNumber(s)); + } catch (Exception e) {} + return s; + } + + private void fillThreadsTable() { + LinkedHashMap> lhm; + + // If the debugger is not yet present then do nothing. + if (debugger == null) + return; + + // Reading the list of threads, null can be returned if the debugger + // does not respond. + try { + lhm = debugger.readThreads(); + if (lhm == null) + return; + } catch (DebugProxyException e) { + showMessage("Error: " + e.getMessage() + "."); + return; + } + + threadsTable.removeAll(); + + Set>> set = lhm.entrySet(); + for (Entry> entry : set) { + HashMap map = entry.getValue(); + TableItem tableItem = new TableItem(threadsTable, SWT.NONE); + tableItem.setText(new String[] { + makeHex(entry.getKey()), + makeHex(map.get("stklimit")), + makeHex(map.get("stack")), + map.get("stkunused"), + map.get("name"), + map.get("state_s"), + HexUtils.byte2HexString((int)HexUtils.parseNumber(map.get("flags"))), + map.get("prio"), + map.get("refs"), + map.get("time"), + makeHex(map.get("wtobjp")) + }); + } + } + + private void fillTimersTable() { + LinkedHashMap> lhm; + + // If the debugger is not yet present then do nothing. + if (debugger == null) + return; + + // Reading the list of threads, null can be returned if the debugger + // does not respond. + try { + lhm = debugger.readTimers(); + if (lhm == null) + return; + } catch (DebugProxyException e) { + showMessage("Error: " + e.getMessage() + "."); + return; + } + + timersTable.removeAll(); + + Set>> set = lhm.entrySet(); + long time = 0; + for (Entry> entry : set) { + HashMap map = entry.getValue(); + time = time + HexUtils.parseNumber(map.get("delta")); + TableItem tableItem = new TableItem(timersTable, SWT.NONE); + tableItem.setText(new String[] { + makeHex(entry.getKey()), + Long.toString(time), + "+" + HexUtils.parseNumber(map.get("delta")), + makeHex(map.get("func")), + makeHex(map.get("par")) + }); + } + } + + private void fillTraceBufferTable() { + LinkedHashMap> lhm, lhmthreads; + + // If the debugger is not yet present then do nothing. + if (debugger == null) + return; + + // Read active threads for retrieving names. + try { + lhmthreads = debugger.readThreads(); + if (lhmthreads == null) + return; + } catch (DebugProxyException e) { + lhmthreads = new LinkedHashMap>(0); + } + + // Reading the list of threads, null can be returned if the debugger + // does not respond. + try { + lhm = debugger.readTraceBuffer(); + if (lhm == null) + return; + } catch (DebugProxyException e) { + showMessage("Error: " + e.getMessage() + "."); + return; + } + + tbTable.removeAll(); + + Set>> set = lhm.entrySet(); + String prev = ""; + String prevname = ""; + for (Entry> entry : set) { + HashMap map = entry.getValue(); + TableItem tableItem = new TableItem(tbTable, SWT.NONE); + + // Searches the current thread into the threads map. + String currentaddr = map.get("tp"); + HashMap thread = lhmthreads.get(currentaddr); + String currentname; + if (thread != null) + currentname = thread.get("name"); + else + currentname = ""; + + String current = makeHex(currentaddr); + tableItem.setText(new String[] { + "", + entry.getKey(), + map.get("time"), + prev, + prevname, + map.get("state_s"), + makeHex(map.get("wtobjp")), + current, + currentname + }); + prev = current; + prevname = currentname; + } + } + + private void makeActions() { + + // Refresh action. + refreshAction = new Action() { + public void run() { + CTabItem tabitem = tabFolder.getSelection(); + if (tabitem == null) + return; + if (tabitem == tbtmGlobal) + fillGlobalTable(); + else if (tabitem == tbtmThreads) + fillThreadsTable(); + else if (tabitem == tbtmTimers) + fillTimersTable(); + else if (tabitem == tbtmTraceBuffer) + fillTraceBufferTable(); + } + }; + refreshAction.setDisabledImageDescriptor(ResourceManager.getPluginImageDescriptor("org.eclipse.cdt.ui", "/icons/dlcl16/refresh_nav.gif")); + refreshAction.setImageDescriptor(ResourceManager.getPluginImageDescriptor("org.eclipse.cdt.ui", "/icons/elcl16/refresh_nav.gif")); + refreshAction.setText("Refresh"); + refreshAction.setToolTipText("Refresh timers list"); + } + + private void showMessage(String message) { + MessageDialog.openInformation(tabFolder.getShell(), + "ChibiOS/RT Views", message); + } + + /** + * Passing the focus request to the viewer's control. + */ + public void setFocus() { + tabFolder.setFocus(); + } +} diff --git a/tools/eclipse/debug_support/src/org/eclipse/wb/swt/ResourceManager.java b/tools/eclipse/debug_support/src/org/eclipse/wb/swt/ResourceManager.java new file mode 100644 index 000000000..4bfbc6b6e --- /dev/null +++ b/tools/eclipse/debug_support/src/org/eclipse/wb/swt/ResourceManager.java @@ -0,0 +1,415 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.swt; + +import java.io.File; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.resource.CompositeImageDescriptor; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.osgi.framework.Bundle; + +/** + * Utility class for managing OS resources associated with SWT/JFace controls such as colors, fonts, images, + * etc. + * + * !!! IMPORTANT !!! Application code must explicitly invoke the dispose() method to release the + * operating system resources managed by cached objects when those objects and OS resources are no longer + * needed (e.g. on application shutdown) + * + * This class may be freely distributed as part of any application or plugin. + *

+ * + * @author scheglov_ke + * @author Dan Rubel + */ +public class ResourceManager extends SWTResourceManager { + //////////////////////////////////////////////////////////////////////////// + // + // Image + // + //////////////////////////////////////////////////////////////////////////// + private static Map m_descriptorImageMap = new HashMap(); + /** + * Returns an {@link ImageDescriptor} stored in the file at the specified path relative to the specified + * class. + * + * @param clazz + * the {@link Class} relative to which to find the image descriptor. + * @param path + * the path to the image file. + * @return the {@link ImageDescriptor} stored in the file at the specified path. + */ + public static ImageDescriptor getImageDescriptor(Class clazz, String path) { + return ImageDescriptor.createFromFile(clazz, path); + } + /** + * Returns an {@link ImageDescriptor} stored in the file at the specified path. + * + * @param path + * the path to the image file. + * @return the {@link ImageDescriptor} stored in the file at the specified path. + */ + public static ImageDescriptor getImageDescriptor(String path) { + try { + return ImageDescriptor.createFromURL(new File(path).toURI().toURL()); + } catch (MalformedURLException e) { + return null; + } + } + /** + * Returns an {@link Image} based on the specified {@link ImageDescriptor}. + * + * @param descriptor + * the {@link ImageDescriptor} for the {@link Image}. + * @return the {@link Image} based on the specified {@link ImageDescriptor}. + */ + public static Image getImage(ImageDescriptor descriptor) { + if (descriptor == null) { + return null; + } + Image image = m_descriptorImageMap.get(descriptor); + if (image == null) { + image = descriptor.createImage(); + m_descriptorImageMap.put(descriptor, image); + } + return image; + } + /** + * Maps images to decorated images. + */ + @SuppressWarnings("unchecked") + private static Map>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY]; + /** + * Returns an {@link Image} composed of a base image decorated by another image. + * + * @param baseImage + * the base {@link Image} that should be decorated. + * @param decorator + * the {@link Image} to decorate the base image. + * @return {@link Image} The resulting decorated image. + */ + public static Image decorateImage(Image baseImage, Image decorator) { + return decorateImage(baseImage, decorator, BOTTOM_RIGHT); + } + /** + * Returns an {@link Image} composed of a base image decorated by another image. + * + * @param baseImage + * the base {@link Image} that should be decorated. + * @param decorator + * the {@link Image} to decorate the base image. + * @param corner + * the corner to place decorator image. + * @return the resulting decorated {@link Image}. + */ + public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) { + if (corner <= 0 || corner >= LAST_CORNER_KEY) { + throw new IllegalArgumentException("Wrong decorate corner"); + } + Map> cornerDecoratedImageMap = m_decoratedImageMap[corner]; + if (cornerDecoratedImageMap == null) { + cornerDecoratedImageMap = new HashMap>(); + m_decoratedImageMap[corner] = cornerDecoratedImageMap; + } + Map decoratedMap = cornerDecoratedImageMap.get(baseImage); + if (decoratedMap == null) { + decoratedMap = new HashMap(); + cornerDecoratedImageMap.put(baseImage, decoratedMap); + } + // + Image result = decoratedMap.get(decorator); + if (result == null) { + final Rectangle bib = baseImage.getBounds(); + final Rectangle dib = decorator.getBounds(); + final Point baseImageSize = new Point(bib.width, bib.height); + CompositeImageDescriptor compositImageDesc = new CompositeImageDescriptor() { + @Override + protected void drawCompositeImage(int width, int height) { + drawImage(baseImage.getImageData(), 0, 0); + if (corner == TOP_LEFT) { + drawImage(decorator.getImageData(), 0, 0); + } else if (corner == TOP_RIGHT) { + drawImage(decorator.getImageData(), bib.width - dib.width, 0); + } else if (corner == BOTTOM_LEFT) { + drawImage(decorator.getImageData(), 0, bib.height - dib.height); + } else if (corner == BOTTOM_RIGHT) { + drawImage(decorator.getImageData(), bib.width - dib.width, bib.height - dib.height); + } + } + @Override + protected Point getSize() { + return baseImageSize; + } + }; + // + result = compositImageDesc.createImage(); + decoratedMap.put(decorator, result); + } + return result; + } + /** + * Dispose all of the cached images. + */ + public static void disposeImages() { + SWTResourceManager.disposeImages(); + // dispose ImageDescriptor images + { + for (Iterator I = m_descriptorImageMap.values().iterator(); I.hasNext();) { + I.next().dispose(); + } + m_descriptorImageMap.clear(); + } + // dispose decorated images + for (int i = 0; i < m_decoratedImageMap.length; i++) { + Map> cornerDecoratedImageMap = m_decoratedImageMap[i]; + if (cornerDecoratedImageMap != null) { + for (Map decoratedMap : cornerDecoratedImageMap.values()) { + for (Image image : decoratedMap.values()) { + image.dispose(); + } + decoratedMap.clear(); + } + cornerDecoratedImageMap.clear(); + } + } + // dispose plugin images + { + for (Iterator I = m_URLImageMap.values().iterator(); I.hasNext();) { + I.next().dispose(); + } + m_URLImageMap.clear(); + } + } + //////////////////////////////////////////////////////////////////////////// + // + // Plugin images support + // + //////////////////////////////////////////////////////////////////////////// + /** + * Maps URL to images. + */ + private static Map m_URLImageMap = new HashMap(); + /** + * Provider for plugin resources, used by WindowBuilder at design time. + */ + public interface PluginResourceProvider { + URL getEntry(String symbolicName, String path); + } + /** + * Instance of {@link PluginResourceProvider}, used by WindowBuilder at design time. + */ + private static PluginResourceProvider m_designTimePluginResourceProvider = null; + /** + * Returns an {@link Image} based on a plugin and file path. + * + * @param plugin + * the plugin {@link Object} containing the image + * @param name + * the path to the image within the plugin + * @return the {@link Image} stored in the file at the specified path + * + * @deprecated Use {@link #getPluginImage(String, String)} instead. + */ + @Deprecated + public static Image getPluginImage(Object plugin, String name) { + try { + URL url = getPluginImageURL(plugin, name); + if (url != null) { + return getPluginImageFromUrl(url); + } + } catch (Throwable e) { + // Ignore any exceptions + } + return null; + } + /** + * Returns an {@link Image} based on a {@link Bundle} and resource entry path. + * + * @param symbolicName + * the symbolic name of the {@link Bundle}. + * @param path + * the path of the resource entry. + * @return the {@link Image} stored in the file at the specified path. + */ + public static Image getPluginImage(String symbolicName, String path) { + try { + URL url = getPluginImageURL(symbolicName, path); + if (url != null) { + return getPluginImageFromUrl(url); + } + } catch (Throwable e) { + // Ignore any exceptions + } + return null; + } + /** + * Returns an {@link Image} based on given {@link URL}. + */ + private static Image getPluginImageFromUrl(URL url) { + try { + try { + String key = url.toExternalForm(); + Image image = m_URLImageMap.get(key); + if (image == null) { + InputStream stream = url.openStream(); + try { + image = getImage(stream); + m_URLImageMap.put(key, image); + } finally { + stream.close(); + } + } + return image; + } catch (Throwable e) { + // Ignore any exceptions + } + } catch (Throwable e) { + // Ignore any exceptions + } + return null; + } + /** + * Returns an {@link ImageDescriptor} based on a plugin and file path. + * + * @param plugin + * the plugin {@link Object} containing the image. + * @param name + * the path to th eimage within the plugin. + * @return the {@link ImageDescriptor} stored in the file at the specified path. + * + * @deprecated Use {@link #getPluginImageDescriptor(String, String)} instead. + */ + @Deprecated + public static ImageDescriptor getPluginImageDescriptor(Object plugin, String name) { + try { + try { + URL url = getPluginImageURL(plugin, name); + return ImageDescriptor.createFromURL(url); + } catch (Throwable e) { + // Ignore any exceptions + } + } catch (Throwable e) { + // Ignore any exceptions + } + return null; + } + /** + * Returns an {@link ImageDescriptor} based on a {@link Bundle} and resource entry path. + * + * @param symbolicName + * the symbolic name of the {@link Bundle}. + * @param path + * the path of the resource entry. + * @return the {@link ImageDescriptor} based on a {@link Bundle} and resource entry path. + */ + public static ImageDescriptor getPluginImageDescriptor(String symbolicName, String path) { + try { + URL url = getPluginImageURL(symbolicName, path); + if (url != null) { + return ImageDescriptor.createFromURL(url); + } + } catch (Throwable e) { + // Ignore any exceptions + } + return null; + } + /** + * Returns an {@link URL} based on a {@link Bundle} and resource entry path. + */ + private static URL getPluginImageURL(String symbolicName, String path) { + // try runtime plugins + { + Bundle bundle = Platform.getBundle(symbolicName); + if (bundle != null) { + return bundle.getEntry(path); + } + } + // try design time provider + if (m_designTimePluginResourceProvider != null) { + return m_designTimePluginResourceProvider.getEntry(symbolicName, path); + } + // no such resource + return null; + } + /** + * Returns an {@link URL} based on a plugin and file path. + * + * @param plugin + * the plugin {@link Object} containing the file path. + * @param name + * the file path. + * @return the {@link URL} representing the file at the specified path. + * @throws Exception + */ + private static URL getPluginImageURL(Object plugin, String name) throws Exception { + // try to work with 'plugin' as with OSGI BundleContext + try { + Class BundleClass = Class.forName("org.osgi.framework.Bundle"); //$NON-NLS-1$ + Class BundleContextClass = Class.forName("org.osgi.framework.BundleContext"); //$NON-NLS-1$ + if (BundleContextClass.isAssignableFrom(plugin.getClass())) { + Method getBundleMethod = BundleContextClass.getMethod("getBundle", new Class[0]); //$NON-NLS-1$ + Object bundle = getBundleMethod.invoke(plugin, new Object[0]); + // + Class PathClass = Class.forName("org.eclipse.core.runtime.Path"); //$NON-NLS-1$ + Constructor pathConstructor = PathClass.getConstructor(new Class[]{String.class}); + Object path = pathConstructor.newInstance(new Object[]{name}); + // + Class IPathClass = Class.forName("org.eclipse.core.runtime.IPath"); //$NON-NLS-1$ + Class PlatformClass = Class.forName("org.eclipse.core.runtime.Platform"); //$NON-NLS-1$ + Method findMethod = PlatformClass.getMethod("find", new Class[]{BundleClass, IPathClass}); //$NON-NLS-1$ + return (URL) findMethod.invoke(null, new Object[]{bundle, path}); + } + } catch (Throwable e) { + // Ignore any exceptions + } + // else work with 'plugin' as with usual Eclipse plugin + { + Class PluginClass = Class.forName("org.eclipse.core.runtime.Plugin"); //$NON-NLS-1$ + if (PluginClass.isAssignableFrom(plugin.getClass())) { + // + Class PathClass = Class.forName("org.eclipse.core.runtime.Path"); //$NON-NLS-1$ + Constructor pathConstructor = PathClass.getConstructor(new Class[]{String.class}); + Object path = pathConstructor.newInstance(new Object[]{name}); + // + Class IPathClass = Class.forName("org.eclipse.core.runtime.IPath"); //$NON-NLS-1$ + Method findMethod = PluginClass.getMethod("find", new Class[]{IPathClass}); //$NON-NLS-1$ + return (URL) findMethod.invoke(plugin, new Object[]{path}); + } + } + return null; + } + //////////////////////////////////////////////////////////////////////////// + // + // General + // + //////////////////////////////////////////////////////////////////////////// + /** + * Dispose of cached objects and their underlying OS resources. This should only be called when the cached + * objects are no longer needed (e.g. on application shutdown). + */ + public static void dispose() { + disposeColors(); + disposeFonts(); + disposeImages(); + } +} \ No newline at end of file diff --git a/tools/eclipse/debug_support/src/org/eclipse/wb/swt/SWTResourceManager.java b/tools/eclipse/debug_support/src/org/eclipse/wb/swt/SWTResourceManager.java new file mode 100644 index 000000000..8b6d4cc3f --- /dev/null +++ b/tools/eclipse/debug_support/src/org/eclipse/wb/swt/SWTResourceManager.java @@ -0,0 +1,447 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.swt; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Cursor; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; + +/** + * Utility class for managing OS resources associated with SWT controls such as colors, fonts, images, etc. + *

+ * !!! IMPORTANT !!! Application code must explicitly invoke the dispose() method to release the + * operating system resources managed by cached objects when those objects and OS resources are no longer + * needed (e.g. on application shutdown) + *

+ * This class may be freely distributed as part of any application or plugin. + *

+ * @author scheglov_ke + * @author Dan Rubel + */ +public class SWTResourceManager { + //////////////////////////////////////////////////////////////////////////// + // + // Color + // + //////////////////////////////////////////////////////////////////////////// + private static Map m_colorMap = new HashMap(); + /** + * Returns the system {@link Color} matching the specific ID. + * + * @param systemColorID + * the ID value for the color + * @return the system {@link Color} matching the specific ID + */ + public static Color getColor(int systemColorID) { + Display display = Display.getCurrent(); + return display.getSystemColor(systemColorID); + } + /** + * Returns a {@link Color} given its red, green and blue component values. + * + * @param r + * the red component of the color + * @param g + * the green component of the color + * @param b + * the blue component of the color + * @return the {@link Color} matching the given red, green and blue component values + */ + public static Color getColor(int r, int g, int b) { + return getColor(new RGB(r, g, b)); + } + /** + * Returns a {@link Color} given its RGB value. + * + * @param rgb + * the {@link RGB} value of the color + * @return the {@link Color} matching the RGB value + */ + public static Color getColor(RGB rgb) { + Color color = m_colorMap.get(rgb); + if (color == null) { + Display display = Display.getCurrent(); + color = new Color(display, rgb); + m_colorMap.put(rgb, color); + } + return color; + } + /** + * Dispose of all the cached {@link Color}'s. + */ + public static void disposeColors() { + for (Color color : m_colorMap.values()) { + color.dispose(); + } + m_colorMap.clear(); + } + //////////////////////////////////////////////////////////////////////////// + // + // Image + // + //////////////////////////////////////////////////////////////////////////// + /** + * Maps image paths to images. + */ + private static Map m_imageMap = new HashMap(); + /** + * Returns an {@link Image} encoded by the specified {@link InputStream}. + * + * @param stream + * the {@link InputStream} encoding the image data + * @return the {@link Image} encoded by the specified input stream + */ + protected static Image getImage(InputStream stream) throws IOException { + try { + Display display = Display.getCurrent(); + ImageData data = new ImageData(stream); + if (data.transparentPixel > 0) { + return new Image(display, data, data.getTransparencyMask()); + } + return new Image(display, data); + } finally { + stream.close(); + } + } + /** + * Returns an {@link Image} stored in the file at the specified path. + * + * @param path + * the path to the image file + * @return the {@link Image} stored in the file at the specified path + */ + public static Image getImage(String path) { + Image image = m_imageMap.get(path); + if (image == null) { + try { + image = getImage(new FileInputStream(path)); + m_imageMap.put(path, image); + } catch (Exception e) { + image = getMissingImage(); + m_imageMap.put(path, image); + } + } + return image; + } + /** + * Returns an {@link Image} stored in the file at the specified path relative to the specified class. + * + * @param clazz + * the {@link Class} relative to which to find the image + * @param path + * the path to the image file, if starts with '/' + * @return the {@link Image} stored in the file at the specified path + */ + public static Image getImage(Class clazz, String path) { + String key = clazz.getName() + '|' + path; + Image image = m_imageMap.get(key); + if (image == null) { + try { + image = getImage(clazz.getResourceAsStream(path)); + m_imageMap.put(key, image); + } catch (Exception e) { + image = getMissingImage(); + m_imageMap.put(key, image); + } + } + return image; + } + private static final int MISSING_IMAGE_SIZE = 10; + /** + * @return the small {@link Image} that can be used as placeholder for missing image. + */ + private static Image getMissingImage() { + Image image = new Image(Display.getCurrent(), MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE); + // + GC gc = new GC(image); + gc.setBackground(getColor(SWT.COLOR_RED)); + gc.fillRectangle(0, 0, MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE); + gc.dispose(); + // + return image; + } + /** + * Style constant for placing decorator image in top left corner of base image. + */ + public static final int TOP_LEFT = 1; + /** + * Style constant for placing decorator image in top right corner of base image. + */ + public static final int TOP_RIGHT = 2; + /** + * Style constant for placing decorator image in bottom left corner of base image. + */ + public static final int BOTTOM_LEFT = 3; + /** + * Style constant for placing decorator image in bottom right corner of base image. + */ + public static final int BOTTOM_RIGHT = 4; + /** + * Internal value. + */ + protected static final int LAST_CORNER_KEY = 5; + /** + * Maps images to decorated images. + */ + @SuppressWarnings("unchecked") + private static Map>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY]; + /** + * Returns an {@link Image} composed of a base image decorated by another image. + * + * @param baseImage + * the base {@link Image} that should be decorated + * @param decorator + * the {@link Image} to decorate the base image + * @return {@link Image} The resulting decorated image + */ + public static Image decorateImage(Image baseImage, Image decorator) { + return decorateImage(baseImage, decorator, BOTTOM_RIGHT); + } + /** + * Returns an {@link Image} composed of a base image decorated by another image. + * + * @param baseImage + * the base {@link Image} that should be decorated + * @param decorator + * the {@link Image} to decorate the base image + * @param corner + * the corner to place decorator image + * @return the resulting decorated {@link Image} + */ + public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) { + if (corner <= 0 || corner >= LAST_CORNER_KEY) { + throw new IllegalArgumentException("Wrong decorate corner"); + } + Map> cornerDecoratedImageMap = m_decoratedImageMap[corner]; + if (cornerDecoratedImageMap == null) { + cornerDecoratedImageMap = new HashMap>(); + m_decoratedImageMap[corner] = cornerDecoratedImageMap; + } + Map decoratedMap = cornerDecoratedImageMap.get(baseImage); + if (decoratedMap == null) { + decoratedMap = new HashMap(); + cornerDecoratedImageMap.put(baseImage, decoratedMap); + } + // + Image result = decoratedMap.get(decorator); + if (result == null) { + Rectangle bib = baseImage.getBounds(); + Rectangle dib = decorator.getBounds(); + // + result = new Image(Display.getCurrent(), bib.width, bib.height); + // + GC gc = new GC(result); + gc.drawImage(baseImage, 0, 0); + if (corner == TOP_LEFT) { + gc.drawImage(decorator, 0, 0); + } else if (corner == TOP_RIGHT) { + gc.drawImage(decorator, bib.width - dib.width, 0); + } else if (corner == BOTTOM_LEFT) { + gc.drawImage(decorator, 0, bib.height - dib.height); + } else if (corner == BOTTOM_RIGHT) { + gc.drawImage(decorator, bib.width - dib.width, bib.height - dib.height); + } + gc.dispose(); + // + decoratedMap.put(decorator, result); + } + return result; + } + /** + * Dispose all of the cached {@link Image}'s. + */ + public static void disposeImages() { + // dispose loaded images + { + for (Image image : m_imageMap.values()) { + image.dispose(); + } + m_imageMap.clear(); + } + // dispose decorated images + for (int i = 0; i < m_decoratedImageMap.length; i++) { + Map> cornerDecoratedImageMap = m_decoratedImageMap[i]; + if (cornerDecoratedImageMap != null) { + for (Map decoratedMap : cornerDecoratedImageMap.values()) { + for (Image image : decoratedMap.values()) { + image.dispose(); + } + decoratedMap.clear(); + } + cornerDecoratedImageMap.clear(); + } + } + } + //////////////////////////////////////////////////////////////////////////// + // + // Font + // + //////////////////////////////////////////////////////////////////////////// + /** + * Maps font names to fonts. + */ + private static Map m_fontMap = new HashMap(); + /** + * Maps fonts to their bold versions. + */ + private static Map m_fontToBoldFontMap = new HashMap(); + /** + * Returns a {@link Font} based on its name, height and style. + * + * @param name + * the name of the font + * @param height + * the height of the font + * @param style + * the style of the font + * @return {@link Font} The font matching the name, height and style + */ + public static Font getFont(String name, int height, int style) { + return getFont(name, height, style, false, false); + } + /** + * Returns a {@link Font} based on its name, height and style. Windows-specific strikeout and underline + * flags are also supported. + * + * @param name + * the name of the font + * @param size + * the size of the font + * @param style + * the style of the font + * @param strikeout + * the strikeout flag (warning: Windows only) + * @param underline + * the underline flag (warning: Windows only) + * @return {@link Font} The font matching the name, height, style, strikeout and underline + */ + public static Font getFont(String name, int size, int style, boolean strikeout, boolean underline) { + String fontName = name + '|' + size + '|' + style + '|' + strikeout + '|' + underline; + Font font = m_fontMap.get(fontName); + if (font == null) { + FontData fontData = new FontData(name, size, style); + if (strikeout || underline) { + try { + Class logFontClass = Class.forName("org.eclipse.swt.internal.win32.LOGFONT"); //$NON-NLS-1$ + Object logFont = FontData.class.getField("data").get(fontData); //$NON-NLS-1$ + if (logFont != null && logFontClass != null) { + if (strikeout) { + logFontClass.getField("lfStrikeOut").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$ + } + if (underline) { + logFontClass.getField("lfUnderline").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$ + } + } + } catch (Throwable e) { + System.err.println("Unable to set underline or strikeout" + " (probably on a non-Windows platform). " + e); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + font = new Font(Display.getCurrent(), fontData); + m_fontMap.put(fontName, font); + } + return font; + } + /** + * Returns a bold version of the given {@link Font}. + * + * @param baseFont + * the {@link Font} for which a bold version is desired + * @return the bold version of the given {@link Font} + */ + public static Font getBoldFont(Font baseFont) { + Font font = m_fontToBoldFontMap.get(baseFont); + if (font == null) { + FontData fontDatas[] = baseFont.getFontData(); + FontData data = fontDatas[0]; + font = new Font(Display.getCurrent(), data.getName(), data.getHeight(), SWT.BOLD); + m_fontToBoldFontMap.put(baseFont, font); + } + return font; + } + /** + * Dispose all of the cached {@link Font}'s. + */ + public static void disposeFonts() { + // clear fonts + for (Font font : m_fontMap.values()) { + font.dispose(); + } + m_fontMap.clear(); + // clear bold fonts + for (Font font : m_fontToBoldFontMap.values()) { + font.dispose(); + } + m_fontToBoldFontMap.clear(); + } + //////////////////////////////////////////////////////////////////////////// + // + // Cursor + // + //////////////////////////////////////////////////////////////////////////// + /** + * Maps IDs to cursors. + */ + private static Map m_idToCursorMap = new HashMap(); + /** + * Returns the system cursor matching the specific ID. + * + * @param id + * int The ID value for the cursor + * @return Cursor The system cursor matching the specific ID + */ + public static Cursor getCursor(int id) { + Integer key = Integer.valueOf(id); + Cursor cursor = m_idToCursorMap.get(key); + if (cursor == null) { + cursor = new Cursor(Display.getDefault(), id); + m_idToCursorMap.put(key, cursor); + } + return cursor; + } + /** + * Dispose all of the cached cursors. + */ + public static void disposeCursors() { + for (Cursor cursor : m_idToCursorMap.values()) { + cursor.dispose(); + } + m_idToCursorMap.clear(); + } + //////////////////////////////////////////////////////////////////////////// + // + // General + // + //////////////////////////////////////////////////////////////////////////// + /** + * Dispose of cached objects and their underlying OS resources. This should only be called when the cached + * objects are no longer needed (e.g. on application shutdown). + */ + public static void dispose() { + disposeColors(); + disposeImages(); + disposeFonts(); + disposeCursors(); + } +} \ No newline at end of file -- cgit v1.2.3