Merge remote-tracking branch 'origin/GP-5003_improveTerminalFonts--SQUASHED'

This commit is contained in:
Ryan Kurtz 2024-12-20 15:21:29 -05:00
commit 473c16f1cb
12 changed files with 456 additions and 152 deletions

View file

@ -90,6 +90,21 @@
selected, then this selects nothing, so that pressing the keystroke twice is effectively
"Select None."</P>
<H3><A name="increase_font_size"></A>Increase Font Size</H3>
<P>This is accessed using the local drop-down menu or the key sequence <B>CTRL-SHIFT-PLUS</B>.
It increases the font size for this terminal.</P>
<H3><A name="decrease_font_size"></A>Decrease Font Size</H3>
<P>This is accessed using the local drop-down menu or the key sequence <B>CTRL-MINUS</B>. It
decreases the font size for this terminal.</P>
<H3><A name="reset_font_size"></A>Reset Font Size</H3>
<P>This is accessed using the local drop-down menu or the key sequence <B>CTRL-0</B>. It resets
the font size for this terminal according to the theme's configuration'.</P>
<H3><A name="terminate"></A>Terminate</H3>
<P>This action is accessed using the local drop-down menu. It will terminate the Terminal's
@ -873,41 +888,45 @@ bdcedit /dbgsettings NET HOSTIP:IP PORT:54321 KEY:1.1.1.1
<UL>
<LI><B>Type</B>: The type of kernel connection, either "Remote", "Local", or "EXDI".
"Remote", the most common type, indicates two-machine debugging over various
possible connection media, e.g. Ethernet, serial, USB, etc. "Local" is used for limited
introspection into the target on which the debugger is running. "EXDI" is arguably
the most exotic type - it essentially simulates the normal "Remote" connection using
the gdb Remote Serial Protocol. It can be used when connecting to gdbstubs in
platforms, such as QEMU, VMWare, Trace32, etc.</LI>
"Remote", the most common type, indicates two-machine debugging over various possible
connection media, e.g. Ethernet, serial, USB, etc. "Local" is used for limited introspection
into the target on which the debugger is running. "EXDI" is arguably the most exotic type -
it essentially simulates the normal "Remote" connection using the gdb Remote Serial Protocol.
It can be used when connecting to gdbstubs in platforms, such as QEMU, VMWare, Trace32,
etc.</LI>
</UL>
<H4>EXDI</H4>
<P>Setup for EXDI connections is fairly complicated and difficult to get correct.
The argument string typically should be something like:</P>
<UL style='list-style-type: none'>
<LI>
<PRE>
exdi:CLSID={29f9906e-9dbe-4d4b-b0fb-6acf7fb6d014},Kd=Guess,DataBreaks=Exdi
</PRE>
</LI>
</UL>
<P>The CLSID here should match the CLSID in the <B>exdiConfigData.xml</B> file in the debugger architectural directory. If windbg has been
run using EXDI at some point, there will also be an entry in the System Registry for this CLSID. The InprocServer32 subentry
for this CLSID in the Registry should point to a copy of ExdiGdbSrv.dll, typically the one in the same directory. This DLL
must reside somewhere that the debugger has permission to load from, i.e. not in the WindowsApps directory tree.
The <B>exdiConfigData</B> file should be configured for the target you're using. We heavily recommend using <B>displayCommPackets==yes</B>,
as many of the tasks take considerable time, and this is the only indicator of progress.
</P>
<H4>EXDI</H4>
<P>The <B>Kd=Guess</B> parameter causes the underlying engine to scan memory for the kernel's base address, which will probably not
be provided by the gdbstub. (<B>Kd=NtBaseAddr</B> is also a valid option, as is eliminating the parameter, but, currently, we have no
idea how to point the configuration at a correct value. Using this option will cause the load to spin pointlessly.) If you can,
we highly recommend breaking the target near the base address, as the search proceeds down through memory starting at the current
program counter. If the difference between the PC and the base address is large, the loading process will punt before useful
values are detected. If anyone understand how to extend this search (or knows how to set the base address to sidestep the scan),
we would really love some guidance.
</P>
<P>Setup for EXDI connections is fairly complicated and difficult to get correct. The argument
string typically should be something like:</P>
<UL style='list-style-type: none'>
<LI>
<PRE>
exdi:CLSID={29f9906e-9dbe-4d4b-b0fb-6acf7fb6d014},Kd=Guess,DataBreaks=Exdi
</PRE>
</LI>
</UL>
<P>The CLSID here should match the CLSID in the <B>exdiConfigData.xml</B> file in the debugger
architectural directory. If windbg has been run using EXDI at some point, there will also be an
entry in the System Registry for this CLSID. The InprocServer32 subentry for this CLSID in the
Registry should point to a copy of ExdiGdbSrv.dll, typically the one in the same directory.
This DLL must reside somewhere that the debugger has permission to load from, i.e. not in the
WindowsApps directory tree. The <B>exdiConfigData</B> file should be configured for the target
you're using. We heavily recommend using <B>displayCommPackets==yes</B>, as many of the tasks
take considerable time, and this is the only indicator of progress.</P>
<P>The <B>Kd=Guess</B> parameter causes the underlying engine to scan memory for the kernel's
base address, which will probably not be provided by the gdbstub. (<B>Kd=NtBaseAddr</B> is also
a valid option, as is eliminating the parameter, but, currently, we have no idea how to point
the configuration at a correct value. Using this option will cause the load to spin
pointlessly.) If you can, we highly recommend breaking the target near the base address, as the
search proceeds down through memory starting at the current program counter. If the difference
between the PC and the base address is large, the loading process will punt before useful
values are detected. If anyone understand how to extend this search (or knows how to set the
base address to sidestep the scan), we would really love some guidance.</P>
<H3><A name="dbgeng_ttd"></A>TTD (Time-Travel Debugging)</H3>

