From 3b25dbcd598646975996f2a668ae17d9b6a70447 Mon Sep 17 00:00:00 2001 From: insjang Date: Wed, 2 Sep 2026 22:15:04 +0900 Subject: [PATCH 1/4] Terminal: support SGR 2 (faint), and send special keys held with a modifier SGR 2 asks for faint text. The attribute was unknown and logged as unsupported, so programs that use it for secondary text - help lines, timestamps, completed items - showed it at full strength, and SGR 22, which turns both bold and faint off, only turned off bold. TerminalStyle gains a dim attribute (an API addition, hence the version bump), and the style map draws a faint colour half way to the background, which works for any colour rather than only the sixteen named ones. Special keys were sent only when no modifier was held, so a program was never told about Ctrl+End, Shift+Home, Alt+Left, Ctrl+F5 and the rest. xterm spells a modified special key as the plain one with the modifier as a parameter (2 shift, 3 alt, 5 control, and their sums): the arrows, Home, End and F1-F4 as CSI 1 ; m X, the others as CSI n ; m ~. Keys the existing switch already answered never reach this and keep working as before. --- .../META-INF/MANIFEST.MF | 2 +- .../internal/emulator/VT100Emulator.java | 7 +- .../emulator/VT100TerminalControl.java | 68 +++++++++++++++++++ .../internal/textcanvas/StyleMap.java | 19 +++++- .../eclipse/terminal/model/TerminalStyle.java | 66 +++++++++++++----- .../internal/emulator/VT100EmulatorTest.java | 27 ++++++++ .../org/eclipse/terminal/model/StyleTest.java | 10 +++ 7 files changed, 179 insertions(+), 20 deletions(-) diff --git a/terminal/bundles/org.eclipse.terminal.control/META-INF/MANIFEST.MF b/terminal/bundles/org.eclipse.terminal.control/META-INF/MANIFEST.MF index ff5a3fb72be..2054f6b2dad 100644 --- a/terminal/bundles/org.eclipse.terminal.control/META-INF/MANIFEST.MF +++ b/terminal/bundles/org.eclipse.terminal.control/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.terminal.control; singleton:=true -Bundle-Version: 1.1.200.qualifier +Bundle-Version: 1.2.0.qualifier Bundle-Activator: org.eclipse.terminal.internal.control.impl.TerminalPlugin Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/emulator/VT100Emulator.java b/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/emulator/VT100Emulator.java index 659fd5cd4fd..b20439a40ff 100644 --- a/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/emulator/VT100Emulator.java +++ b/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/emulator/VT100Emulator.java @@ -914,6 +914,10 @@ private void processAnsiCommand_m() { style = style.setBold(true); break; + case 2: + style = style.setDim(true); + break; + case 4: style = style.setUnderline(true); break; @@ -931,7 +935,8 @@ private void processAnsiCommand_m() { case 21: case 22: - style = style.setBold(false); + // Normal intensity: undoes both of the two that change it. + style = style.setBold(false).setDim(false); break; case 24: diff --git a/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/emulator/VT100TerminalControl.java b/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/emulator/VT100TerminalControl.java index e80d897f11c..b30711d7e67 100644 --- a/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/emulator/VT100TerminalControl.java +++ b/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/emulator/VT100TerminalControl.java @@ -569,6 +569,70 @@ public Shell getShell() { return getCtlText().getShell(); } + /** + * The sequences above are sent only when no modifier is held, so a program was + * never told about Ctrl+End and the rest of them. xterm spells a modified + * special key as the plain one carrying the modifier as a parameter, which is + * what this builds. Keys the switch already answered never reach here, so what + * works today keeps working. + * + * @return the sequence, or null when the key is not one that is spelled this way + */ + static String modifiedSpecialKey(int keyCode, int modifierKeys) { + int modifier = 1 + ((modifierKeys & SWT.SHIFT) != 0 ? 1 : 0) + ((modifierKeys & SWT.ALT) != 0 ? 2 : 0) + + ((modifierKeys & SWT.CTRL) != 0 ? 4 : 0); + if (modifier == 1) { + // Something held that xterm has no spelling for, the Mac's Cmd among them. + return null; + } + switch (keyCode) { + case 0x1000001: // Up arrow. + return "\u001b[1;" + modifier + 'A'; //$NON-NLS-1$ + case 0x1000002: // Down arrow. + return "\u001b[1;" + modifier + 'B'; //$NON-NLS-1$ + case 0x1000003: // Left arrow. + return "\u001b[1;" + modifier + 'D'; //$NON-NLS-1$ + case 0x1000004: // Right arrow. + return "\u001b[1;" + modifier + 'C'; //$NON-NLS-1$ + case 0x1000005: // PgUp key. + return "\u001b[5;" + modifier + '~'; //$NON-NLS-1$ + case 0x1000006: // PgDn key. + return "\u001b[6;" + modifier + '~'; //$NON-NLS-1$ + case 0x1000007: // Home key. + return "\u001b[1;" + modifier + 'H'; //$NON-NLS-1$ + case 0x1000008: // End key. + return "\u001b[1;" + modifier + 'F'; //$NON-NLS-1$ + case 0x1000009: // Insert. + return "\u001b[2;" + modifier + '~'; //$NON-NLS-1$ + case 0x100000a: // F1, which xterm spells with a letter like the arrows. + return "\u001b[1;" + modifier + 'P'; //$NON-NLS-1$ + case 0x100000b: // F2. + return "\u001b[1;" + modifier + 'Q'; //$NON-NLS-1$ + case 0x100000c: // F3. + return "\u001b[1;" + modifier + 'R'; //$NON-NLS-1$ + case 0x100000d: // F4. + return "\u001b[1;" + modifier + 'S'; //$NON-NLS-1$ + case 0x100000e: // F5, and from here on a number, with 16 and 22 left out. + return "\u001b[15;" + modifier + '~'; //$NON-NLS-1$ + case 0x100000f: // F6. + return "\u001b[17;" + modifier + '~'; //$NON-NLS-1$ + case 0x1000010: // F7. + return "\u001b[18;" + modifier + '~'; //$NON-NLS-1$ + case 0x1000011: // F8. + return "\u001b[19;" + modifier + '~'; //$NON-NLS-1$ + case 0x1000012: // F9. + return "\u001b[20;" + modifier + '~'; //$NON-NLS-1$ + case 0x1000013: // F10. + return "\u001b[21;" + modifier + '~'; //$NON-NLS-1$ + case 0x1000014: // F11. + return "\u001b[23;" + modifier + '~'; //$NON-NLS-1$ + case 0x1000015: // F12. + return "\u001b[24;" + modifier + '~'; //$NON-NLS-1$ + default: + return null; + } + } + protected void sendChar(char chKey, boolean altKeyPressed) { try { int byteToSend = chKey; @@ -1150,6 +1214,10 @@ public void keyPressed(KeyEvent event) { break; } + if (escSeq == null) { + escSeq = modifiedSpecialKey(event.keyCode, modifierKeys); + } + if (escSeq == null) { // Any unmapped key should be handled locally by Eclipse event.doit = true; diff --git a/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/textcanvas/StyleMap.java b/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/textcanvas/StyleMap.java index f7c91d635e0..b333c595765 100644 --- a/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/textcanvas/StyleMap.java +++ b/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/textcanvas/StyleMap.java @@ -86,7 +86,7 @@ public Color getForegroundColor(TerminalStyle style) { foregroundRGB = style.getForegroundRGB(); } if (foregroundRGB != null) { - return new Color(foregroundRGB); + return dimmed(style, new Color(foregroundRGB)); } TerminalColor color; @@ -101,7 +101,22 @@ public Color getForegroundColor(TerminalStyle style) { } color = color.convertColor(fInvertColors, style.isBold()); - return getColor(color); + return dimmed(style, getColor(color)); + } + + /** + * SGR 2 asks for a fainter version of whatever colour is in force. Half way to + * the background keeps the text readable while setting it apart from the rest, + * and it works for any colour rather than only the sixteen named ones. + */ + private Color dimmed(TerminalStyle style, Color color) { + if (!style.isDim()) { + return color; + } + RGB rgb = color.getRGB(); + RGB background = getBackgroundColor(style).getRGB(); + return new Color((rgb.red + background.red) / 2, (rgb.green + background.green) / 2, + (rgb.blue + background.blue) / 2); } public Color getBackgroundColor(TerminalStyle style) { diff --git a/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/model/TerminalStyle.java b/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/model/TerminalStyle.java index 4cf8c8f93c5..6c1bd03ba52 100644 --- a/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/model/TerminalStyle.java +++ b/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/model/TerminalStyle.java @@ -37,6 +37,7 @@ public class TerminalStyle { private final boolean fBlink; private final boolean fUnderline; private final boolean fReverse; + private final boolean fDim; private final static Map fgStyles = Collections .synchronizedMap(new LinkedHashMap() { @Override @@ -55,7 +56,8 @@ protected boolean removeEldestEntry(Map.Entry elde }); private TerminalStyle(TerminalColor foregroundTerminalColor, TerminalColor backgroundTerminalColor, - RGB foregroundRGB, RGB backgroundRGB, boolean bold, boolean blink, boolean underline, boolean reverse) { + RGB foregroundRGB, RGB backgroundRGB, boolean bold, boolean blink, boolean underline, boolean reverse, + boolean dim) { Assert.isLegal(foregroundTerminalColor == null || foregroundRGB == null, "Only one of ANSI or RGB colors can be specified as a foreground color"); //$NON-NLS-1$ Assert.isLegal(backgroundTerminalColor == null || backgroundRGB == null, @@ -68,12 +70,24 @@ private TerminalStyle(TerminalColor foregroundTerminalColor, TerminalColor backg fBlink = blink; fUnderline = underline; fReverse = reverse; + fDim = dim; } public static TerminalStyle getStyle(TerminalColor foregroundTerminalColor, TerminalColor backgroundTerminalColor, RGB foregroundRGB, RGB backgroundRGB, boolean bold, boolean blink, boolean underline, boolean reverse) { + return getStyle(foregroundTerminalColor, backgroundTerminalColor, foregroundRGB, backgroundRGB, bold, blink, + underline, reverse, false); + } + + /** + * @param dim SGR 2, drawn with the foreground moved toward the background + * @since 1.2 + */ + public static TerminalStyle getStyle(TerminalColor foregroundTerminalColor, TerminalColor backgroundTerminalColor, + RGB foregroundRGB, RGB backgroundRGB, boolean bold, boolean blink, boolean underline, boolean reverse, + boolean dim) { TerminalStyle style = new TerminalStyle(foregroundTerminalColor, backgroundTerminalColor, foregroundRGB, - backgroundRGB, bold, blink, underline, reverse); + backgroundRGB, bold, blink, underline, reverse, dim); // If set had a computeIfAbsent we would use a set, instead just store 1-2-1 mapping return fgStyles.computeIfAbsent(style, (s) -> style); } @@ -98,32 +112,32 @@ public static TerminalStyle getStyle(TerminalColor foregroundTerminalColor, Term public TerminalStyle setForeground(TerminalColor foregroundTerminalColor) { return getStyle(foregroundTerminalColor, fBackgroundTerminalColor, null, fBackgroundRGB, fBold, fBlink, - fUnderline, fReverse); + fUnderline, fReverse, fDim); } public TerminalStyle setBackground(TerminalColor backgroundTerminalColor) { return getStyle(fForegroundTerminalColor, backgroundTerminalColor, fForegroundRGB, null, fBold, fBlink, - fUnderline, fReverse); + fUnderline, fReverse, fDim); } public TerminalStyle setForeground(RGB foregroundRGB) { return getStyle(null, fBackgroundTerminalColor, foregroundRGB, fBackgroundRGB, fBold, fBlink, fUnderline, - fReverse); + fReverse, fDim); } public TerminalStyle setBackground(RGB backgroundRGB) { return getStyle(fForegroundTerminalColor, null, fForegroundRGB, backgroundRGB, fBold, fBlink, fUnderline, - fReverse); + fReverse, fDim); } public TerminalStyle setForeground(TerminalStyle other) { return getStyle(other.fForegroundTerminalColor, fBackgroundTerminalColor, other.fForegroundRGB, fBackgroundRGB, - fBold, fBlink, fUnderline, fReverse); + fBold, fBlink, fUnderline, fReverse, fDim); } public TerminalStyle setBackground(TerminalStyle other) { return getStyle(fForegroundTerminalColor, other.fBackgroundTerminalColor, fForegroundRGB, other.fBackgroundRGB, - fBold, fBlink, fUnderline, fReverse); + fBold, fBlink, fUnderline, fReverse, fDim); } public TerminalStyle setForeground(int eightBitindexedColor) { @@ -131,11 +145,11 @@ public TerminalStyle setForeground(int eightBitindexedColor) { if (isIndexTerminalColor) { TerminalColor foregroundTerminalColor = TerminalColor.getIndexedTerminalColor(eightBitindexedColor); return getStyle(foregroundTerminalColor, fBackgroundTerminalColor, null, fBackgroundRGB, fBold, fBlink, - fUnderline, fReverse); + fUnderline, fReverse, fDim); } else { RGB foregroundRGB = TerminalColor.getIndexedRGBColor(eightBitindexedColor); return getStyle(null, fBackgroundTerminalColor, foregroundRGB, fBackgroundRGB, fBold, fBlink, fUnderline, - fReverse); + fReverse, fDim); } } @@ -144,32 +158,40 @@ public TerminalStyle setBackground(int eightBitindexedColor) { if (isIndexTerminalColor) { TerminalColor backgroundTerminalColor = TerminalColor.getIndexedTerminalColor(eightBitindexedColor); return getStyle(fForegroundTerminalColor, backgroundTerminalColor, fForegroundRGB, null, fBold, fBlink, - fUnderline, fReverse); + fUnderline, fReverse, fDim); } else { RGB backgroundRGB = TerminalColor.getIndexedRGBColor(eightBitindexedColor); return getStyle(fForegroundTerminalColor, null, fForegroundRGB, backgroundRGB, fBold, fBlink, fUnderline, - fReverse); + fReverse, fDim); } } public TerminalStyle setBold(boolean bold) { return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fForegroundRGB, fBackgroundRGB, bold, - fBlink, fUnderline, fReverse); + fBlink, fUnderline, fReverse, fDim); } public TerminalStyle setBlink(boolean blink) { return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fForegroundRGB, fBackgroundRGB, fBold, - blink, fUnderline, fReverse); + blink, fUnderline, fReverse, fDim); } public TerminalStyle setUnderline(boolean underline) { return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fForegroundRGB, fBackgroundRGB, fBold, - fBlink, underline, fReverse); + fBlink, underline, fReverse, fDim); } public TerminalStyle setReverse(boolean reverse) { return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fForegroundRGB, fBackgroundRGB, fBold, - fBlink, fUnderline, reverse); + fBlink, fUnderline, reverse, fDim); + } + + /** + * @since 1.2 + */ + public TerminalStyle setDim(boolean dim) { + return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fForegroundRGB, fBackgroundRGB, fBold, + fBlink, fUnderline, fReverse, dim); } public TerminalColor getForegroundTerminalColor() { @@ -204,6 +226,14 @@ public boolean isUnderline() { return fUnderline; } + /** + * @return whether SGR 2 is in effect + * @since 1.2 + */ + public boolean isDim() { + return fDim; + } + @Override public int hashCode() { final int prime = 31; @@ -216,6 +246,7 @@ public int hashCode() { result = prime * result + ((fForegroundRGB == null) ? 0 : fForegroundRGB.hashCode()); result = prime * result + (fReverse ? 1231 : 1237); result = prime * result + (fUnderline ? 1231 : 1237); + result = prime * result + (fDim ? 1231 : 1237); return result; } @@ -250,6 +281,9 @@ public boolean equals(Object obj) { if (fForegroundTerminalColor != other.fForegroundTerminalColor) { return false; } + if (fDim != other.fDim) { + return false; + } if (fForegroundRGB == null) { if (other.fForegroundRGB != null) { return false; diff --git a/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/internal/emulator/VT100EmulatorTest.java b/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/internal/emulator/VT100EmulatorTest.java index d7c5692f1a9..61e272904a9 100644 --- a/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/internal/emulator/VT100EmulatorTest.java +++ b/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/internal/emulator/VT100EmulatorTest.java @@ -11,6 +11,8 @@ import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.io.Reader; @@ -375,4 +377,29 @@ public void testMalformedStringTerminator() { () -> assertEquals(List.of("TITLE1", "TITLE2"), control.getAllTitles())); } + @Test + public void testFaint() { + // SGR 2 makes the text faint; 22 takes both bold and faint off, as in xterm + run("\u001b[2mfaint\u001b[22m normal \u001b[1;2mboth\u001b[22mplain"); + assertTrue(data.getStyle(0, 0).isDim()); + assertFalse(data.getStyle(0, 0).isBold()); + assertFalse(data.getStyle(0, 6).isDim()); + assertTrue(data.getStyle(0, 13).isDim()); + assertTrue(data.getStyle(0, 13).isBold()); + assertFalse(data.getStyle(0, 17).isDim()); + assertFalse(data.getStyle(0, 17).isBold()); + } + + @Test + public void testModifiedSpecialKeys() { + // xterm's spelling: the plain key with the modifier as a parameter, 2 shift, 3 alt, 5 control + assertEquals("\u001b[1;5A", VT100TerminalControl.modifiedSpecialKey(0x1000001, org.eclipse.swt.SWT.CTRL)); + assertEquals("\u001b[1;2F", VT100TerminalControl.modifiedSpecialKey(0x1000008, org.eclipse.swt.SWT.SHIFT)); + assertEquals("\u001b[5;3~", VT100TerminalControl.modifiedSpecialKey(0x1000005, org.eclipse.swt.SWT.ALT)); + assertEquals("\u001b[1;6P", VT100TerminalControl.modifiedSpecialKey(0x100000a, org.eclipse.swt.SWT.CTRL | org.eclipse.swt.SWT.SHIFT)); + assertEquals("\u001b[15;5~", VT100TerminalControl.modifiedSpecialKey(0x100000e, org.eclipse.swt.SWT.CTRL)); + assertEquals("\u001b[24;2~", VT100TerminalControl.modifiedSpecialKey(0x1000015, org.eclipse.swt.SWT.SHIFT)); + // no modifier held that xterm can spell: not this way + assertEquals(null, VT100TerminalControl.modifiedSpecialKey(0x1000001, 0)); + } } diff --git a/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/model/StyleTest.java b/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/model/StyleTest.java index 6dbc4e976c3..745e5bba0a7 100644 --- a/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/model/StyleTest.java +++ b/terminal/tests/org.eclipse.terminal.test/src/org/eclipse/terminal/model/StyleTest.java @@ -126,4 +126,14 @@ private TerminalStyle getDefaultStyle() { return TerminalStyle.getStyle(c1, c2, false, false, false, false); } + @Test + public void testSetDim() { + TerminalStyle s1 = TerminalStyle.getStyle(c1, c2, true, false, true, false); + assertFalse(s1.isDim()); + TerminalStyle s2 = s1.setDim(true); + assertNotSame(s1, s2); + assertTrue(s2.isDim()); + assertTrue(s2.isBold()); // the rest is kept + assertSame(s1, s2.setDim(false)); + } } From cf98384a966bb4435fd64cb2f761d26b1e461991 Mon Sep 17 00:00:00 2001 From: insjang Date: Thu, 3 Sep 2026 12:33:16 +0900 Subject: [PATCH 2/4] Terminal: bump org.eclipse.terminal.model package export to 1.1.0 New @since 1.2 API was added to the package without bumping its Export-Package version, which API Tools flags as an error. --- .../bundles/org.eclipse.terminal.control/META-INF/MANIFEST.MF | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminal/bundles/org.eclipse.terminal.control/META-INF/MANIFEST.MF b/terminal/bundles/org.eclipse.terminal.control/META-INF/MANIFEST.MF index 2054f6b2dad..8ef0e9347c0 100644 --- a/terminal/bundles/org.eclipse.terminal.control/META-INF/MANIFEST.MF +++ b/terminal/bundles/org.eclipse.terminal.control/META-INF/MANIFEST.MF @@ -30,5 +30,5 @@ Export-Package: org.eclipse.terminal.connector;version="1.0.100"; org.eclipse.terminal.internal.model;x-internal:=true, org.eclipse.terminal.internal.preferences;x-internal:=true;x-friends:="org.eclipse.terminal.view.ui", org.eclipse.terminal.internal.textcanvas;x-internal:=true, - org.eclipse.terminal.model;version="1.0.100";uses:="org.eclipse.swt.graphics" + org.eclipse.terminal.model;version="1.1.0";uses:="org.eclipse.swt.graphics" Automatic-Module-Name: org.eclipse.terminal.control From fd839d5506758328a7a9d8fce9d9bd1f462b61f2 Mon Sep 17 00:00:00 2001 From: insjang Date: Thu, 3 Sep 2026 17:02:13 +0900 Subject: [PATCH 3/4] Terminal: make modifiedSpecialKey public so the test bundle can call it Same cross-bundle package-private access problem as mouseReport in #2902 -- the test bundle is a separate OSGi bundle, so package-private static does not reach it even under the same package name. --- .../terminal/internal/emulator/VT100TerminalControl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/emulator/VT100TerminalControl.java b/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/emulator/VT100TerminalControl.java index b30711d7e67..79ebf4f2e62 100644 --- a/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/emulator/VT100TerminalControl.java +++ b/terminal/bundles/org.eclipse.terminal.control/src/org/eclipse/terminal/internal/emulator/VT100TerminalControl.java @@ -578,7 +578,7 @@ public Shell getShell() { * * @return the sequence, or null when the key is not one that is spelled this way */ - static String modifiedSpecialKey(int keyCode, int modifierKeys) { + public static String modifiedSpecialKey(int keyCode, int modifierKeys) { int modifier = 1 + ((modifierKeys & SWT.SHIFT) != 0 ? 1 : 0) + ((modifierKeys & SWT.ALT) != 0 ? 2 : 0) + ((modifierKeys & SWT.CTRL) != 0 ? 4 : 0); if (modifier == 1) { From 7700b2fc91004f066373903e43c9b27c024b3e70 Mon Sep 17 00:00:00 2001 From: insjang Date: Thu, 3 Sep 2026 18:49:45 +0900 Subject: [PATCH 4/4] Terminal: retrigger CI (previous run hit the pipeline timeout, unrelated to this change)