5 | * Encodes effects, foreground and background colors into a 64 bit long, which are stored for each cell in a terminal 6 | * row in {@link TerminalRow#mStyle}. 7 | *
8 | *9 | * The bit layout is: 10 | *
11 | * - 16 flags (11 currently used). 12 | * - 24 for foreground color (only 9 first bits if a color index). 13 | * - 24 for background color (only 9 first bits if a color index). 14 | */ 15 | public final class TextStyle { 16 | 17 | public final static int CHARACTER_ATTRIBUTE_BOLD = 1; 18 | public final static int CHARACTER_ATTRIBUTE_ITALIC = 1 << 1; 19 | public final static int CHARACTER_ATTRIBUTE_UNDERLINE = 1 << 2; 20 | public final static int CHARACTER_ATTRIBUTE_BLINK = 1 << 3; 21 | public final static int CHARACTER_ATTRIBUTE_INVERSE = 1 << 4; 22 | public final static int CHARACTER_ATTRIBUTE_INVISIBLE = 1 << 5; 23 | public final static int CHARACTER_ATTRIBUTE_STRIKETHROUGH = 1 << 6; 24 | /** 25 | * The selective erase control functions (DECSED and DECSEL) can only erase characters defined as erasable. 26 | *27 | * This bit is set if DECSCA (Select Character Protection Attribute) has been used to define the characters that 28 | * come after it as erasable from the screen. 29 | *
30 | */ 31 | public final static int CHARACTER_ATTRIBUTE_PROTECTED = 1 << 7; 32 | /** Dim colors. Also known as faint or half intensity. */ 33 | public final static int CHARACTER_ATTRIBUTE_DIM = 1 << 8; 34 | /** If true (24-bit) color is used for the cell for foreground. */ 35 | private final static int CHARACTER_ATTRIBUTE_TRUECOLOR_FOREGROUND = 1 << 9; 36 | /** If true (24-bit) color is used for the cell for foreground. */ 37 | private final static int CHARACTER_ATTRIBUTE_TRUECOLOR_BACKGROUND= 1 << 10; 38 | 39 | public final static int COLOR_INDEX_FOREGROUND = 256; 40 | public final static int COLOR_INDEX_BACKGROUND = 257; 41 | public final static int COLOR_INDEX_CURSOR = 258; 42 | 43 | /** The 256 standard color entries and the three special (foreground, background and cursor) ones. */ 44 | public final static int NUM_INDEXED_COLORS = 259; 45 | 46 | /** Normal foreground and background colors and no effects. */ 47 | final static long NORMAL = encode(COLOR_INDEX_FOREGROUND, COLOR_INDEX_BACKGROUND, 0); 48 | 49 | static long encode(int foreColor, int backColor, int effect) { 50 | long result = effect & 0b111111111; 51 | if ((0xff000000 & foreColor) == 0xff000000) { 52 | // 24-bit color. 53 | result |= CHARACTER_ATTRIBUTE_TRUECOLOR_FOREGROUND | ((foreColor & 0x00ffffffL) << 40L); 54 | } else { 55 | // Indexed color. 56 | result |= (foreColor & 0b111111111L) << 40; 57 | } 58 | if ((0xff000000 & backColor) == 0xff000000) { 59 | // 24-bit color. 60 | result |= CHARACTER_ATTRIBUTE_TRUECOLOR_BACKGROUND | ((backColor & 0x00ffffffL) << 16L); 61 | } else { 62 | // Indexed color. 63 | result |= (backColor & 0b111111111L) << 16L; 64 | } 65 | 66 | return result; 67 | } 68 | 69 | public static int decodeForeColor(long style) { 70 | if ((style & CHARACTER_ATTRIBUTE_TRUECOLOR_FOREGROUND) == 0) { 71 | return (int) ((style >>> 40) & 0b111111111L); 72 | } else { 73 | return 0xff000000 | (int) ((style >>> 40) & 0x00ffffffL); 74 | } 75 | 76 | } 77 | 78 | public static int decodeBackColor(long style) { 79 | if ((style & CHARACTER_ATTRIBUTE_TRUECOLOR_BACKGROUND) == 0) { 80 | return (int) ((style >>> 16) & 0b111111111L); 81 | } else { 82 | return 0xff000000 | (int) ((style >>> 16) & 0x00ffffffL); 83 | } 84 | } 85 | 86 | public static int decodeEffect(long style) { 87 | return (int) (style & 0b11111111111); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /terminal-emulator/src/main/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH:= $(call my-dir) 2 | include $(CLEAR_VARS) 3 | LOCAL_MODULE:= libtermux 4 | LOCAL_SRC_FILES:= termux.c 5 | include $(BUILD_SHARED_LIBRARY) 6 | -------------------------------------------------------------------------------- /terminal-emulator/src/test/java/com/termux/terminal/ByteQueueTest.java: -------------------------------------------------------------------------------- 1 | package com.termux.terminal; 2 | 3 | import junit.framework.TestCase; 4 | 5 | public class ByteQueueTest extends TestCase { 6 | 7 | private static void assertArrayEquals(byte[] expected, byte[] actual) { 8 | if (expected.length != actual.length) { 9 | fail("Difference array length"); 10 | } 11 | for (int i = 0; i < expected.length; i++) { 12 | if (expected[i] != actual[i]) { 13 | fail("Inequals at index=" + i + ", expected=" + (int) expected[i] + ", actual=" + (int) actual[i]); 14 | } 15 | } 16 | } 17 | 18 | public void testCompleteWrites() throws Exception { 19 | ByteQueue q = new ByteQueue(10); 20 | assertEquals(true, q.write(new byte[]{1, 2, 3}, 0, 3)); 21 | 22 | byte[] arr = new byte[10]; 23 | assertEquals(3, q.read(arr, true)); 24 | assertArrayEquals(new byte[]{1, 2, 3}, new byte[]{arr[0], arr[1], arr[2]}); 25 | 26 | assertEquals(true, q.write(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 0, 10)); 27 | assertEquals(10, q.read(arr, true)); 28 | assertArrayEquals(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, arr); 29 | } 30 | 31 | public void testQueueWraparound() throws Exception { 32 | ByteQueue q = new ByteQueue(10); 33 | 34 | byte[] origArray = new byte[]{1, 2, 3, 4, 5, 6}; 35 | byte[] readArray = new byte[origArray.length]; 36 | for (int i = 0; i < 20; i++) { 37 | q.write(origArray, 0, origArray.length); 38 | assertEquals(origArray.length, q.read(readArray, true)); 39 | assertArrayEquals(origArray, readArray); 40 | } 41 | } 42 | 43 | public void testWriteNotesClosing() throws Exception { 44 | ByteQueue q = new ByteQueue(10); 45 | q.close(); 46 | assertEquals(false, q.write(new byte[]{1, 2, 3}, 0, 3)); 47 | } 48 | 49 | public void testReadNonBlocking() throws Exception { 50 | ByteQueue q = new ByteQueue(10); 51 | assertEquals(0, q.read(new byte[128], false)); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /terminal-emulator/src/test/java/com/termux/terminal/ControlSequenceIntroducerTest.java: -------------------------------------------------------------------------------- 1 | package com.termux.terminal; 2 | 3 | /** "\033[" is the Control Sequence Introducer char sequence (CSI). */ 4 | public class ControlSequenceIntroducerTest extends TerminalTestCase { 5 | 6 | /** CSI Ps P Scroll down Ps lines (default = 1) (SD). */ 7 | public void testCsiT() { 8 | withTerminalSized(4, 6).enterString("1\r\n2\r\n3\r\nhi\033[2Tyo\r\nA\r\nB").assertLinesAre(" ", " ", "1 ", "2 yo", "A ", 9 | "Bi "); 10 | // Default value (1): 11 | withTerminalSized(4, 6).enterString("1\r\n2\r\n3\r\nhi\033[Tyo\r\nA\r\nB").assertLinesAre(" ", "1 ", "2 ", "3 yo", "Ai ", 12 | "B "); 13 | } 14 | 15 | /** CSI Ps S Scroll up Ps lines (default = 1) (SU). */ 16 | public void testCsiS() { 17 | // The behaviour here is a bit inconsistent between terminals - this is how the OS X Terminal.app does it: 18 | withTerminalSized(3, 4).enterString("1\r\n2\r\n3\r\nhi\033[2Sy").assertLinesAre("3 ", "hi ", " ", " y"); 19 | // Default value (1): 20 | withTerminalSized(3, 4).enterString("1\r\n2\r\n3\r\nhi\033[Sy").assertLinesAre("2 ", "3 ", "hi ", " y"); 21 | } 22 | 23 | /** CSI Ps X Erase Ps Character(s) (default = 1) (ECH). */ 24 | public void testCsiX() { 25 | // See https://code.google.com/p/chromium/issues/detail?id=212712 where test was extraced from. 26 | withTerminalSized(13, 2).enterString("abcdefghijkl\b\b\b\b\b\033[X").assertLinesAre("abcdefg ijkl ", " "); 27 | withTerminalSized(13, 2).enterString("abcdefghijkl\b\b\b\b\b\033[1X").assertLinesAre("abcdefg ijkl ", " "); 28 | withTerminalSized(13, 2).enterString("abcdefghijkl\b\b\b\b\b\033[2X").assertLinesAre("abcdefg jkl ", " "); 29 | withTerminalSized(13, 2).enterString("abcdefghijkl\b\b\b\b\b\033[20X").assertLinesAre("abcdefg ", " "); 30 | } 31 | 32 | /** CSI Pm m Set SGR parameter(s) from semicolon-separated list Pm. */ 33 | public void testCsiSGRParameters() { 34 | // Set more parameters (19) than supported (16). Additional parameters should be silently consumed. 35 | withTerminalSized(3, 2).enterString("\033[0;38;2;255;255;255;48;2;0;0;0;1;2;3;4;5;7;8;9mabc").assertLinesAre("abc", " "); 36 | } 37 | 38 | /** CSI Ps b Repeat the preceding graphic character Ps times (REP). */ 39 | public void testRepeat() { 40 | withTerminalSized(3, 2).enterString("a\033[b").assertLinesAre("aa ", " "); 41 | withTerminalSized(3, 2).enterString("a\033[2b").assertLinesAre("aaa", " "); 42 | // When no char has been output we ignore REP: 43 | withTerminalSized(3, 2).enterString("\033[b").assertLinesAre(" ", " "); 44 | // This shows that REP outputs the last emitted code point and not the one relative to the 45 | // current cursor position: 46 | withTerminalSized(5, 2).enterString("abcde\033[2G\033[2b\n").assertLinesAre("aeede", " "); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /terminal-emulator/src/test/java/com/termux/terminal/DecSetTest.java: -------------------------------------------------------------------------------- 1 | package com.termux.terminal; 2 | 3 | /** 4 | *5 | * "CSI ? Pm h", DEC Private Mode Set (DECSET) 6 | *7 | * 8 | * and 9 | * 10 | *
11 | * "CSI ? Pm l", DEC Private Mode Reset (DECRST) 12 | *13 | * 14 | * controls various aspects of the terminal 15 | */ 16 | public class DecSetTest extends TerminalTestCase { 17 | 18 | /** DECSET 25, DECTCEM, controls visibility of the cursor. */ 19 | public void testShowHideCursor() { 20 | withTerminalSized(3, 3); 21 | assertTrue("Initially the cursor should be visible", mTerminal.isShowingCursor()); 22 | enterString("\033[?25l"); // Hide Cursor (DECTCEM). 23 | assertFalse(mTerminal.isShowingCursor()); 24 | enterString("\033[?25h"); // Show Cursor (DECTCEM). 25 | assertTrue(mTerminal.isShowingCursor()); 26 | 27 | enterString("\033[?25l"); // Hide Cursor (DECTCEM), again. 28 | assertFalse(mTerminal.isShowingCursor()); 29 | mTerminal.reset(); 30 | assertTrue("Resetting the terminal should show the cursor", mTerminal.isShowingCursor()); 31 | 32 | enterString("\033[?25l"); 33 | assertFalse(mTerminal.isShowingCursor()); 34 | enterString("\033c"); // RIS resetting should reveal cursor. 35 | assertTrue(mTerminal.isShowingCursor()); 36 | } 37 | 38 | /** DECSET 2004, controls bracketed paste mode. */ 39 | public void testBracketedPasteMode() { 40 | withTerminalSized(3, 3); 41 | 42 | mTerminal.paste("a"); 43 | assertEquals("Pasting 'a' should output 'a' when bracketed paste mode is disabled", "a", mOutput.getOutputAndClear()); 44 | 45 | enterString("\033[?2004h"); // Enable bracketed paste mode. 46 | mTerminal.paste("a"); 47 | assertEquals("Pasting when in bracketed paste mode should be bracketed", "\033[200~a\033[201~", mOutput.getOutputAndClear()); 48 | 49 | enterString("\033[?2004l"); // Disable bracketed paste mode. 50 | mTerminal.paste("a"); 51 | assertEquals("Pasting 'a' should output 'a' when bracketed paste mode is disabled", "a", mOutput.getOutputAndClear()); 52 | 53 | enterString("\033[?2004h"); // Enable bracketed paste mode, again. 54 | mTerminal.paste("a"); 55 | assertEquals("Pasting when in bracketed paste mode again should be bracketed", "\033[200~a\033[201~", mOutput.getOutputAndClear()); 56 | 57 | mTerminal.paste("\033ab\033cd\033"); 58 | assertEquals("Pasting an escape character should not input it", "\033[200~abcd\033[201~", mOutput.getOutputAndClear()); 59 | mTerminal.paste("\u0081ab\u0081cd\u009F"); 60 | assertEquals("Pasting C1 control codes should not input it", "\033[200~abcd\033[201~", mOutput.getOutputAndClear()); 61 | 62 | mTerminal.reset(); 63 | mTerminal.paste("a"); 64 | assertEquals("Terminal reset() should disable bracketed paste mode", "a", mOutput.getOutputAndClear()); 65 | } 66 | 67 | /** DECSET 7, DECAWM, controls wraparound mode. */ 68 | public void testWrapAroundMode() { 69 | // Default with wraparound: 70 | withTerminalSized(3, 3).enterString("abcd").assertLinesAre("abc", "d ", " "); 71 | // With wraparound disabled: 72 | withTerminalSized(3, 3).enterString("\033[?7labcd").assertLinesAre("abd", " ", " "); 73 | enterString("efg").assertLinesAre("abg", " ", " "); 74 | // Re-enabling wraparound: 75 | enterString("\033[?7hhij").assertLinesAre("abh", "ij ", " "); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /terminal-emulator/src/test/java/com/termux/terminal/DeviceControlStringTest.java: -------------------------------------------------------------------------------- 1 | package com.termux.terminal; 2 | 3 | /** 4 | * "\033P" is a device control string. 5 | */ 6 | public class DeviceControlStringTest extends TerminalTestCase { 7 | 8 | private static String hexEncode(String s) { 9 | StringBuilder result = new StringBuilder(); 10 | for (int i = 0; i < s.length(); i++) 11 | result.append(String.format("%02X", (int) s.charAt(i))); 12 | return result.toString(); 13 | } 14 | 15 | private void assertCapabilityResponse(String cap, String expectedResponse) { 16 | String input = "\033P+q" + hexEncode(cap) + "\033\\"; 17 | assertEnteringStringGivesResponse(input, "\033P1+r" + hexEncode(cap) + "=" + hexEncode(expectedResponse) + "\033\\"); 18 | } 19 | 20 | public void testReportColorsAndName() { 21 | // Request Termcap/Terminfo String. The string following the "q" is a list of names encoded in 22 | // hexadecimal (2 digits per character) separated by ; which correspond to termcap or terminfo key 23 | // names. 24 | // Two special features are also recognized, which are not key names: Co for termcap colors (or colors 25 | // for terminfo colors), and TN for termcap name (or name for terminfo name). 26 | // xterm responds with DCS 1 + r P t ST for valid requests, adding to P t an = , and the value of the 27 | // corresponding string that xterm would send, or DCS 0 + r P t ST for invalid requests. The strings are 28 | // encoded in hexadecimal (2 digits per character). 29 | withTerminalSized(3, 3).enterString("A"); 30 | assertCapabilityResponse("Co", "256"); 31 | assertCapabilityResponse("colors", "256"); 32 | assertCapabilityResponse("TN", "xterm"); 33 | assertCapabilityResponse("name", "xterm"); 34 | enterString("B").assertLinesAre("AB ", " ", " "); 35 | } 36 | 37 | public void testReportKeys() { 38 | withTerminalSized(3, 3); 39 | assertCapabilityResponse("kB", "\033[Z"); 40 | } 41 | 42 | public void testReallyLongDeviceControlString() { 43 | withTerminalSized(3, 3).enterString("\033P"); 44 | for (int i = 0; i < 10000; i++) { 45 | enterString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); 46 | } 47 | // The terminal should ignore the overlong DCS sequence and continue printing "aaa." and fill at least the first two lines with 48 | // them: 49 | assertLineIs(0, "aaa"); 50 | assertLineIs(1, "aaa"); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /terminal-emulator/src/test/java/com/termux/terminal/HistoryTest.java: -------------------------------------------------------------------------------- 1 | package com.termux.terminal; 2 | 3 | 4 | public class HistoryTest extends TerminalTestCase { 5 | 6 | public void testHistory() { 7 | final int rows = 3; 8 | final int cols = 3; 9 | withTerminalSized(cols, rows).enterString("111222333444555666777888999"); 10 | assertCursorAt(2, 2); 11 | assertLinesAre("777", "888", "999"); 12 | assertHistoryStartsWith("666", "555"); 13 | 14 | mTerminal.resize(cols, 2); 15 | assertHistoryStartsWith("777", "666", "555"); 16 | 17 | mTerminal.resize(cols, 3); 18 | assertHistoryStartsWith("666", "555"); 19 | } 20 | 21 | public void testHistoryWithScrollRegion() { 22 | // "CSI P_s ; P_s r" - set Scrolling Region [top;bottom] (default = full size of window) (DECSTBM). 23 | withTerminalSized(3, 4).enterString("111222333444"); 24 | assertLinesAre("111", "222", "333", "444"); 25 | enterString("\033[2;3r"); 26 | // NOTE: "DECSTBM moves the cursor to column 1, line 1 of the page." 27 | assertCursorAt(0, 0); 28 | enterString("\nCDEFGH").assertLinesAre("111", "CDE", "FGH", "444"); 29 | enterString("IJK").assertLinesAre("111", "FGH", "IJK", "444").assertHistoryStartsWith("CDE"); 30 | enterString("LMN").assertLinesAre("111", "IJK", "LMN", "444").assertHistoryStartsWith("FGH", "CDE"); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /terminal-emulator/src/test/java/com/termux/terminal/ScreenBufferTest.java: -------------------------------------------------------------------------------- 1 | package com.termux.terminal; 2 | 3 | public class ScreenBufferTest extends TerminalTestCase { 4 | 5 | public void testBasics() { 6 | TerminalBuffer screen = new TerminalBuffer(5, 3, 3); 7 | assertEquals("", screen.getTranscriptText()); 8 | screen.setChar(0, 0, 'a', 0); 9 | assertEquals("a", screen.getTranscriptText()); 10 | screen.setChar(0, 0, 'b', 0); 11 | assertEquals("b", screen.getTranscriptText()); 12 | screen.setChar(2, 0, 'c', 0); 13 | assertEquals("b c", screen.getTranscriptText()); 14 | screen.setChar(2, 2, 'f', 0); 15 | assertEquals("b c\n\n f", screen.getTranscriptText()); 16 | screen.blockSet(0, 0, 2, 2, 'X', 0); 17 | } 18 | 19 | public void testBlockSet() { 20 | TerminalBuffer screen = new TerminalBuffer(5, 3, 3); 21 | screen.blockSet(0, 0, 2, 2, 'X', 0); 22 | assertEquals("XX\nXX", screen.getTranscriptText()); 23 | screen.blockSet(1, 1, 2, 2, 'Y', 0); 24 | assertEquals("XX\nXYY\n YY", screen.getTranscriptText()); 25 | } 26 | 27 | public void testGetSelectedText() { 28 | withTerminalSized(5, 3).enterString("ABCDEFGHIJ").assertLinesAre("ABCDE", "FGHIJ", " "); 29 | assertEquals("AB", mTerminal.getSelectedText(0, 0, 1, 0)); 30 | assertEquals("BC", mTerminal.getSelectedText(1, 0, 2, 0)); 31 | assertEquals("CDE", mTerminal.getSelectedText(2, 0, 4, 0)); 32 | assertEquals("FG", mTerminal.getSelectedText(0, 1, 1, 1)); 33 | assertEquals("GH", mTerminal.getSelectedText(1, 1, 2, 1)); 34 | assertEquals("HIJ", mTerminal.getSelectedText(2, 1, 4, 1)); 35 | 36 | assertEquals("ABCDEFG", mTerminal.getSelectedText(0, 0, 1, 1)); 37 | withTerminalSized(5, 3).enterString("ABCDE\r\nFGHIJ").assertLinesAre("ABCDE", "FGHIJ", " "); 38 | assertEquals("ABCDE\nFG", mTerminal.getSelectedText(0, 0, 1, 1)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /terminal-emulator/src/test/java/com/termux/terminal/TextStyleTest.java: -------------------------------------------------------------------------------- 1 | package com.termux.terminal; 2 | 3 | import junit.framework.TestCase; 4 | 5 | public class TextStyleTest extends TestCase { 6 | 7 | private static final int[] ALL_EFFECTS = new int[]{0, TextStyle.CHARACTER_ATTRIBUTE_BOLD, TextStyle.CHARACTER_ATTRIBUTE_ITALIC, 8 | TextStyle.CHARACTER_ATTRIBUTE_UNDERLINE, TextStyle.CHARACTER_ATTRIBUTE_BLINK, TextStyle.CHARACTER_ATTRIBUTE_INVERSE, 9 | TextStyle.CHARACTER_ATTRIBUTE_INVISIBLE, TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH, TextStyle.CHARACTER_ATTRIBUTE_PROTECTED, 10 | TextStyle.CHARACTER_ATTRIBUTE_DIM}; 11 | 12 | public void testEncodingSingle() { 13 | for (int fx : ALL_EFFECTS) { 14 | for (int fg = 0; fg < TextStyle.NUM_INDEXED_COLORS; fg++) { 15 | for (int bg = 0; bg < TextStyle.NUM_INDEXED_COLORS; bg++) { 16 | long encoded = TextStyle.encode(fg, bg, fx); 17 | assertEquals(fg, TextStyle.decodeForeColor(encoded)); 18 | assertEquals(bg, TextStyle.decodeBackColor(encoded)); 19 | assertEquals(fx, TextStyle.decodeEffect(encoded)); 20 | } 21 | } 22 | } 23 | } 24 | 25 | public void testEncoding24Bit() { 26 | int[] values = {255, 240, 127, 1, 0}; 27 | for (int red : values) { 28 | for (int green : values) { 29 | for (int blue : values) { 30 | int argb = 0xFF000000 | (red << 16) | (green << 8) | blue; 31 | long encoded = TextStyle.encode(argb, 0, 0); 32 | assertEquals(argb, TextStyle.decodeForeColor(encoded)); 33 | encoded = TextStyle.encode(0, argb, 0); 34 | assertEquals(argb, TextStyle.decodeBackColor(encoded)); 35 | } 36 | } 37 | } 38 | } 39 | 40 | 41 | public void testEncodingCombinations() { 42 | for (int f1 : ALL_EFFECTS) { 43 | for (int f2 : ALL_EFFECTS) { 44 | int combined = f1 | f2; 45 | assertEquals(combined, TextStyle.decodeEffect(TextStyle.encode(0, 0, combined))); 46 | } 47 | } 48 | } 49 | 50 | public void testEncodingStrikeThrough() { 51 | long encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND, 52 | TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH); 53 | assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0); 54 | } 55 | 56 | public void testEncodingProtected() { 57 | long encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND, 58 | TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH); 59 | assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0); 60 | encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND, 61 | TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH | TextStyle.CHARACTER_ATTRIBUTE_PROTECTED); 62 | assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) != 0); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /terminal-emulator/src/test/java/com/termux/terminal/WcWidthTest.java: -------------------------------------------------------------------------------- 1 | package com.termux.terminal; 2 | 3 | import junit.framework.TestCase; 4 | 5 | public class WcWidthTest extends TestCase { 6 | 7 | private static void assertWidthIs(int expectedWidth, int codePoint) { 8 | int wcWidth = WcWidth.width(codePoint); 9 | assertEquals(expectedWidth, wcWidth); 10 | } 11 | 12 | public void testPrintableAscii() { 13 | for (int i = 0x20; i <= 0x7E; i++) { 14 | assertWidthIs(1, i); 15 | } 16 | } 17 | 18 | public void testSomeWidthOne() { 19 | assertWidthIs(1, 'å'); 20 | assertWidthIs(1, 'ä'); 21 | assertWidthIs(1, 'ö'); 22 | assertWidthIs(1, 0x23F2); 23 | } 24 | 25 | public void testSomeWide() { 26 | assertWidthIs(2, 'A'); 27 | assertWidthIs(2, 'B'); 28 | assertWidthIs(2, 'C'); 29 | assertWidthIs(2, '中'); 30 | assertWidthIs(2, '文'); 31 | 32 | assertWidthIs(2, 0x679C); 33 | assertWidthIs(2, 0x679D); 34 | 35 | assertWidthIs(2, 0x2070E); 36 | assertWidthIs(2, 0x20731); 37 | 38 | assertWidthIs(1, 0x1F781); 39 | } 40 | 41 | public void testSomeNonWide() { 42 | assertWidthIs(1, 0x1D11E); 43 | assertWidthIs(1, 0x1D11F); 44 | } 45 | 46 | public void testCombining() { 47 | assertWidthIs(0, 0x0302); 48 | assertWidthIs(0, 0x0308); 49 | assertWidthIs(0, 0xFE0F); 50 | } 51 | 52 | public void testWordJoiner() { 53 | // https://en.wikipedia.org/wiki/Word_joiner 54 | // The word joiner (WJ) is a code point in Unicode used to separate words when using scripts 55 | // that do not use explicit spacing. It is encoded since Unicode version 3.2 56 | // (released in 2002) as U+2060 WORD JOINER (HTML ). 57 | // The word joiner does not produce any space, and prohibits a line break at its position. 58 | assertWidthIs(0, 0x2060); 59 | } 60 | 61 | public void testWatch() { 62 | 63 | } 64 | 65 | public void testSofthyphen() { 66 | // http://osdir.com/ml/internationalization.linux/2003-05/msg00006.html: 67 | // "Existing implementation practice in terminals is that the SOFT HYPHEN is 68 | // a spacing graphical character, and the purpose of my wcwidth() was to 69 | // predict the advancement of the cursor position after a string is sent to 70 | // a terminal. Hence, I have no choice but to keep wcwidth(SOFT HYPHEN) = 1. 71 | // VT100-style terminals do not hyphenate." 72 | assertWidthIs(1, 0x00AD); 73 | } 74 | 75 | public void testHangul() { 76 | assertWidthIs(1, 0x11A3); 77 | } 78 | 79 | public void testEmojis() { 80 | assertWidthIs(2, 0x1F428); // KOALA. 81 | assertWidthIs(2, 0x231a); // WATCH. 82 | assertWidthIs(2, 0x1F643); // UPSIDE-DOWN FACE (Unicode 8). 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /terminal-view/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.jfrog.bintray" version "1.7.3" 3 | id "com.github.dcendents.android-maven" version "2.0" 4 | } 5 | 6 | apply plugin: 'com.android.library' 7 | 8 | ext { 9 | bintrayName = 'terminal-view' 10 | publishedGroupId = 'com.termux' 11 | libraryName = 'TerminalView' 12 | artifact = 'terminal-view' 13 | libraryDescription = 'The terminal view used in Termux' 14 | siteUrl = 'https://github.com/termux/termux' 15 | gitUrl = 'https://github.com/termux/termux.git' 16 | libraryVersion = '0.50' 17 | } 18 | 19 | android { 20 | compileSdkVersion 27 21 | buildToolsVersion "27.0.2" 22 | 23 | dependencies { 24 | implementation 'com.android.support:support-annotations:27.0.0' 25 | api project(":terminal-emulator") 26 | } 27 | 28 | defaultConfig { 29 | minSdkVersion 21 30 | targetSdkVersion 27 31 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 32 | } 33 | 34 | buildTypes { 35 | release { 36 | minifyEnabled false 37 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 38 | } 39 | } 40 | } 41 | 42 | dependencies { 43 | testImplementation 'junit:junit:4.12' 44 | } 45 | 46 | apply from: '../scripts/bintray-publish.gradle' 47 | -------------------------------------------------------------------------------- /terminal-view/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/fornwall/lib/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /terminal-view/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 |