View file

@ -127,6 +127,15 @@ color.fg.plugin.terminal.bright.blue = rgb(88,51,255)
color.fg.plugin.terminal.bright.magenta = rgb(249,53,248)
color.fg.plugin.terminal.bright.cyan = rgb(20,240,240)
color.fg.plugin.terminal.bright.white = rgb(233,235,235)
// Just the normals, since normal is bold and dim is plain weight
color.fg.plugin.terminal.dim.black = rgb(0,0,0)
color.fg.plugin.terminal.dim.red = rgb(194,54,33)
color.fg.plugin.terminal.dim.green = rgb(37,188,36)
color.fg.plugin.terminal.dim.yellow = rgb(173,173,39)
color.fg.plugin.terminal.dim.blue = rgb(73,46,255)
color.fg.plugin.terminal.dim.magenta = rgb(211,56,211)
color.fg.plugin.terminal.dim.cyan = rgb(51,187,200)
color.fg.plugin.terminal.dim.white = rgb(203,204,205)
color.bg.plugin.locationreferences.highlight = color.palette.lightcornflowerblue
@ -183,7 +192,15 @@ font.plugin.tabs = sansserif-plain-11
font.plugin.tabs.list = sansserif-bold-9
font.plugin.tips = dialog-plain-12
font.plugin.tips.label = font.plugin.tips[bold]
font.plugin.terminal = font.monospaced
font.plugin.terminal = font.monospaced[bold]
font.plugin.terminal.italic = font.plugin.terminal[italic]
font.plugin.terminal.fraktur = font.plugin.terminal
font.plugin.terminal.bright = font.plugin.terminal[bold]
font.plugin.terminal.bright.italic = font.plugin.terminal.bright[italic]
font.plugin.terminal.bright.fraktur = font.plugin.terminal.bright
font.plugin.terminal.dim = font.plugin.terminal[plain]
font.plugin.terminal.dim.italic = font.plugin.terminal.dim[italic]
font.plugin.terminal.dim.fraktur = font.plugin.terminal.dim
font.plugin.terminal.completion.list = dialog-plain-12
font.plugin.window.location = font.monospaced[40]

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -35,8 +35,9 @@ public class TerminalLayout extends SingleRowLayout {
protected final VtLine line;
protected final TerminalTextField field;
public TerminalLayout(VtLine line, FontMetrics metrics, AnsiColorResolver colors) {
super(TerminalTextField.create(line, metrics, colors));
public TerminalLayout(VtLine line, FontMetrics metrics, float fontSizeAdjustment,
AnsiColorResolver colors) {
super(TerminalTextField.create(line, metrics, fontSizeAdjustment, colors));
this.line = line;
this.field = (TerminalTextField) getField(0);
}

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -30,6 +30,7 @@ import docking.widgets.fieldpanel.listener.LayoutModelListener;
import docking.widgets.fieldpanel.support.FieldLocation;
import docking.widgets.fieldpanel.support.FieldRange;
import ghidra.app.plugin.core.terminal.vt.*;
import ghidra.app.plugin.core.terminal.vt.AnsiColorResolver.ReverseVideo;
import ghidra.app.plugin.core.terminal.vt.VtCharset.G;
import ghidra.util.*;
@ -65,6 +66,7 @@ public class TerminalLayoutModel implements LayoutModel, VtHandler {
// Rendering properties
protected FontMetrics metrics;
protected float fontSizeAdjustment;
protected final AnsiColorResolver colors;
protected final ArrayList<LayoutModelListener> listeners = new ArrayList<>();
@ -103,6 +105,7 @@ public class TerminalLayoutModel implements LayoutModel, VtHandler {
* @param panel the panel to receive commands from the model's VT/ANSI parser
* @param charset the charset for decoding bytes to characters
* @param metrics font metrics for the monospaced terminal font
* @param fontSizeAdjustment the font size adjustment
* @param colors a resolver for ANSI colors
*/
public TerminalLayoutModel(TerminalPanel panel, Charset charset, FontMetrics metrics,
@ -202,7 +205,7 @@ public class TerminalLayoutModel implements LayoutModel, VtHandler {
}
protected TerminalLayout newLayout(VtLine line) {
return new TerminalLayout(line, metrics, colors);
return new TerminalLayout(line, metrics, fontSizeAdjustment, colors);
}
protected void buildLayouts() {
@ -371,7 +374,7 @@ public class TerminalLayoutModel implements LayoutModel, VtHandler {
}
@Override
public void handleReverseVideo(boolean reverse) {
public void handleReverseVideo(ReverseVideo reverse) {
buffer.setAttributes(buffer.getAttributes().reverseVideo(reverse));
}
@ -412,7 +415,7 @@ public class TerminalLayoutModel implements LayoutModel, VtHandler {
@Override
public void handleAutoWrapMode(boolean en) {
System.err.println("TODO: handleAutoWrapMode: " + en);
Msg.trace(this, "TODO: handleAutoWrapMode: " + en);
}
@Override
@ -641,7 +644,9 @@ public class TerminalLayoutModel implements LayoutModel, VtHandler {
}
}
public void setFontMetrics(FontMetrics metrics2) {
public void setFontMetrics(FontMetrics metrics, float fontSizeAdjustment) {
this.metrics = metrics;
this.fontSizeAdjustment = fontSizeAdjustment;
layouts.clear();
layoutCache.clear();
buildLayouts();

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -33,8 +33,7 @@ import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.listener.*;
import docking.widgets.fieldpanel.support.*;
import docking.widgets.indexedscrollpane.IndexedScrollPane;
import generic.theme.GColor;
import generic.theme.Gui;
import generic.theme.*;
import ghidra.app.plugin.core.terminal.TerminalFinder.RegexTerminalFinder;
import ghidra.app.plugin.core.terminal.TerminalFinder.TextTerminalFinder;
import ghidra.app.plugin.core.terminal.vt.*;
@ -56,10 +55,30 @@ import ghidra.util.Msg;
* most sense to declare the various {@link GColor}s here.
*/
public class TerminalPanel extends JPanel implements FieldLocationListener, FieldSelectionListener,
LayoutListener, AnsiColorResolver {
LayoutListener, AnsiColorResolver, ThemeListener {
protected static final int MAX_TITLE_STACK_SIZE = 20;
protected static final String DEFAULT_FONT_ID = "font.plugin.terminal";
protected static final String DEFAULT_FRAKTUR_FONT_ID = "font.plugin.terminal.fraktur";
protected static final String DEFAULT_ITALIC_FONT_ID = "font.plugin.terminal.italic";
protected static final String DIM_FONT_ID = "font.plugin.terminal.dim";
protected static final String DIM_FRAKTUR_FONT_ID = "font.plugin.terminal.dim.fraktur";
protected static final String DIM_ITALIC_FONT_ID = "font.plugin.terminal.dim.italic";
protected static final String BRIGHT_FONT_ID = "font.plugin.terminal.bright";
protected static final String BRIGHT_FRAKTUR_FONT_ID = "font.plugin.terminal.bright.fraktur";
protected static final String BRIGHT_ITALIC_FONT_ID = "font.plugin.terminal.bright.italic";
protected static final Set<String> ALL_FONT_IDS = Set.of(
DEFAULT_FONT_ID,
DEFAULT_FRAKTUR_FONT_ID,
DEFAULT_ITALIC_FONT_ID,
DIM_FONT_ID,
DIM_FRAKTUR_FONT_ID,
DIM_ITALIC_FONT_ID,
BRIGHT_FONT_ID,
BRIGHT_FRAKTUR_FONT_ID,
BRIGHT_ITALIC_FONT_ID);
protected static final GColor COLOR_BACKGROUND = new GColor("color.bg.plugin.terminal");
protected static final GColor COLOR_FOREGROUND = new GColor("color.fg.plugin.terminal");
protected static final GColor COLOR_CURSOR_FOCUSED =
@ -84,6 +103,7 @@ public class TerminalPanel extends JPanel implements FieldLocationListener, Fiel
new GColor("color.fg.plugin.terminal.normal.cyan");
protected static final GColor COLOR_7_WHITE =
new GColor("color.fg.plugin.terminal.normal.white");
protected static final GColor COLOR_0_BRIGHT_BLACK =
new GColor("color.fg.plugin.terminal.bright.black");
protected static final GColor COLOR_1_BRIGHT_RED =
@ -101,6 +121,23 @@ public class TerminalPanel extends JPanel implements FieldLocationListener, Fiel
protected static final GColor COLOR_7_BRIGHT_WHITE =
new GColor("color.fg.plugin.terminal.bright.white");
protected static final GColor COLOR_0_DIM_BLACK =
new GColor("color.fg.plugin.terminal.dim.black");
protected static final GColor COLOR_1_DIM_RED =
new GColor("color.fg.plugin.terminal.dim.red");
protected static final GColor COLOR_2_DIM_GREEN =
new GColor("color.fg.plugin.terminal.dim.green");
protected static final GColor COLOR_3_DIM_YELLOW =
new GColor("color.fg.plugin.terminal.dim.yellow");
protected static final GColor COLOR_4_DIM_BLUE =
new GColor("color.fg.plugin.terminal.dim.blue");
protected static final GColor COLOR_5_DIM_MAGENTA =
new GColor("color.fg.plugin.terminal.dim.magenta");
protected static final GColor COLOR_6_DIM_CYAN =
new GColor("color.fg.plugin.terminal.dim.cyan");
protected static final GColor COLOR_7_DIM_WHITE =
new GColor("color.fg.plugin.terminal.dim.white");
protected static final int[] CUBE_STEPS = {
0, 95, 135, 175, 215, 255
};
@ -129,6 +166,7 @@ public class TerminalPanel extends JPanel implements FieldLocationListener, Fiel
}
}
protected float fontSizeAdjustment = 0;
protected FontMetrics metrics;
protected final TerminalLayoutModel model;
protected final TerminalFieldPanel fieldPanel;
@ -153,6 +191,7 @@ public class TerminalPanel extends JPanel implements FieldLocationListener, Fiel
this.provider = provider;
clipboardProvider = new TerminalClipboardProvider(provider);
Gui.registerFont(this, DEFAULT_FONT_ID);
Gui.addThemeListener(this);
this.metrics = getFontMetrics(getFont());
this.model = new TerminalLayoutModel(this, charset, metrics, this);
this.fieldPanel = new TerminalFieldPanel(model);
@ -346,13 +385,57 @@ public class TerminalPanel extends JPanel implements FieldLocationListener, Fiel
}
}
@Override
public void themeChanged(ThemeEvent event) {
if (event.isLookAndFeelChanged()) {
setFont(Gui.getFont(DEFAULT_FONT_ID));
}
if (event.isFontChanged(DEFAULT_FONT_ID)) {
setFont(Gui.getFont(DEFAULT_FONT_ID));
}
else if (ALL_FONT_IDS.stream().anyMatch(event::isFontChanged)) {
if (model != null) {
model.modelChanged();
}
}
}
protected void updateFontMetrics() {
Font font = getFont();
float size = font.getSize2D();
fontSizeAdjustment = Math.max(-size + 1, fontSizeAdjustment);
font = font.deriveFont(size + fontSizeAdjustment);
metrics = getFontMetrics(font);
if (model != null) {
model.setFontMetrics(metrics, fontSizeAdjustment);
}
if (!fixedSize) {
resizeTerminalToWindow();
}
if (model != null) {
model.modelChanged();
}
}
@Override
public void setFont(Font font) {
super.setFont(font);
this.metrics = getFontMetrics(font);
if (model != null) {
model.setFontMetrics(this.metrics);
}
updateFontMetrics();
}
protected void increaseFontSize() {
fontSizeAdjustment++;
updateFontMetrics();
}
protected void decreaseFontSize() {
fontSizeAdjustment--;
updateFontMetrics();
}
protected void resetFontSize() {
fontSizeAdjustment = 0;
updateFontMetrics();
}
public TerminalFieldPanel getFieldPanel() {
@ -424,29 +507,42 @@ public class TerminalPanel extends JPanel implements FieldLocationListener, Fiel
model.processInput(buffer);
}
protected Color resolveDefaultColor(WhichGround ground, boolean reverseVideo) {
if (ground == WhichGround.BACKGROUND) {
if (reverseVideo) {
return COLOR_FOREGROUND;
}
return null; // background is already drawn
protected Color resolveDefaultColor(WhichGround ground, ReverseVideo reverse,
Intensity intensity) {
Color color = switch (reverse) {
case NORMAL -> switch (ground) {
case FOREGROUND -> COLOR_FOREGROUND;
case BACKGROUND -> null; // background is already drawn
};
case REVERSED -> switch (ground) {
case FOREGROUND -> COLOR_BACKGROUND;
case BACKGROUND -> COLOR_FOREGROUND;
};
};
if (color == null) {
return null;
}
if (reverseVideo) {
return COLOR_BACKGROUND;
}
return COLOR_FOREGROUND;
return switch (intensity) {
case NORMAL -> color;
case BOLD -> color.brighter();
case DIM -> color.darker();
};
}
protected Color resolveStandardColor(AnsiStandardColor standard) {
return switch (standard) {
case BLACK -> COLOR_0_BLACK;
case RED -> COLOR_1_RED;
case GREEN -> COLOR_2_GREEN;
case YELLOW -> COLOR_3_YELLOW;
case BLUE -> COLOR_4_BLUE;
case MAGENTA -> COLOR_5_MAGENTA;
case CYAN -> COLOR_6_CYAN;
case WHITE -> COLOR_7_WHITE;
protected Color resolveStandardColor(AnsiStandardColor standard, Intensity intensity) {
return switch (intensity) {
case BOLD -> resolveIntenseColor(standard.intense);
case DIM -> resolveDimColor(standard.dim);
case NORMAL -> switch (standard) {
case BLACK -> COLOR_0_BLACK;
case RED -> COLOR_1_RED;
case GREEN -> COLOR_2_GREEN;
case YELLOW -> COLOR_3_YELLOW;
case BLUE -> COLOR_4_BLUE;
case MAGENTA -> COLOR_5_MAGENTA;
case CYAN -> COLOR_6_CYAN;
case WHITE -> COLOR_7_WHITE;
};
};
}
@ -463,6 +559,19 @@ public class TerminalPanel extends JPanel implements FieldLocationListener, Fiel
};
}
protected Color resolveDimColor(AnsiDimColor intense) {
return switch (intense) {
case BLACK -> COLOR_0_DIM_BLACK;
case RED -> COLOR_1_DIM_RED;
case GREEN -> COLOR_2_DIM_GREEN;
case YELLOW -> COLOR_3_DIM_YELLOW;
case BLUE -> COLOR_4_DIM_BLUE;
case MAGENTA -> COLOR_5_DIM_MAGENTA;
case CYAN -> COLOR_6_DIM_CYAN;
case WHITE -> COLOR_7_DIM_WHITE;
};
}
protected Color resolve216Color(Ansi216Color cube) {
return ColorUtils.getColor(CUBE_STEPS[cube.r()], CUBE_STEPS[cube.g()],
CUBE_STEPS[cube.b()]);
@ -478,12 +587,12 @@ public class TerminalPanel extends JPanel implements FieldLocationListener, Fiel
@Override
public Color resolveColor(AnsiColor color, WhichGround ground, Intensity intensity,
boolean reverseVideo) {
ReverseVideo reverse) {
if (color == AnsiDefaultColor.INSTANCE) {
return resolveDefaultColor(ground, reverseVideo);
return resolveDefaultColor(ground, reverse, intensity);
}
if (color instanceof AnsiStandardColor standard) {
return resolveStandardColor(standard);
return resolveStandardColor(standard, intensity);
}
if (color instanceof AnsiIntenseColor intense) {
return resolveIntenseColor(intense);
@ -560,6 +669,8 @@ public class TerminalPanel extends JPanel implements FieldLocationListener, Fiel
}
public void dispose() {
Gui.unRegisterFont(this, DEFAULT_FONT_ID);
Gui.removeThemeListener(this);
if (this.clipboardService != null) {
clipboardService.deRegisterClipboardContentProvider(clipboardProvider);
}
@ -663,6 +774,10 @@ public class TerminalPanel extends JPanel implements FieldLocationListener, Fiel
}
protected void resizeTerminalToWindow() {
if (scroller == null) {
// Some callback during construction
return;
}
Rectangle bounds = scroller.getViewportBorderBounds();
int cols = bounds.width / metrics.charWidth('M');
int rows = bounds.height / metrics.getHeight();

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -167,6 +167,9 @@ public class TerminalProvider extends ComponentProviderAdapter {
protected DockingAction actionFindPrevious;
protected DockingAction actionSelectAll;
protected DockingAction actionTerminate;
protected DockingAction actionIncreaseSize;
protected DockingAction actionDecreaseSize;
protected DockingAction actionResetSize;
private boolean terminated = false;
@ -262,6 +265,28 @@ public class TerminalProvider extends ComponentProviderAdapter {
.helpLocation(new HelpLocation(helpPlugin.getName(), "select_all"))
.onAction(this::activatedSelectAll)
.buildAndInstallLocal(this);
actionIncreaseSize = new ActionBuilder("Increase Font Size", plugin.getName())
.menuPath("Increase Font Size")
.menuGroup("View")
.keyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS,
InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK))
.helpLocation(new HelpLocation(helpPlugin.getName(), "increase_font_size"))
.onAction(this::activatedIncreaseFontSize)
.buildAndInstallLocal(this);
actionDecreaseSize = new ActionBuilder("Decrease Font Size", plugin.getName())
.menuPath("Decrease Font Size")
.menuGroup("View")
.keyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, InputEvent.CTRL_DOWN_MASK))
.helpLocation(new HelpLocation(helpPlugin.getName(), "decrease_font_size"))
.onAction(this::activatedDecreaseFontSize)
.buildAndInstallLocal(this);
actionResetSize = new ActionBuilder("Reset Font Size", plugin.getName())
.menuPath("Reset Font Size")
.menuGroup("View")
.keyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_0, InputEvent.CTRL_DOWN_MASK))
.helpLocation(new HelpLocation(helpPlugin.getName(), "decrease_font_size"))
.onAction(this::activatedResetFontSize)
.buildAndInstallLocal(this);
}
protected void activatedFind(ActionContext ctx) {
@ -329,6 +354,18 @@ public class TerminalProvider extends ComponentProviderAdapter {
panel.getFieldPanel().setSelection(sel, EventTrigger.GUI_ACTION);
}
protected void activatedIncreaseFontSize(ActionContext ctx) {
panel.increaseFontSize();
}
protected void activatedDecreaseFontSize(ActionContext ctx) {
panel.decreaseFontSize();
}
protected void activatedResetFontSize(ActionContext ctx) {
panel.resetFontSize();
}
/**
* Check if the given keystroke would activate a local action.
*

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -51,13 +51,14 @@ public class TerminalTextField implements TextField {
*
* @param line the line from the {@link VtBuffer} that will be rendered in this field
* @param metrics the font metrics
* @param fontSizeAdjustment the font size adjustment
* @param colors the color resolver
* @return the field
*/
public static TerminalTextField create(VtLine line, FontMetrics metrics,
AnsiColorResolver colors) {
return new TerminalTextField(0, new TerminalTextFieldElement(line, metrics, colors),
metrics);
float fontSizeAdjustment, AnsiColorResolver colors) {
return new TerminalTextField(0,
new TerminalTextFieldElement(line, metrics, fontSizeAdjustment, colors), metrics);
}
protected TerminalTextField(int startX, TerminalTextFieldElement element, FontMetrics metrics) {

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -22,8 +22,8 @@ import javax.swing.JComponent;
import docking.widgets.fieldpanel.field.FieldElement;
import docking.widgets.fieldpanel.support.RowColLocation;
import generic.theme.Gui;
import ghidra.app.plugin.core.terminal.vt.*;
import ghidra.app.plugin.core.terminal.vt.VtHandler.Intensity;
/**
* A text field element for rendering a full line of terminal text
@ -41,6 +41,7 @@ public class TerminalTextFieldElement implements FieldElement {
protected final VtLine line;
protected final FontMetrics metrics;
protected final float fontSizeAdjustment;
protected final AnsiColorResolver colors;
protected final int em;
@ -50,11 +51,14 @@ public class TerminalTextFieldElement implements FieldElement {
*
* @param line the line of text from the {@link VtBuffer}
* @param metrics the font metrics
* @param fontSizeAdjustment the font size adjustment
* @param colors the color resolver
*/
public TerminalTextFieldElement(VtLine line, FontMetrics metrics, AnsiColorResolver colors) {
public TerminalTextFieldElement(VtLine line, FontMetrics metrics, float fontSizeAdjustment,
AnsiColorResolver colors) {
this.line = line;
this.metrics = metrics;
this.fontSizeAdjustment = fontSizeAdjustment;
this.colors = colors;
this.em = metrics.charWidth('M');
@ -163,22 +167,34 @@ public class TerminalTextFieldElement implements FieldElement {
int height = metrics.getHeight();
int left = x + start * em;
int width = em * (end - start);
Font font = metrics.getFont();
Color bg = attrs.resolveBackground(colors);
if (bg != null) {
g.setColor(bg);
g.fillRect(left, descent - height, width, height);
}
g.setColor(attrs.resolveForeground(colors));
// NB. I don't really intend to implement blinking.
// TODO: AnsiFont mapping?
if (attrs.intensity() == Intensity.DIM) {
g.setFont(font.deriveFont(Font.PLAIN));
}
else {
// Normal will use bold font, but standard color
g.setFont(font.deriveFont(Font.BOLD));
}
// I don't really intend to implement blinking.
// We still use metrics from DEFAULT_FONT_ID
Font font = Gui.getFont(switch (attrs.intensity()) {
case NORMAL -> switch (attrs.font()) {
case NORMAL -> TerminalPanel.DEFAULT_FONT_ID;
case ITALIC -> TerminalPanel.DEFAULT_ITALIC_FONT_ID;
case BLACK_LETTER -> TerminalPanel.DEFAULT_FRAKTUR_FONT_ID;
};
case BOLD -> switch (attrs.font()) {
case NORMAL -> TerminalPanel.BRIGHT_FONT_ID;
case ITALIC -> TerminalPanel.BRIGHT_ITALIC_FONT_ID;
case BLACK_LETTER -> TerminalPanel.BRIGHT_FRAKTUR_FONT_ID;
};
case DIM -> switch (attrs.font()) {
case NORMAL -> TerminalPanel.DIM_FONT_ID;
case ITALIC -> TerminalPanel.DIM_ITALIC_FONT_ID;
case BLACK_LETTER -> TerminalPanel.DIM_FRAKTUR_FONT_ID;
};
});
font = font.deriveFont(font.getSize2D() + fontSizeAdjustment);
g.setFont(font);
if (!attrs.hidden()) {
switch (attrs.underline()) {
case DOUBLE:

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -32,6 +32,35 @@ public interface AnsiColorResolver {
FOREGROUND, BACKGROUND;
}
enum ReverseVideo {
NORMAL {
@Override
AnsiColor fg(AnsiColor fg, AnsiColor bg) {
return fg;
}
@Override
AnsiColor bg(AnsiColor fg, AnsiColor bg) {
return bg;
}
},
REVERSED {
@Override
AnsiColor fg(AnsiColor fg, AnsiColor bg) {
return bg;
}
@Override
AnsiColor bg(AnsiColor fg, AnsiColor bg) {
return fg;
}
};
abstract AnsiColor fg(AnsiColor fg, AnsiColor bg);
abstract AnsiColor bg(AnsiColor fg, AnsiColor bg);
}
/**
* Convert a color specification to an AWT color
*
@ -39,11 +68,11 @@ public interface AnsiColorResolver {
* @param ground identifies the colors use in the foreground or the background
* @param intensity gives the intensity of the color, really only used when a basic color is
* specified.
* @param reverseVideo identifies whether the foreground and background colors were swapped,
* really only used when the default color is specified.
* @param reverse identifies whether the foreground and background colors were swapped, really
* only used when the default color is specified.
* @return the AWT color, or null to not draw (usually in the case of the default background
* color)
*/
Color resolveColor(AnsiColor color, WhichGround ground, Intensity intensity,
boolean reverseVideo);
ReverseVideo reverse);
}

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -17,6 +17,7 @@ package ghidra.app.plugin.core.terminal.vt;
import java.awt.Color;
import ghidra.app.plugin.core.terminal.vt.AnsiColorResolver.ReverseVideo;
import ghidra.app.plugin.core.terminal.vt.AnsiColorResolver.WhichGround;
import ghidra.app.plugin.core.terminal.vt.VtHandler.*;
@ -30,7 +31,7 @@ import ghidra.app.plugin.core.terminal.vt.VtHandler.*;
* in the buffer.
*/
public record VtAttributes(AnsiColor fg, AnsiColor bg, Intensity intensity,
AnsiFont font, Underline underline, Blink blink, boolean reverseVideo, boolean hidden,
AnsiFont font, Underline underline, Blink blink, ReverseVideo reverse, boolean hidden,
boolean strikeThrough, boolean proportionalSpacing) {
/**
@ -38,8 +39,8 @@ public record VtAttributes(AnsiColor fg, AnsiColor bg, Intensity intensity,
*/
public static final VtAttributes DEFAULTS =
new VtAttributes(AnsiDefaultColor.INSTANCE, AnsiDefaultColor.INSTANCE,
Intensity.NORMAL, AnsiFont.NORMAL, Underline.NONE, Blink.NONE, false, false, false,
false);
Intensity.NORMAL, AnsiFont.NORMAL, Underline.NONE, Blink.NONE, ReverseVideo.NORMAL,
false, false, false);
/**
* Create a copy of this record with the foreground color replaced
@ -48,8 +49,8 @@ public record VtAttributes(AnsiColor fg, AnsiColor bg, Intensity intensity,
* @return the new record
*/
public VtAttributes fg(AnsiColor fg) {
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverseVideo,
hidden, strikeThrough, proportionalSpacing);
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverse, hidden,
strikeThrough, proportionalSpacing);
}
/**
@ -59,8 +60,8 @@ public record VtAttributes(AnsiColor fg, AnsiColor bg, Intensity intensity,
* @return the new record
*/
public VtAttributes bg(AnsiColor bg) {
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverseVideo,
hidden, strikeThrough, proportionalSpacing);
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverse, hidden,
strikeThrough, proportionalSpacing);
}
/**
@ -70,8 +71,8 @@ public record VtAttributes(AnsiColor fg, AnsiColor bg, Intensity intensity,
* @return the new record
*/
public VtAttributes intensity(Intensity intensity) {
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverseVideo,
hidden, strikeThrough, proportionalSpacing);
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverse, hidden,
strikeThrough, proportionalSpacing);
}
/**
@ -81,8 +82,8 @@ public record VtAttributes(AnsiColor fg, AnsiColor bg, Intensity intensity,
* @return the new record
*/
public VtAttributes font(AnsiFont font) {
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverseVideo,
hidden, strikeThrough, proportionalSpacing);
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverse, hidden,
strikeThrough, proportionalSpacing);
}
/**
@ -92,8 +93,8 @@ public record VtAttributes(AnsiColor fg, AnsiColor bg, Intensity intensity,
* @return the new record
*/
public VtAttributes underline(Underline underline) {
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverseVideo,
hidden, strikeThrough, proportionalSpacing);
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverse, hidden,
strikeThrough, proportionalSpacing);
}
/**
@ -103,19 +104,19 @@ public record VtAttributes(AnsiColor fg, AnsiColor bg, Intensity intensity,
* @return the new record
*/
public VtAttributes blink(Blink blink) {
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverseVideo,
hidden, strikeThrough, proportionalSpacing);
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverse, hidden,
strikeThrough, proportionalSpacing);
}
/**
* Create a copy of this record with the reverse-video replaced
*
* @param reverseVideo the new reverse-video
* @param reverse the new reverse-video
* @return the new record
*/
public VtAttributes reverseVideo(boolean reverseVideo) {
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverseVideo,
hidden, strikeThrough, proportionalSpacing);
public VtAttributes reverseVideo(ReverseVideo reverse) {
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverse, hidden,
strikeThrough, proportionalSpacing);
}
/**
@ -125,8 +126,8 @@ public record VtAttributes(AnsiColor fg, AnsiColor bg, Intensity intensity,
* @return the new record
*/
public VtAttributes hidden(boolean hidden) {
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverseVideo,
hidden, strikeThrough, proportionalSpacing);
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverse, hidden,
strikeThrough, proportionalSpacing);
}
/**
@ -136,8 +137,8 @@ public record VtAttributes(AnsiColor fg, AnsiColor bg, Intensity intensity,
* @return the new record
*/
public VtAttributes strikeThrough(boolean strikeThrough) {
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverseVideo,
hidden, strikeThrough, proportionalSpacing);
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverse, hidden,
strikeThrough, proportionalSpacing);
}
/**
@ -147,8 +148,8 @@ public record VtAttributes(AnsiColor fg, AnsiColor bg, Intensity intensity,
* @return the new record
*/
public VtAttributes proportionalSpacing(boolean proportionalSpacing) {
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverseVideo,
hidden, strikeThrough, proportionalSpacing);
return new VtAttributes(fg, bg, intensity, font, underline, blink, reverse, hidden,
strikeThrough, proportionalSpacing);
}
/**
@ -158,8 +159,7 @@ public record VtAttributes(AnsiColor fg, AnsiColor bg, Intensity intensity,
* @return the color
*/
public Color resolveForeground(AnsiColorResolver colors) {
return colors.resolveColor(reverseVideo ? bg : fg, WhichGround.FOREGROUND, intensity,
reverseVideo);
return colors.resolveColor(reverse.fg(fg, bg), WhichGround.FOREGROUND, intensity, reverse);
}
/**
@ -169,7 +169,7 @@ public record VtAttributes(AnsiColor fg, AnsiColor bg, Intensity intensity,
* @return the color, or null to not paint the background
*/
public Color resolveBackground(AnsiColorResolver colors) {
return colors.resolveColor(reverseVideo ? fg : bg, WhichGround.BACKGROUND, Intensity.NORMAL,
reverseVideo);
return colors.resolveColor(reverse.bg(fg, bg), WhichGround.BACKGROUND, Intensity.NORMAL,
reverse);
}
}

