6 Commits

Author SHA1 Message Date
rudi bf5eefd190 Fix the builder
Build and Test a3270 / Build Android APK (push) Successful in 1m55s
Release a3270 / Build & Publish Release (push) Successful in 1m44s
2026-09-15 23:49:48 +00:00
rudi c941fd3a2a Add new settings from j3270
Build and Test a3270 / Build Android APK (push) Failing after 26s
2026-09-15 23:40:22 +00:00
rudi b71f0f5ba4 Add icon
Build and Test a3270 / Build Android APK (push) Successful in 2m22s
2026-09-14 12:24:07 +00:00
rudi b81a244bb9 Add bugfixes from testing
Build and Test a3270 / Build Android APK (push) Successful in 2m36s
Release a3270 / Build & Publish Release (push) Successful in 3m58s
2026-09-12 17:22:19 -04:00
rudi 351379b5b3 Merge branch 'main' of git.hugfreevikings.wtf:rudi/a3270
Build and Test a3270 / Build Android APK (push) Successful in 4m20s
# Conflicts:
#	build.sh
#	src/main/java/haus/nightmare/a3270/ui/TerminalView.kt
2026-09-12 15:54:59 +00:00
rudi eba1406cbb updtaes from j3270 2026-09-12 15:50:42 +00:00
23 changed files with 996 additions and 15 deletions
+2
View File
@@ -36,6 +36,8 @@ jobs:
- name: Set up Android SDK
uses: android-actions/setup-android@v3
with:
packages: ''
- name: Make scripts executable
run: |
+2
View File
@@ -34,6 +34,8 @@ jobs:
- name: Set up Android SDK
uses: android-actions/setup-android@v3
with:
packages: ''
- name: Make scripts executable
run: |
+1
View File
@@ -10,3 +10,4 @@ captures/
*.jar
!gradle/wrapper/gradle-wrapper.jar
*.txt
*.log*
+146
View File
@@ -0,0 +1,146 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="124 738 320 320"
width="320"
height="320"
xml:space="preserve"
id="svg_a3270_icon"
version="1.1">
<metadata id="metadata8">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<!-- Pure black background (#000000) for classic 3270 CRT terminal look -->
<rect x="124" y="738" width="320" height="320" fill="#000000" />
<g transform="matrix(1.3333333,0,0,-1.3333333,0,1122.6667)" id="g10">
<g transform="scale(0.1)" id="g12">
<g id="g14">
<!-- Green Terminal Screen Frame -->
<path
id="path28"
style="fill:none;stroke:#00ff00;stroke-width:3.75079012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="M 1177.27,2220.71 V 1142.45 H 3084.31 V 2220.71 H 1177.27" />
<!-- Blue Wave / Exponential Curve -->
<path
id="path30"
style="fill:none;stroke:#0000ff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1172.41,1137.58 79.1,-1.22 -0.21,-0.38 c 1.08,0 2.18,0 3.27,0 762.95,0 1464.86,417.88 1829.53,1089.21" />
<!-- Red Circular Arc -->
<path
id="path32"
style="fill:none;stroke:#ff0000;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2593.86,1620.73 -1.22,-28 -0.08,-0.08 c 0.43,-250.15 202.81,-452.72 452.3,-452.72 0.14,0 0.28,0 0.42,0 l 43.9,2.52" />
<!-- Yellow Smiley Head -->
<path
id="path34"
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2976,1363.94 v 0.08 c 44.73,34.29 71.21,87.25 71.8,143.61 l 0.01,-0.15 c -0.06,97.48 -79.11,176.47 -176.59,176.47 -0.36,0 -0.73,0 -1.09,0 l -0.07,0.03 c -0.25,0 -0.51,0 -0.75,0 -37.38,0 -73.79,-11.94 -103.92,-34.08 l 0.02,-0.03 c -44.38,-34.04 -70.82,-86.47 -71.8,-142.39 l 0.06,0.04 c 0.54,-97.43 79.04,-176.47 176.46,-177.69 l 0.08,0.02 c 0.64,-0.01 1.29,-0.01 1.92,-0.01 37.4,0 73.82,11.94 103.96,34.08" />
<!-- 'l' -->
<path
id="path36"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
<path
id="path38"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
<!-- 'i' -->
<path
id="path40"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
<path
id="path42"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
<!-- 'b' -->
<path
id="path44"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
<path
id="path46"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
<!-- '3' -->
<path
id="path48"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
<path
id="path50"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
<!-- '2' -->
<path
id="path52"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
<path
id="path54"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
<!-- '7' -->
<path
id="path56"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
<path
id="path58"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
<!-- '0' -->
<path
id="path60"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
<path
id="path62"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
<!-- 'j' -->
<path
id="path64"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
<path
id="path66"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
<!-- Smile -->
<path
id="path68"
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2778.85,1478.34 0.04,-0.02 c 21.03,-31.91 56.69,-51.12 94.91,-51.12 0.41,0 0.83,0 1.24,0.01 l -0.02,0.07 c 43.88,0.19 84.11,24.51 104.66,63.28" />
<!-- Left Eye (cyan) -->
<path
id="path70"
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2820.22,1595.17 0.01,0.01 c -14.62,-1.91 -25.56,-14.37 -25.56,-29.11 0,-0.03 0,-0.07 0,-0.1 l -0.02,-0.01 c 0,-0.03 0,-0.07 0,-0.1 0,-16.09 13.04,-29.14 29.13,-29.14 0.43,0 0.87,0.01 1.29,0.03 h 3.67 0.01 c 14.61,1.91 25.55,14.37 25.55,29.11 0,0.03 0,0.07 0,0.1 l 0.02,0.01 c 0,0.04 0,0.08 0,0.12 0,16.07 -13.03,29.09 -29.09,29.09 -0.03,0 -0.08,0 -0.12,0 l -4.89,-0.01" />
<!-- Right Eye (cyan) -->
<path
id="path72"
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2921.23,1569.61 v -3.65 l -0.01,-0.01 c 0,-0.27 -0.01,-0.54 -0.01,-0.81 0,-18.22 14.66,-33.05 32.87,-33.26 l 0.03,0.01 c 0.28,0 0.57,-0.01 0.84,-0.01 17.29,0 31.72,13.22 33.24,30.44 l -0.02,3.64 0.02,0.02 c 0,0.27 0.01,0.54 0.01,0.81 0,18.37 -14.9,33.28 -33.28,33.28 -0.26,0 -0.54,-0.01 -0.81,-0.02 l -0.02,-0.03 c -17.14,-0.18 -31.36,-13.34 -32.86,-30.42" />
<!-- Nose -->
<path
id="path74"
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2892.03,1528.23 -29.21,-20.68" />
<path
id="path76"
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2862.82,1507.55 h 32.86" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

+146
View File
@@ -0,0 +1,146 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="144 814 280 169"
width="280"
height="169"
xml:space="preserve"
id="svg_lib3270j_cropped"
version="1.1">
<metadata id="metadata8">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<!-- Pure black background (#000000) for classic 3270 CRT terminal look -->
<rect x="144" y="814" width="280" height="169" fill="#000000" />
<g transform="matrix(1.3333333,0,0,-1.3333333,0,1122.6667)" id="g10">
<g transform="scale(0.1)" id="g12">
<g id="g14">
<!-- Green Terminal Screen Frame -->
<path
id="path28"
style="fill:none;stroke:#00ff00;stroke-width:3.75079012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="M 1177.27,2220.71 V 1142.45 H 3084.31 V 2220.71 H 1177.27" />
<!-- Blue Wave / Exponential Curve -->
<path
id="path30"
style="fill:none;stroke:#0000ff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1172.41,1137.58 79.1,-1.22 -0.21,-0.38 c 1.08,0 2.18,0 3.27,0 762.95,0 1464.86,417.88 1829.53,1089.21" />
<!-- Red Circular Arc -->
<path
id="path32"
style="fill:none;stroke:#ff0000;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2593.86,1620.73 -1.22,-28 -0.08,-0.08 c 0.43,-250.15 202.81,-452.72 452.3,-452.72 0.14,0 0.28,0 0.42,0 l 43.9,2.52" />
<!-- Yellow Smiley Head -->
<path
id="path34"
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2976,1363.94 v 0.08 c 44.73,34.29 71.21,87.25 71.8,143.61 l 0.01,-0.15 c -0.06,97.48 -79.11,176.47 -176.59,176.47 -0.36,0 -0.73,0 -1.09,0 l -0.07,0.03 c -0.25,0 -0.51,0 -0.75,0 -37.38,0 -73.79,-11.94 -103.92,-34.08 l 0.02,-0.03 c -44.38,-34.04 -70.82,-86.47 -71.8,-142.39 l 0.06,0.04 c 0.54,-97.43 79.04,-176.47 176.46,-177.69 l 0.08,0.02 c 0.64,-0.01 1.29,-0.01 1.92,-0.01 37.4,0 73.82,11.94 103.96,34.08" />
<!-- 'l' -->
<path
id="path36"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
<path
id="path38"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
<!-- 'i' -->
<path
id="path40"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
<path
id="path42"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
<!-- 'b' -->
<path
id="path44"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
<path
id="path46"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
<!-- '3' -->
<path
id="path48"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
<path
id="path50"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
<!-- '2' -->
<path
id="path52"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
<path
id="path54"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
<!-- '7' -->
<path
id="path56"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
<path
id="path58"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
<!-- '0' -->
<path
id="path60"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
<path
id="path62"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
<!-- 'j' -->
<path
id="path64"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
<path
id="path66"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
<!-- Smile -->
<path
id="path68"
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2778.85,1478.34 0.04,-0.02 c 21.03,-31.91 56.69,-51.12 94.91,-51.12 0.41,0 0.83,0 1.24,0.01 l -0.02,0.07 c 43.88,0.19 84.11,24.51 104.66,63.28" />
<!-- Left Eye (cyan) -->
<path
id="path70"
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2820.22,1595.17 0.01,0.01 c -14.62,-1.91 -25.56,-14.37 -25.56,-29.11 0,-0.03 0,-0.07 0,-0.1 l -0.02,-0.01 c 0,-0.03 0,-0.07 0,-0.1 0,-16.09 13.04,-29.14 29.13,-29.14 0.43,0 0.87,0.01 1.29,0.03 h 3.67 0.01 c 14.61,1.91 25.55,14.37 25.55,29.11 0,0.03 0,0.07 0,0.1 l 0.02,0.01 c 0,0.04 0,0.08 0,0.12 0,16.07 -13.03,29.09 -29.09,29.09 -0.03,0 -0.08,0 -0.12,0 l -4.89,-0.01" />
<!-- Right Eye (cyan) -->
<path
id="path72"
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2921.23,1569.61 v -3.65 l -0.01,-0.01 c 0,-0.27 -0.01,-0.54 -0.01,-0.81 0,-18.22 14.66,-33.05 32.87,-33.26 l 0.03,0.01 c 0.28,0 0.57,-0.01 0.84,-0.01 17.29,0 31.72,13.22 33.24,30.44 l -0.02,3.64 0.02,0.02 c 0,0.27 0.01,0.54 0.01,0.81 0,18.37 -14.9,33.28 -33.28,33.28 -0.26,0 -0.54,-0.01 -0.81,-0.02 l -0.02,-0.03 c -17.14,-0.18 -31.36,-13.34 -32.86,-30.42" />
<!-- Nose -->
<path
id="path74"
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2892.03,1528.23 -29.21,-20.68" />
<path
id="path76"
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2862.82,1507.55 h 32.86" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

+10 -5
View File
@@ -45,14 +45,19 @@ if [ ! -d "$J3270_PATH/lib3270j" ]; then
exit 1
fi
echo "Building debug APK..."
./gradlew assembleDebug --no-daemon
if [ $# -gt 0 ]; then
echo "Running: ./gradlew $@"
./gradlew "$@"
else
echo "Building debug APK..."
./gradlew assembleDebug --no-daemon
APK_PATH="$SCRIPT_DIR/build/outputs/apk/debug/a3270-debug.apk"
if [ -f "$APK_PATH" ]; then
APK_PATH="$SCRIPT_DIR/build/outputs/apk/debug/a3270-debug.apk"
if [ -f "$APK_PATH" ]; then
echo "=== Build Complete ==="
echo "APK created at: $APK_PATH"
ls -lh "$APK_PATH"
else
else
echo "Build completed, but APK not found at $APK_PATH"
fi
fi
Vendored
+20
View File
@@ -115,6 +115,26 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Prefer compatible JDK (JDK 17 or 21) if active Java is incompatible (Java 22+)
if [ -d "/Users/rudi/.sdkman/candidates/java/21.0.6-sem" ]; then
export JAVA_HOME="/Users/rudi/.sdkman/candidates/java/21.0.6-sem"
elif [ -d "$HOME/.sdkman/candidates/java/21.0.2-open" ]; then
CURRENT_JAVA_CMD="${JAVA_HOME:+$JAVA_HOME/bin/java}"
CURRENT_JAVA_CMD="${CURRENT_JAVA_CMD:-java}"
CURRENT_VER=$("$CURRENT_JAVA_CMD" -version 2>&1 | awk -F '"' '/version/ {print $2}' | cut -d'.' -f1)
if [ -z "$JAVA_HOME" ] || [ "$CURRENT_VER" -gt 21 ] 2>/dev/null; then
export JAVA_HOME="$HOME/.sdkman/candidates/java/21.0.2-open"
fi
fi
if [ -z "$ANDROID_HOME" ]; then
if [ -d "$HOME/Library/Android/sdk" ]; then
export ANDROID_HOME="$HOME/Library/Android/sdk"
elif [ -d "$HOME/Android/Sdk" ]; then
export ANDROID_HOME="$HOME/Android/Sdk"
fi
fi
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+2
View File
@@ -9,6 +9,8 @@
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="a3270"
android:supportsRtl="true"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+146
View File
@@ -0,0 +1,146 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="124 738 320 320"
width="320"
height="320"
xml:space="preserve"
id="svg_a3270_icon"
version="1.1">
<metadata id="metadata8">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<!-- Pure black background (#000000) for classic 3270 CRT terminal look -->
<rect x="124" y="738" width="320" height="320" fill="#000000" />
<g transform="matrix(1.3333333,0,0,-1.3333333,0,1122.6667)" id="g10">
<g transform="scale(0.1)" id="g12">
<g id="g14">
<!-- Green Terminal Screen Frame -->
<path
id="path28"
style="fill:none;stroke:#00ff00;stroke-width:3.75079012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="M 1177.27,2220.71 V 1142.45 H 3084.31 V 2220.71 H 1177.27" />
<!-- Blue Wave / Exponential Curve -->
<path
id="path30"
style="fill:none;stroke:#0000ff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1172.41,1137.58 79.1,-1.22 -0.21,-0.38 c 1.08,0 2.18,0 3.27,0 762.95,0 1464.86,417.88 1829.53,1089.21" />
<!-- Red Circular Arc -->
<path
id="path32"
style="fill:none;stroke:#ff0000;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2593.86,1620.73 -1.22,-28 -0.08,-0.08 c 0.43,-250.15 202.81,-452.72 452.3,-452.72 0.14,0 0.28,0 0.42,0 l 43.9,2.52" />
<!-- Yellow Smiley Head -->
<path
id="path34"
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2976,1363.94 v 0.08 c 44.73,34.29 71.21,87.25 71.8,143.61 l 0.01,-0.15 c -0.06,97.48 -79.11,176.47 -176.59,176.47 -0.36,0 -0.73,0 -1.09,0 l -0.07,0.03 c -0.25,0 -0.51,0 -0.75,0 -37.38,0 -73.79,-11.94 -103.92,-34.08 l 0.02,-0.03 c -44.38,-34.04 -70.82,-86.47 -71.8,-142.39 l 0.06,0.04 c 0.54,-97.43 79.04,-176.47 176.46,-177.69 l 0.08,0.02 c 0.64,-0.01 1.29,-0.01 1.92,-0.01 37.4,0 73.82,11.94 103.96,34.08" />
<!-- 'l' -->
<path
id="path36"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
<path
id="path38"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
<!-- 'i' -->
<path
id="path40"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
<path
id="path42"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
<!-- 'b' -->
<path
id="path44"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
<path
id="path46"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
<!-- '3' -->
<path
id="path48"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
<path
id="path50"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
<!-- '2' -->
<path
id="path52"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
<path
id="path54"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
<!-- '7' -->
<path
id="path56"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
<path
id="path58"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
<!-- '0' -->
<path
id="path60"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
<path
id="path62"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
<!-- 'j' -->
<path
id="path64"
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
<path
id="path66"
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
<!-- Smile -->
<path
id="path68"
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2778.85,1478.34 0.04,-0.02 c 21.03,-31.91 56.69,-51.12 94.91,-51.12 0.41,0 0.83,0 1.24,0.01 l -0.02,0.07 c 43.88,0.19 84.11,24.51 104.66,63.28" />
<!-- Left Eye (cyan) -->
<path
id="path70"
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2820.22,1595.17 0.01,0.01 c -14.62,-1.91 -25.56,-14.37 -25.56,-29.11 0,-0.03 0,-0.07 0,-0.1 l -0.02,-0.01 c 0,-0.03 0,-0.07 0,-0.1 0,-16.09 13.04,-29.14 29.13,-29.14 0.43,0 0.87,0.01 1.29,0.03 h 3.67 0.01 c 14.61,1.91 25.55,14.37 25.55,29.11 0,0.03 0,0.07 0,0.1 l 0.02,0.01 c 0,0.04 0,0.08 0,0.12 0,16.07 -13.03,29.09 -29.09,29.09 -0.03,0 -0.08,0 -0.12,0 l -4.89,-0.01" />
<!-- Right Eye (cyan) -->
<path
id="path72"
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2921.23,1569.61 v -3.65 l -0.01,-0.01 c 0,-0.27 -0.01,-0.54 -0.01,-0.81 0,-18.22 14.66,-33.05 32.87,-33.26 l 0.03,0.01 c 0.28,0 0.57,-0.01 0.84,-0.01 17.29,0 31.72,13.22 33.24,30.44 l -0.02,3.64 0.02,0.02 c 0,0.27 0.01,0.54 0.01,0.81 0,18.37 -14.9,33.28 -33.28,33.28 -0.26,0 -0.54,-0.01 -0.81,-0.02 l -0.02,-0.03 c -17.14,-0.18 -31.36,-13.34 -32.86,-30.42" />
<!-- Nose -->
<path
id="path74"
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2892.03,1528.23 -29.21,-20.68" />
<path
id="path76"
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 2862.82,1507.55 h 32.86" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

@@ -528,6 +528,7 @@ fun MainScreen(
val isCrosshairRuler by viewModel.isCrosshairRuler.collectAsState()
val cursorStyle by viewModel.cursorStyle.collectAsState()
val blockSelectMode by viewModel.blockSelectMode.collectAsState()
val blockPaste by viewModel.blockPaste.collectAsState()
val uiTheme by viewModel.uiTheme.collectAsState()
val isInsertMode by viewModel.isInsertMode.collectAsState()
val isDocMode by viewModel.isDocMode.collectAsState()
@@ -660,6 +661,9 @@ fun MainScreen(
cursorStyle = cursorStyle,
isInsertMode = isInsertMode,
blockSelectMode = blockSelectMode,
blockPaste = blockPaste,
onToggleBlockSelectMode = { viewModel.setBlockSelectMode(!blockSelectMode) },
onToggleBlockPaste = { viewModel.setBlockPaste(!blockPaste) },
fourColorOverride = fourColorOverride,
onPasteLineWrap = { text, wrapCol, wordWrap ->
viewModel.pasteLineWrap(text, wrapCol, wordWrap)
@@ -897,6 +901,7 @@ fun MainScreen(
initialCrosshairRuler = isCrosshairRuler,
initialCursorStyle = cursorStyle,
initialBlockSelectMode = blockSelectMode,
initialBlockPaste = blockPaste,
initialStartupBehavior = AppSettings.getStartupBehavior(context).name,
initialDynamicRows = AppSettings.getDynamicRows(context),
initialDynamicCols = AppSettings.getDynamicCols(context),
@@ -915,7 +920,7 @@ fun MainScreen(
showSettingsDialog = false
terminalInputViewRef?.showSoftKeyboard()
},
onSave = { mask, blink, haptic, verify, gfx, defaultCp, theme, crosshair, style, blockSel, startup, dynRows, dynCols,
onSave = { mask, blink, haptic, verify, gfx, defaultCp, theme, crosshair, style, blockSel, blockPst, startup, dynRows, dynCols,
doc, wrap, startCol, endCol, bell, bellCol, tabs, resetIns, fourCol, numLk, autoSk ->
showSettingsDialog = false
viewModel.updateSettings(mask, blink, haptic, verify, gfx, defaultCp)
@@ -923,6 +928,7 @@ fun MainScreen(
viewModel.setCrosshairRuler(crosshair)
viewModel.setCursorStyle(style)
viewModel.setBlockSelectMode(blockSel)
viewModel.setBlockPaste(blockPst)
AppSettings.setStartupBehavior(context, AppSettings.StartupBehavior.valueOf(startup))
AppSettings.setDynamicRows(context, dynRows)
AppSettings.setDynamicCols(context, dynCols)
@@ -61,6 +61,7 @@ sealed interface TerminalInputAction {
data class SetAplMode(val enabled: Boolean) : TerminalInputAction
data class SetCursor(val baddr: Int) : TerminalInputAction
data class LightPenSelect(val baddr: Int) : TerminalInputAction
data class PasteText(val text: String) : TerminalInputAction
}
data class UntrustedCertPromptState(
@@ -161,6 +162,9 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
private val _blockSelectMode = MutableStateFlow(AppSettings.isBlockSelectModeEnabled(application))
val blockSelectMode: StateFlow<Boolean> = _blockSelectMode.asStateFlow()
private val _blockPaste = MutableStateFlow(AppSettings.isBlockPasteEnabled(application))
val blockPaste: StateFlow<Boolean> = _blockPaste.asStateFlow()
private val _uiTheme = MutableStateFlow(AppSettings.getUiTheme(application))
val uiTheme: StateFlow<String> = _uiTheme.asStateFlow()
@@ -206,6 +210,17 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
_blockSelectMode.value = enabled
}
fun setBlockPaste(enabled: Boolean) {
val app = getApplication<Application>()
AppSettings.setBlockPasteEnabled(app, enabled)
_blockPaste.value = enabled
applyModeSettings()
}
fun toggleBlockPaste() {
setBlockPaste(!_blockPaste.value)
}
fun setUiTheme(theme: String) {
AppSettings.setUiTheme(getApplication(), theme)
_uiTheme.value = theme
@@ -270,7 +285,9 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
ip.isInsertOffOnAid = AppSettings.getInsertOffOnAid(app)
ip.isNumericFieldLock = AppSettings.getNumericFieldLock(app)
ip.isAutoSkipEnabled = AppSettings.getAutoSkipEnabled(app)
ip.isBlockPaste = AppSettings.isBlockPasteEnabled(app)
}
c.ps?.isBlockPaste = AppSettings.isBlockPasteEnabled(app)
_fourColorOverride.value = AppSettings.getFourColorOverride(app)
_isDocMode.value = c.isDocMode
_isWordWrap.value = c.isWordWrap
@@ -290,6 +307,7 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
_isCrosshairRuler.value = AppSettings.isCrosshairRulerEnabled(app)
_cursorStyle.value = AppSettings.getCursorStyle(app)
_blockSelectMode.value = AppSettings.isBlockSelectModeEnabled(app)
_blockPaste.value = AppSettings.isBlockPasteEnabled(app)
_uiTheme.value = AppSettings.getUiTheme(app)
_fourColorOverride.value = AppSettings.getFourColorOverride(app)
applyModeSettings()
@@ -391,6 +409,24 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
}
}
}
is TerminalInputAction.PasteText -> {
log.info("PasteText: len=${action.text.length}, curAddr=${buf.cursorAddress}, formatted=${buf.isFormatted}, isNvt=$isNvt")
ip.isKeyboardLocked = false
if (isNvt) {
for (ch in action.text) {
if (ch == '\n' || ch == '\r') {
c.sendNVTString("\r\n")
} else {
c.sendNVTChar(ch)
}
}
} else {
val app = getApplication<Application>()
val blockPaste = AppSettings.isBlockPasteEnabled(app)
ip.pasteText(action.text, true, false, blockPaste)
_screenVersion.value = System.currentTimeMillis()
}
}
is TerminalInputAction.SendAid -> {
if (isNvt) {
if (action.aidCode == AID_ENTER) {
@@ -789,21 +825,26 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
val globalVerify = AppSettings.isVerifyCertsEnabled(getApplication())
val effectiveVerify = tlsVerifyCert && globalVerify
val gMode = haus.nightmare.lib3270j.graphics.GraphicsMode.fromString(graphicsModeStr)
val effectiveLu = if (luName.isNotBlank()) luName.trim() else (parsedConfig.luName ?: "").trim()
_isTlsActive.value = effectiveTls
_isTlsVerified.value = effectiveVerify
_oiaText.value = "Connecting to $effectiveHost:$effectivePort" + (if (effectiveTls) " [TLS]" else "") + "..."
_oiaText.value = "Connecting to " +
(if (effectiveLu.isNotBlank()) "$effectiveLu@" else "") +
"$effectiveHost:$effectivePort" +
(if (effectiveTls) " [TLS]" else "") + "..."
val config = ConnectionConfig(effectiveHost, effectivePort, model).apply {
if (model == TerminalModel.IBM_DYNAMIC) {
setDynamicDimensions(dynamicRows, dynamicCols)
}
if (luName.isNotBlank()) {
setLuName(luName)
if (effectiveLu.isNotBlank()) {
setLuName(effectiveLu)
}
isUseTls = effectiveTls
isTlsVerifyCert = effectiveVerify
isTn3270eEnabled = effectiveTn3270e
isAutoSysUnlock = true
graphicsMode = gMode
codePage = codePageStr
@@ -1098,7 +1139,7 @@ class TerminalViewModel(application: Application) : AndroidViewModel(application
fun pasteString(text: String) {
if (text.isNotEmpty()) {
inputChannel.trySend(TerminalInputAction.TypeText(text))
inputChannel.trySend(TerminalInputAction.PasteText(text))
}
}
@@ -25,6 +25,7 @@ object AppSettings {
private const val KEY_CROSSHAIR_RULER = "crosshair_ruler"
private const val KEY_CURSOR_STYLE = "cursor_style"
private const val KEY_BLOCK_SELECT_MODE = "block_select_mode"
private const val KEY_BLOCK_PASTE = "block_paste"
private const val KEY_STARTUP_BEHAVIOR = "startup_behavior"
private const val KEY_DYNAMIC_ROWS = "dynamic_rows"
private const val KEY_DYNAMIC_COLS = "dynamic_cols"
@@ -137,6 +138,15 @@ object AppSettings {
getPrefs(context).edit().putBoolean(KEY_BLOCK_SELECT_MODE, enabled).apply()
}
// ========== Block Paste Mode ==========
fun isBlockPasteEnabled(context: Context): Boolean {
return getPrefs(context).getBoolean(KEY_BLOCK_PASTE, true)
}
fun setBlockPasteEnabled(context: Context, enabled: Boolean) {
getPrefs(context).edit().putBoolean(KEY_BLOCK_PASTE, enabled).apply()
}
// ========== Startup Behavior ==========
fun getStartupBehavior(context: Context): StartupBehavior {
val s = getPrefs(context).getString(KEY_STARTUP_BEHAVIOR, StartupBehavior.SHOW_CONNECT.name)
@@ -302,6 +312,7 @@ object AppSettings {
p.println("dynamicRows = ${getDynamicRows(context)}")
p.println("dynamicCols = ${getDynamicCols(context)}")
p.println("blockSelectMode = ${isBlockSelectModeEnabled(context)}")
p.println("blockPaste = ${isBlockPasteEnabled(context)}")
p.println()
p.println("[entryassist]")
@@ -369,6 +380,12 @@ object AppSettings {
"dynamicrows", "dynamic_rows" -> value.toIntOrNull()?.let { setDynamicRows(context, it) }
"dynamiccols", "dynamic_cols" -> value.toIntOrNull()?.let { setDynamicCols(context, it) }
"blockselectmode" -> setBlockSelectModeEnabled(context, value.toBoolean())
"blockpaste", "blockpastemode" -> setBlockPasteEnabled(context, value.toBoolean())
}
}
"clipboard" -> {
when (key.lowercase(Locale.US)) {
"blockpaste", "blockpastemode" -> setBlockPasteEnabled(context, value.toBoolean())
}
}
"entryassist" -> {
@@ -30,6 +30,7 @@ fun SettingsDialog(
initialCrosshairRuler: Boolean = false,
initialCursorStyle: String = "BLOCK",
initialBlockSelectMode: Boolean = false,
initialBlockPaste: Boolean = true,
initialStartupBehavior: String = "SHOW_CONNECT",
initialDynamicRows: Int = 62,
initialDynamicCols: Int = 160,
@@ -56,6 +57,7 @@ fun SettingsDialog(
crosshairRuler: Boolean,
cursorStyle: String,
blockSelectMode: Boolean,
blockPaste: Boolean,
startupBehavior: String,
dynamicRows: Int,
dynamicCols: Int,
@@ -86,6 +88,7 @@ fun SettingsDialog(
var crosshairRuler by remember { mutableStateOf(initialCrosshairRuler) }
var cursorStyle by remember { mutableStateOf(initialCursorStyle) }
var blockSelectMode by remember { mutableStateOf(initialBlockSelectMode) }
var blockPaste by remember { mutableStateOf(initialBlockPaste) }
var startupBehavior by remember { mutableStateOf(initialStartupBehavior) }
var dynamicRowsStr by remember { mutableStateOf(initialDynamicRows.toString()) }
var dynamicColsStr by remember { mutableStateOf(initialDynamicCols.toString()) }
@@ -262,6 +265,25 @@ fun SettingsDialog(
Spacer(modifier = Modifier.height(10.dp))
// Block Paste Mode
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Column(modifier = Modifier.weight(1f).padding(end = 12.dp)) {
Text("Block Paste Mode", fontSize = 14.sp, fontWeight = FontWeight.SemiBold, color = Color.White)
Text("Align multi-line clipboard pastes with cursor start column.", fontSize = 12.sp, color = Color.LightGray)
}
Switch(
checked = blockPaste,
onCheckedChange = { blockPaste = it },
colors = SwitchDefaults.colors(checkedThumbColor = Color.White, checkedTrackColor = Color(0xFF2B8A3E))
)
}
Spacer(modifier = Modifier.height(10.dp))
// Haptic Feedback
Row(
modifier = Modifier.fillMaxWidth(),
@@ -707,6 +729,7 @@ fun SettingsDialog(
crosshairRuler,
cursorStyle,
blockSelectMode,
blockPaste,
startupBehavior,
dRows,
dCols,
@@ -760,6 +783,7 @@ fun SettingsDialog(
crosshairRuler = haus.nightmare.a3270.storage.AppSettings.isCrosshairRulerEnabled(context)
cursorStyle = haus.nightmare.a3270.storage.AppSettings.getCursorStyle(context)
blockSelectMode = haus.nightmare.a3270.storage.AppSettings.isBlockSelectModeEnabled(context)
blockPaste = haus.nightmare.a3270.storage.AppSettings.isBlockPasteEnabled(context)
startupBehavior = haus.nightmare.a3270.storage.AppSettings.getStartupBehavior(context).name
dynamicRowsStr = haus.nightmare.a3270.storage.AppSettings.getDynamicRows(context).toString()
dynamicColsStr = haus.nightmare.a3270.storage.AppSettings.getDynamicCols(context).toString()
@@ -70,6 +70,7 @@ fun TerminalView(
cursorStyle: String = "BLOCK",
isInsertMode: Boolean = false,
blockSelectMode: Boolean = false,
blockPaste: Boolean = true,
fourColorOverride: Boolean = false,
searchHighlightAddr: Int = -1,
searchHighlightLen: Int = 0,
@@ -78,6 +79,8 @@ fun TerminalView(
onGraphicTouch: ((Int, Int, Int) -> Unit)? = null,
onPasteText: (String) -> Unit = {},
onPasteLineWrap: ((String, Int, Boolean) -> Unit)? = null,
onToggleBlockSelectMode: (() -> Unit)? = null,
onToggleBlockPaste: (() -> Unit)? = null,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
@@ -569,6 +572,26 @@ fun TerminalView(
showPasteWrapDialog = true
}
)
if (onToggleBlockSelectMode != null) {
DropdownMenuItem(
text = { Text(if (blockSelectMode) "✓ Block Select Mode" else " Block Select Mode") },
onClick = {
showContextMenu = false
onToggleBlockSelectMode()
}
)
}
if (onToggleBlockPaste != null) {
DropdownMenuItem(
text = { Text(if (blockPaste) "✓ Block Paste" else " Block Paste") },
onClick = {
showContextMenu = false
onToggleBlockPaste()
}
)
}
}
if (showPasteWrapDialog) {
@@ -728,7 +751,7 @@ private fun copySelection(
}
}
private fun getFgColorForAttribute(
internal fun getFgColorForAttribute(
ea: ExtendedAttribute,
currentFieldEa: ExtendedAttribute?,
currentFA: Byte,
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#000000"
android:pathData="M0,0h108v108h-108z"/>
</vector>
@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group
android:scaleX="0.029365"
android:scaleY="-0.029365"
android:translateX="-8.57"
android:translateY="103.38">
<!-- Green Terminal Screen Frame -->
<path
android:strokeColor="#00FF00"
android:strokeWidth="40"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:pathData="M 1177.27,2220.71 V 1142.45 H 3084.31 V 2220.71 H 1177.27" />
<!-- Blue Wave / Exponential Curve -->
<path
android:strokeColor="#0000FF"
android:strokeWidth="60"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:pathData="m 1172.41,1137.58 79.1,-1.22 -0.21,-0.38 c 1.08,0 2.18,0 3.27,0 762.95,0 1464.86,417.88 1829.53,1089.21" />
<!-- Red Circular Arc -->
<path
android:strokeColor="#FF0000"
android:strokeWidth="60"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:pathData="m 2593.86,1620.73 -1.22,-28 -0.08,-0.08 c 0.43,-250.15 202.81,-452.72 452.3,-452.72 0.14,0 0.28,0 0.42,0 l 43.9,2.52" />
<!-- Yellow Smiley Head -->
<path
android:strokeColor="#FFFF00"
android:strokeWidth="50"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:pathData="m 2976,1363.94 v 0.08 c 44.73,34.29 71.21,87.25 71.8,143.61 l 0.01,-0.15 c -0.06,97.48 -79.11,176.47 -176.59,176.47 -0.36,0 -0.73,0 -1.09,0 l -0.07,0.03 c -0.25,0 -0.51,0 -0.75,0 -37.38,0 -73.79,-11.94 -103.92,-34.08 l 0.02,-0.03 c -44.38,-34.04 -70.82,-86.47 -71.8,-142.39 l 0.06,0.04 c 0.54,-97.43 79.04,-176.47 176.46,-177.69 l 0.08,0.02 c 0.64,-0.01 1.29,-0.01 1.92,-0.01 37.4,0 73.82,11.94 103.96,34.08" />
<!-- Magenta Typography: lib3270j -->
<!-- 'l' -->
<path
android:fillColor="#FF00FF"
android:pathData="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
<!-- 'i' -->
<path
android:fillColor="#FF00FF"
android:pathData="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
<!-- 'b' -->
<path
android:fillColor="#FF00FF"
android:pathData="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
<!-- '3' -->
<path
android:fillColor="#FF00FF"
android:pathData="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
<!-- '2' -->
<path
android:fillColor="#FF00FF"
android:pathData="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
<!-- '7' -->
<path
android:fillColor="#FF00FF"
android:pathData="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
<!-- '0' -->
<path
android:fillColor="#FF00FF"
android:pathData="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
<!-- 'j' -->
<path
android:fillColor="#FF00FF"
android:pathData="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
<!-- Smiley Details -->
<!-- Mouth -->
<path
android:strokeColor="#FFFF00"
android:strokeWidth="50"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:pathData="m 2778.85,1478.34 0.04,-0.02 c 21.03,-31.91 56.69,-51.12 94.91,-51.12 0.41,0 0.83,0 1.24,0.01 l -0.02,0.07 c 43.88,0.19 84.11,24.51 104.66,63.28" />
<!-- Left Eye -->
<path
android:strokeColor="#00FFFF"
android:strokeWidth="50"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:pathData="m 2820.22,1595.17 0.01,0.01 c -14.62,-1.91 -25.56,-14.37 -25.56,-29.11 0,-0.03 0,-0.07 0,-0.1 l -0.02,-0.01 c 0,-0.03 0,-0.07 0,-0.1 0,-16.09 13.04,-29.14 29.13,-29.14 0.43,0 0.87,0.01 1.29,0.03 h 3.67 0.01 c 14.61,1.91 25.55,14.37 25.55,29.11 0,0.03 0,0.07 0,0.1 l 0.02,0.01 c 0,0.04 0,0.08 0,0.12 0,16.07 -13.03,29.09 -29.09,29.09 -0.03,0 -0.08,0 -0.12,0 l -4.89,-0.01" />
<!-- Right Eye -->
<path
android:strokeColor="#00FFFF"
android:strokeWidth="50"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:pathData="m 2921.23,1569.61 v -3.65 l -0.01,-0.01 c 0,-0.27 -0.01,-0.54 -0.01,-0.81 0,-18.22 14.66,-33.05 32.87,-33.26 l 0.03,0.01 c 0.28,0 0.57,-0.01 0.84,-0.01 17.29,0 31.72,13.22 33.24,30.44 l -0.02,3.64 0.02,0.02 c 0,0.27 0.01,0.54 0.01,0.81 0,18.37 -14.9,33.28 -33.28,33.28 -0.26,0 -0.54,-0.01 -0.81,-0.02 l -0.02,-0.03 c -17.14,-0.18 -31.36,-13.34 -32.86,-30.42" />
<!-- Nose -->
<path
android:strokeColor="#FFFF00"
android:strokeWidth="50"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:pathData="m 2892.03,1528.23 -29.21,-20.68" />
<path
android:strokeColor="#FFFF00"
android:strokeWidth="50"
android:strokeLineCap="round"
android:strokeLineJoin="round"
android:pathData="m 2862.82,1507.55 h 32.86" />
</group>
</vector>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_launcher_background" />
<item android:drawable="@drawable/ic_launcher_foreground" />
</layer-list>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_launcher_background" />
<item android:drawable="@drawable/ic_launcher_foreground" />
</layer-list>
@@ -1,12 +1,18 @@
package haus.nightmare.a3270
import haus.nightmare.a3270.storage.SavedHost
import haus.nightmare.a3270.ui.getFgColorForAttribute
import haus.nightmare.lib3270j.ConnectionConfig
import haus.nightmare.lib3270j.TerminalModel
import haus.nightmare.lib3270j.charset.EbcdicTranslator
import haus.nightmare.lib3270j.datastream.DataStreamProcessor
import haus.nightmare.lib3270j.protocol.DS3270Constants.*
import haus.nightmare.lib3270j.screen.ExtendedAttribute
import haus.nightmare.lib3270j.screen.ScreenBuffer
import org.json.JSONObject
import org.junit.Assert.*
import org.junit.Test
import java.io.ByteArrayOutputStream
import java.util.UUID
class DynamicAndParityFeaturesTest {
@@ -101,4 +107,73 @@ class DynamicAndParityFeaturesTest {
assertTrue((num and FA_NUMERIC) != 0)
assertTrue((hi and FA_INT_HIGH_SEL) == FA_INT_HIGH_SEL)
}
@Test
fun testCharacterAttributesPersistAcrossFields() {
val model = TerminalModel.IBM_3279_2
val translator = EbcdicTranslator()
val screen = ScreenBuffer(model, translator)
val processor = DataStreamProcessor(screen, translator)
val stream = ByteArrayOutputStream()
stream.write(CMD_EW)
stream.write(0xC3) // WCC
// SF(prot, skip)
stream.write(ORDER_SF)
stream.write(FA_PRINTABLE or FA_PROTECT or FA_NUMERIC)
// SA(yellow): XA_FOREGROUND, COLOR_YELLOW (0xF6)
stream.write(ORDER_SA)
stream.write(XA_FOREGROUND)
stream.write(0xF6)
// Data: " 4 "
stream.write(translator.stringToEbcdic(" 4 "))
// Second SF(prot, skip)
stream.write(ORDER_SF)
stream.write(FA_PRINTABLE or FA_PROTECT or FA_NUMERIC)
// Data: "DISPLAY"
stream.write(translator.stringToEbcdic("DISPLAY"))
val record = stream.toByteArray()
processor.processRecord(record, 0, record.size, true)
// " 4 " cells (indices 1..5) should have yellow foreground (0xF6)
for (i in 1..5) {
assertEquals(0xF6.toByte(), screen.getCell(i).fg)
}
// Across second SF, "DISPLAY" cells (indices 7..13) must retain yellow foreground (0xF6) (commit c28c097e)
for (i in 7..13) {
assertEquals(0xF6.toByte(), screen.getCell(i).fg)
}
}
@Test
fun testTerminalViewColorResolutionForProtectedField() {
val ea = ExtendedAttribute()
// Normal protected field attribute -> Turquoise / Cyan (0x00FFFF)
val protNormalColor = getFgColorForAttribute(ea, null, (FA_PRINTABLE or FA_PROTECT).toByte())
assertEquals(androidx.compose.ui.graphics.Color(0xFF00FFFF), protNormalColor)
// Intensified protected field attribute -> White (0xFFC0C0C0 per HOST_COLOR_WHITE / HoD palette)
val protHighColor = getFgColorForAttribute(ea, null, (FA_PRINTABLE or FA_PROTECT or FA_INT_HIGH_SEL).toByte())
assertEquals(androidx.compose.ui.graphics.Color(0xFFC0C0C0), protHighColor)
// Normal unprotected -> Green (0x00FF00)
val unprotNormalColor = getFgColorForAttribute(ea, null, FA_PRINTABLE.toByte())
assertEquals(androidx.compose.ui.graphics.Color(0xFF00FF00), unprotNormalColor)
// Intensified unprotected -> Red (0xFF0000)
val unprotHighColor = getFgColorForAttribute(ea, null, (FA_PRINTABLE or FA_INT_HIGH_SEL).toByte())
assertEquals(androidx.compose.ui.graphics.Color(0xFFFF0000), unprotHighColor)
// Character mode override (SA yellow = 0xF6)
ea.fg = 0xF6.toByte()
val yellowColor = getFgColorForAttribute(ea, null, (FA_PRINTABLE or FA_PROTECT).toByte())
assertEquals(androidx.compose.ui.graphics.Color(0xFFFFFF00), yellowColor)
}
}
@@ -4,6 +4,7 @@ import haus.nightmare.lib3270j.TerminalModel
import haus.nightmare.lib3270j.charset.EbcdicTranslator
import haus.nightmare.lib3270j.ecl.ECLConstants
import haus.nightmare.lib3270j.ecl.ECLOIA
import haus.nightmare.lib3270j.ecl.ECLPS
import haus.nightmare.lib3270j.input.InputProcessor
import haus.nightmare.lib3270j.protocol.DS3270Constants.*
import haus.nightmare.lib3270j.screen.ScreenBuffer
@@ -191,6 +192,13 @@ class EntryAssistAndModesTest {
@Test
fun testIniSectionSyntaxMatchingJ3270() {
val sampleIni = """
[behavior]
blockSelectMode = false
blockPaste = true
[clipboard]
blockPasteMode = true
[entryassist]
docMode = true
wordWrap = true
@@ -223,6 +231,10 @@ class EntryAssistAndModesTest {
}
}
assertEquals("false", parsed["behavior.blockselectmode"])
assertEquals("true", parsed["behavior.blockpaste"])
assertEquals("true", parsed["clipboard.blockpastemode"])
assertEquals("true", parsed["entryassist.docmode"])
assertEquals("true", parsed["entryassist.wordwrap"])
assertEquals("5", parsed["entryassist.startcol"])
@@ -237,6 +249,57 @@ class EntryAssistAndModesTest {
assertEquals("true", parsed["modes.autoskip"])
}
@Test
fun testBlockPasteMultiLineAlignment() {
val translator = EbcdicTranslator()
val screen = ScreenBuffer(TerminalModel.IBM_3278_2, translator)
val input = InputProcessor(screen, translator, null)
val ps = ECLPS(screen, input, translator)
// Verify default blockPaste is true
assertTrue(input.isBlockPaste)
assertTrue(ps.isBlockPaste)
// 1. Unformatted screen: verify start column 15 is preserved across newlines
screen.erase(false)
val startPos = screen.rowColToAddress(0, 15)
screen.cursorAddress = startPos
val multilineText = "FIRST\nSECOND\nTHIRD"
val pasted = input.pasteText(multilineText, true, false, true)
assertEquals(16, pasted)
assertEquals("FIRST", ps.getString(startPos, 5))
val row1Col15 = screen.rowColToAddress(1, 15)
assertEquals("SECOND", ps.getString(row1Col15, 6))
val row2Col15 = screen.rowColToAddress(2, 15)
assertEquals("THIRD", ps.getString(row2Col15, 5))
// 2. Formatted screen with blockPaste = true
screen.erase(false)
screen.setFieldAttribute(0, FA_PRINTABLE.toByte())
screen.setFieldAttribute(80, FA_PRINTABLE.toByte())
screen.setFieldAttribute(160, FA_PRINTABLE.toByte())
screen.cursorAddress = screen.rowColToAddress(0, 25)
val pastedFormatted = input.pasteText("ALPHA\r\nBETA\r\nGAMMA", true, false, true)
assertEquals(14, pastedFormatted)
assertEquals("ALPHA", ps.getString(screen.rowColToAddress(0, 25), 5))
assertEquals("BETA", ps.getString(screen.rowColToAddress(1, 25), 4))
assertEquals("GAMMA", ps.getString(screen.rowColToAddress(2, 25), 5))
// 3. Formatted screen with blockPaste = false: reverts to first unprotected field on next row (pos 81)
screen.erase(false)
screen.setFieldAttribute(0, FA_PRINTABLE.toByte())
screen.setFieldAttribute(80, FA_PRINTABLE.toByte())
screen.cursorAddress = screen.rowColToAddress(0, 25)
input.pasteText("LINE1\nLINE2", true, false, false)
assertEquals("LINE1", ps.getString(screen.rowColToAddress(0, 25), 5))
assertEquals("LINE2", ps.getString(81, 5))
}
@Test
fun testCmsFilenameFormatting() {
// Upload (local -> host)
@@ -8,6 +8,8 @@ import haus.nightmare.lib3270j.datastream.DataStreamProcessor
import haus.nightmare.lib3270j.graphics.GocaDecoder
import haus.nightmare.lib3270j.graphics.GraphicsPlane
import haus.nightmare.lib3270j.input.InputProcessor
import haus.nightmare.lib3270j.graphics.GocaConstants
import haus.nightmare.a3270.ui.calculateGridMetrics
import haus.nightmare.lib3270j.protocol.DS3270Constants.*
import haus.nightmare.lib3270j.protocol.TelnetConstants.*
import haus.nightmare.lib3270j.protocol.TN3270EConstants.*
@@ -175,4 +177,120 @@ class GraphicsAndSysReqTest {
inputProcessor.sendAid(AID_SYSREQ)
assertEquals(ConnectionState.CONNECTED_TN3270E, fsm.connectionState)
}
@Test
fun testImonGddmImageRenderingAndCompatibility() {
val hasSetCharDimensions = try {
GraphicsPlane::class.java.getMethod("setCharDimensions", Int::class.javaPrimitiveType, Int::class.javaPrimitiveType)
true
} catch (_: NoSuchMethodException) {
false
}
if (!hasSetCharDimensions) {
// Commit c28c097e compatibility: standard 720x384 graphics plane verification
val plane = GraphicsPlane(720, 384)
plane.setScreenDimensions(80, 24)
val decoder = GocaDecoder(plane)
assertEquals(720, plane.totalWidth)
assertEquals(384, plane.totalHeight)
val out = ByteArrayOutputStream()
out.write(GocaConstants.G_GSCP)
out.write(0x04)
out.write(0x00); out.write(0x10) // X = 16
out.write(0x00); out.write(0x10) // Y = 16
out.write(GocaConstants.G_GSCOL)
out.write(0x01)
decoder.decodeStream(out.toByteArray(), 0, out.size())
val metrics = calculateGridMetrics(1080f, 1920f, 80, 24)
assertTrue(metrics.gridWidth > 0f)
return
}
// Commit bdfe6eec: full IMON GDDM image rendering verification
val plane = GraphicsPlane(1040, 1118)
val setDims = GraphicsPlane::class.java.getMethod("setCharDimensions", Int::class.javaPrimitiveType, Int::class.javaPrimitiveType)
setDims.invoke(plane, 13, 26)
plane.setScreenDimensions(80, 43)
val decoder = GocaDecoder(plane)
assertEquals(1040, plane.totalWidth)
assertEquals(1118, plane.totalHeight)
assertEquals(520, plane.xMax)
assertEquals(558, plane.yMax)
val out = ByteArrayOutputStream()
// GSCP: Set Current Position (377, 494)
out.write(GocaConstants.G_GSCP)
out.write(0x04)
out.write(0x01); out.write(0x79) // X = 377
out.write(0x01); out.write(0xEE) // Y = 494
// GSCOL: Color 1 = Blue
out.write(GocaConstants.G_GSCOL)
out.write(0x01)
// G_GBIMGC (0x91): Begin Image Current Position: len=6, flags=0, w=80, h=80
out.write(GocaConstants.G_GBIMGC)
out.write(0x06)
out.write(0x00); out.write(0x00)
out.write(0x00); out.write(0x50) // w = 80
out.write(0x00); out.write(0x50) // h = 80
// G_GIMD (0x92): 10 bytes scanline
out.write(GocaConstants.G_GIMD)
out.write(0x0A)
out.write(byteArrayOf(0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte()))
// G_GEIMG (0x93): End Image with 2-byte operand
out.write(GocaConstants.G_GEIMG)
out.write(0x02)
out.write(0x00); out.write(0x00)
// Second image tile: Color 2 = Red
out.write(GocaConstants.G_GSCOL)
out.write(0x02)
out.write(GocaConstants.G_GBIMGC)
out.write(0x06)
out.write(0x00); out.write(0x00)
out.write(0x00); out.write(0x50)
out.write(0x00); out.write(0x50)
out.write(GocaConstants.G_GIMD)
out.write(0x0A)
out.write(byteArrayOf(0xAA.toByte(), 0xAA.toByte(), 0xAA.toByte(), 0xAA.toByte(), 0xAA.toByte(), 0xAA.toByte(), 0xAA.toByte(), 0xAA.toByte(), 0xAA.toByte(), 0xAA.toByte()))
out.write(GocaConstants.G_GEIMG)
out.write(0x02)
out.write(0x00); out.write(0x00)
val stream = out.toByteArray()
decoder.decodeStream(stream, 0, stream.size)
assertTrue(plane.hasContent())
val mappedX = plane.mapX(377)
val mappedY = plane.mapY(494)
assertEquals(897, mappedX)
assertEquals(64, mappedY)
val rgb = plane.rgbBuffer
assertNotNull(rgb)
val pixelAtImg = rgb[mappedY * 1040 + mappedX]
assertNotEquals(0, pixelAtImg)
// Verify grid metrics and UI scaling calculation against IMON canvas
val metrics = calculateGridMetrics(1080f, 1920f, 80, 43)
assertTrue(metrics.gridWidth > 0f)
assertTrue(metrics.gridHeight > 0f)
// Touch scaling test: touch in the middle of grid maps accurately to plane coordinates
val touchScreenX = metrics.offsetX + metrics.gridWidth / 2f
val touchScreenY = metrics.offsetY + metrics.gridHeight / 2f
val planeX = (((touchScreenX - metrics.offsetX) * plane.canvasWidth) / metrics.gridWidth).toInt()
val planeY = (((touchScreenY - metrics.offsetY) * plane.canvasHeight) / metrics.gridHeight).toInt()
assertEquals(plane.canvasWidth / 2, planeX)
assertEquals(plane.canvasHeight / 2, planeY)
}
}