View file

@ -23,6 +23,7 @@ import java.util.PrimitiveIterator.OfInt;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ghidra.app.plugin.core.terminal.vt.AnsiColorResolver.ReverseVideo;
import ghidra.util.Msg;
/**
@ -171,43 +172,51 @@ public interface VtHandler {
* Usually the darkest black available. Implementations may select a color softer on the
* eyes, depending on use. For foreground, this should likely be true black (0,0,0).
*/
BLACK,
BLACK(AnsiIntenseColor.BLACK, AnsiDimColor.BLACK),
/**
* A color whose hue is clearly red.
*/
RED,
RED(AnsiIntenseColor.RED, AnsiDimColor.RED),
/**
* A color whose hue is clearly green.
*/
GREEN,
GREEN(AnsiIntenseColor.GREEN, AnsiDimColor.GREEN),
/**
* A color whose hue is clearly yellow.
*/
YELLOW,
YELLOW(AnsiIntenseColor.YELLOW, AnsiDimColor.YELLOW),
/**
* A color whose hue is clearly blue. For palettes made to display on a dark (but not black)
* background, a hue tinted toward cyan is recommended.
*/
BLUE,
BLUE(AnsiIntenseColor.BLUE, AnsiDimColor.BLUE),
/**
* A color whose hue is clearly magenta or purple. For palettes made to display on a dark
* (but not black) background, a hue tinted toward red is recommended.
*/
MAGENTA,
MAGENTA(AnsiIntenseColor.MAGENTA, AnsiDimColor.MAGENTA),
/**
* A color whose hue is clearly cyan.
*/
CYAN,
CYAN(AnsiIntenseColor.CYAN, AnsiDimColor.CYAN),
/**
* A relatively bright white, sparing the brightest for intense white.
*/
WHITE;
WHITE(AnsiIntenseColor.WHITE, AnsiDimColor.WHITE);
/**
* An unmodifiable list giving all the standard colors
*/
public static final List<AnsiStandardColor> ALL = List.of(AnsiStandardColor.values());
public final AnsiIntenseColor intense;
public final AnsiDimColor dim;
private AnsiStandardColor(AnsiIntenseColor intense, AnsiDimColor dim) {
this.intense = intense;
this.dim = dim;
}
/**
* Get the standard color for the given numerical code
*
@ -282,14 +291,69 @@ public interface VtHandler {
}
}
/**
* One of the eight ANSI dim colors
*/
public enum AnsiDimColor implements AnsiColor {
/**
* A relatively dark grey, but not true black.
*/
BLACK,
/**
* See {@link AnsiStandardColor#RED}, but darker.
*/
RED,
/**
* See {@link AnsiStandardColor#GREEN}, but darker.
*/
GREEN,
/**
* See {@link AnsiStandardColor#YELLOW}, but darker.
*/
YELLOW,
/**
* See {@link AnsiStandardColor#BLUE}, but darker.
*/
BLUE,
/**
* See {@link AnsiStandardColor#MAGENTA}, but darker.
*/
MAGENTA,
/**
* See {@link AnsiStandardColor#CYAN}, but darker.
*/
CYAN,
/**
* Usually grey.
*/
WHITE;
/**
* An unmodifiable list giving all the dim colors
*/
public static final List<AnsiDimColor> ALL = List.of(AnsiDimColor.values());
/**
* Get the dim color for the given numerical code
*
* <p>
* For example, the sequence {@code CSI [ 34 m} would use code 4 (blue).
*
* @param code the code
* @return the color
*/
public static AnsiDimColor get(int code) {
return ALL.get(code);
}
}
/**
* For 8-bit colors, one of the 216 colors from the RGB cube
*
* <p>
* The r, g, and b fields give the "step" number from 0 to 5, dimmest to brightest.
*/
public record Ansi216Color(int r, int g, int b) implements AnsiColor {
}
public record Ansi216Color(int r, int g, int b) implements AnsiColor {}
/**
* For 8-bit colors, one of the 24 grays
@ -298,8 +362,7 @@ public interface VtHandler {
* The v field is a value from 0 to 23, 0 being the dimmest, but not true black, and 23 being
* the brightest, but not true white.
*/
public record AnsiGrayscaleColor(int v) implements AnsiColor {
}
public record AnsiGrayscaleColor(int v) implements AnsiColor {}
/**
* A 24-bit color
@ -307,8 +370,7 @@ public interface VtHandler {
* <p>
* The r, g, and b fields are values from 0 to 255 dimmest to brightest.
*/
public record Ansi24BitColor(int r, int g, int b) implements AnsiColor {
}
public record Ansi24BitColor(int r, int g, int b) implements AnsiColor {}
/**
* Modifies the intensity of the character either by color or by font weight.
@ -1202,7 +1264,7 @@ public interface VtHandler {
handleBlink(Blink.FAST);
return;
case 7:
handleReverseVideo(true);
handleReverseVideo(ReverseVideo.REVERSED);
return;
case 8:
handleHidden(true);
@ -1232,7 +1294,7 @@ public interface VtHandler {
handleProportionalSpacing(true);
return;
case 27:
handleReverseVideo(false);
handleReverseVideo(ReverseVideo.NORMAL);
return;
case 28:
handleHidden(false);
@ -1345,9 +1407,9 @@ public interface VtHandler {
* "default background," care must be taken to ensure the foreground is still painted in
* reversed mode.
*
* @param reverse true to reverse, false otherwise
* @param reverse the reverse video mode
*/
void handleReverseVideo(boolean reverse);
void handleReverseVideo(ReverseVideo reverse);
/**
* Handle toggling of the hidden attribute

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -63,6 +63,8 @@ public class TerminalProviderTest extends AbstractGhidraHeadedDebuggerTest {
terminalService = addPlugin(tool, TerminalPlugin.class);
clipboardService = addPlugin(tool, ClipboardPlugin.class);
env.showFrontEndTool();
PtyFactory factory = PtyFactory.local();
try (Pty pty = factory.openpty()) {
Map<String, String> env = new HashMap<>(System.getenv());