Cleaning and some optimizations

This commit is contained in:
2026-09-28 01:56:13 +00:00
parent 0201c7c277
commit 9918c823c2
19 changed files with 518 additions and 155 deletions
+6 -1
View File
@@ -1,6 +1,6 @@
allprojects { allprojects {
group = 'haus.nightmare.j3270' group = 'haus.nightmare.j3270'
version = '0.1.0' version = '1.2.4'
repositories { repositories {
mavenCentral() mavenCentral()
@@ -23,6 +23,11 @@ subprojects {
} }
} }
tasks.withType(Javadoc).configureEach {
options.addStringOption('Xdoclint:none', '-quiet')
options.encoding = 'UTF-8'
}
dependencies { dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+11 -3
View File
@@ -91,15 +91,23 @@ if [ -d "$SCRIPT_DIR/lib3270j/src/main/resources" ]; then
cp -r "$SCRIPT_DIR/lib3270j/src/main/resources/"* "$BUILD_DIR/lib3270j/" 2>/dev/null || true cp -r "$SCRIPT_DIR/lib3270j/src/main/resources/"* "$BUILD_DIR/lib3270j/" 2>/dev/null || true
fi fi
# 4. Package executable JAR # 4. Package JARs
echo "Packaging j3270.jar..." echo "Packaging lib3270j.jar (standalone library)..."
printf "Manifest-Version: 1.0\nMain-Class: haus.nightmare.j3270.J3270App\nImplementation-Title: j3270\nImplementation-Version: 0.1.0\nCreated-By: j3270 build_all.sh\n\n" > "$BUILD_DIR/MANIFEST.MF" printf "Manifest-Version: 1.0\nImplementation-Title: lib3270j\nImplementation-Version: 1.2.4\nCreated-By: j3270 build_all.sh\n\n" > "$BUILD_DIR/LIB_MANIFEST.MF"
"$JAR_BIN" cvfm "$BUILD_DIR/lib3270j.jar" "$BUILD_DIR/LIB_MANIFEST.MF" \
-C "$BUILD_DIR/lib3270j" . > /dev/null
echo "Packaging j3270.jar (standalone executable)..."
printf "Manifest-Version: 1.0\nMain-Class: haus.nightmare.j3270.J3270App\nImplementation-Title: j3270\nImplementation-Version: 1.2.4\nCreated-By: j3270 build_all.sh\n\n" > "$BUILD_DIR/MANIFEST.MF"
"$JAR_BIN" cvfm "$BUILD_DIR/j3270.jar" "$BUILD_DIR/MANIFEST.MF" \ "$JAR_BIN" cvfm "$BUILD_DIR/j3270.jar" "$BUILD_DIR/MANIFEST.MF" \
-C "$BUILD_DIR/lib3270j" . \ -C "$BUILD_DIR/lib3270j" . \
-C "$BUILD_DIR/j3270" . > /dev/null -C "$BUILD_DIR/j3270" . > /dev/null
echo "=== Build Complete ===" echo "=== Build Complete ==="
echo "Library JAR created: $BUILD_DIR/lib3270j.jar"
ls -lh "$BUILD_DIR/lib3270j.jar"
echo "Executable JAR created: $BUILD_DIR/j3270.jar" echo "Executable JAR created: $BUILD_DIR/j3270.jar"
ls -lh "$BUILD_DIR/j3270.jar" ls -lh "$BUILD_DIR/j3270.jar"
-1
View File
@@ -1,2 +1 @@
android.useAndroidX=true
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
+3 -1
View File
@@ -1,7 +1,9 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
networkTimeout=10000 networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
Vendored
+9 -10
View File
@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Copyright © 2015-2021 the original authors. # Copyright © 2015 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -15,10 +15,12 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
# Gradle start up script for POSIX generated by Gradle. # gradlew start up script for POSIX generated by Gradle.
# #
# Important for running: # Important for running:
# #
@@ -27,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole # bash, then to run this script, type that shell name before the whole
# command line, like: # command line, like:
# #
# ksh Gradle # ksh gradlew
# #
# Busybox and similar reduced shells will NOT work, because this script # Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features: # requires all of these POSIX shell features:
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/<unknown>/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -84,7 +86,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -112,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -170,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" ) JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -203,15 +203,14 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command: # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped. # and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line. # treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available. # Stop when "xargs" is not available.
Vendored
+24 -34
View File
@@ -13,16 +13,18 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem gradlew startup script for Windows
@rem @rem
@rem ########################################################################## @rem ##########################################################################
@rem Set local scope for the variables with windows NT shell @rem Set local scope for the variables, and ensure extensions are enabled
if "%OS%"=="Windows_NT" setlocal setlocal EnableExtensions
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@@ -43,13 +45,13 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail "%COMSPEC%" /c exit 1
:findJavaFromJavaHome :findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
@@ -57,36 +59,24 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail "%COMSPEC%" /c exit 1
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute gradlew
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* @rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:end :exitWithErrorLevel
@rem End local scope for the variables with windows NT shell @rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
if %ERRORLEVEL% equ 0 goto mainEnd "%COMSPEC%" /c exit %ERRORLEVEL%
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@@ -9,6 +9,7 @@ import haus.nightmare.lib3270j.screen.ScreenBuffer;
import haus.nightmare.j3270.ui.*; import haus.nightmare.j3270.ui.*;
import haus.nightmare.j3270.ft.FileTransfer; import haus.nightmare.j3270.ft.FileTransfer;
import haus.nightmare.j3270.ft.FileTransferDialog; import haus.nightmare.j3270.ft.FileTransferDialog;
import haus.nightmare.j3270.config.Settings;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -182,7 +183,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
terminalPanel.setStatusBarToggleCallback(this::toggleStatusBar); terminalPanel.setStatusBarToggleCallback(this::toggleStatusBar);
terminalPanel.addModeChangeListener(this::syncModeMenuItems); terminalPanel.addModeChangeListener(this::syncModeMenuItems);
statusBar.setVisible(haus.nightmare.j3270.config.Settings.getStatusBarVisible()); statusBar.setVisible(Settings.getStatusBarVisible());
getContentPane().setLayout(new BorderLayout()); getContentPane().setLayout(new BorderLayout());
getContentPane().setBackground(Color.BLACK); getContentPane().setBackground(Color.BLACK);
@@ -195,10 +196,10 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
if (wordWrapItem != null) wordWrapItem.setSelected(terminalPanel.isWordWrap()); if (wordWrapItem != null) wordWrapItem.setSelected(terminalPanel.isWordWrap());
if (aplModeItem != null) aplModeItem.setSelected(terminalPanel.isAplMode()); if (aplModeItem != null) aplModeItem.setSelected(terminalPanel.isAplMode());
if (insertModeItem != null) insertModeItem.setSelected(terminalPanel.isInsertMode()); if (insertModeItem != null) insertModeItem.setSelected(terminalPanel.isInsertMode());
if (fourColorItem != null) fourColorItem.setSelected(haus.nightmare.j3270.config.Settings.getFourColorOverride()); if (fourColorItem != null) fourColorItem.setSelected(Settings.getFourColorOverride());
if (numLockItem != null) numLockItem.setSelected(haus.nightmare.j3270.config.Settings.getNumericFieldLock()); if (numLockItem != null) numLockItem.setSelected(Settings.getNumericFieldLock());
if (autoSkipItem != null) autoSkipItem.setSelected(haus.nightmare.j3270.config.Settings.getAutoSkipEnabled()); if (autoSkipItem != null) autoSkipItem.setSelected(Settings.getAutoSkipEnabled());
if (insertOffAidItem != null) insertOffAidItem.setSelected(haus.nightmare.j3270.config.Settings.getInsertOffOnAid()); if (insertOffAidItem != null) insertOffAidItem.setSelected(Settings.getInsertOffOnAid());
} }
private void onThemeChanged(UITheme theme) { private void onThemeChanged(UITheme theme) {
@@ -302,7 +303,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
JRadioButtonMenuItem darkThemeItem = new JRadioButtonMenuItem("Dark Mode", ThemeManager.isDark()); JRadioButtonMenuItem darkThemeItem = new JRadioButtonMenuItem("Dark Mode", ThemeManager.isDark());
ThemeManager.styleMenuItem(darkThemeItem); ThemeManager.styleMenuItem(darkThemeItem);
darkThemeItem.addActionListener(e -> { darkThemeItem.addActionListener(e -> {
haus.nightmare.j3270.config.Settings.setJavaUiTheme(UITheme.DARK); Settings.setJavaUiTheme(UITheme.DARK);
ThemeManager.setTheme(UITheme.DARK); ThemeManager.setTheme(UITheme.DARK);
}); });
themeGroup.add(darkThemeItem); themeGroup.add(darkThemeItem);
@@ -311,7 +312,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
JRadioButtonMenuItem lightThemeItem = new JRadioButtonMenuItem("Light Mode", ThemeManager.isLight()); JRadioButtonMenuItem lightThemeItem = new JRadioButtonMenuItem("Light Mode", ThemeManager.isLight());
ThemeManager.styleMenuItem(lightThemeItem); ThemeManager.styleMenuItem(lightThemeItem);
lightThemeItem.addActionListener(e -> { lightThemeItem.addActionListener(e -> {
haus.nightmare.j3270.config.Settings.setJavaUiTheme(UITheme.LIGHT); Settings.setJavaUiTheme(UITheme.LIGHT);
ThemeManager.setTheme(UITheme.LIGHT); ThemeManager.setTheme(UITheme.LIGHT);
}); });
themeGroup.add(lightThemeItem); themeGroup.add(lightThemeItem);
@@ -320,7 +321,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
viewMenu.add(themeMenu); viewMenu.add(themeMenu);
viewMenu.addSeparator(); viewMenu.addSeparator();
statusBarItem = new JCheckBoxMenuItem("Status Bar", statusBar != null ? statusBar.isVisible() : haus.nightmare.j3270.config.Settings.getStatusBarVisible()); statusBarItem = new JCheckBoxMenuItem("Status Bar", statusBar != null ? statusBar.isVisible() : Settings.getStatusBarVisible());
ThemeManager.styleMenuItem(statusBarItem); ThemeManager.styleMenuItem(statusBarItem);
statusBarItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, InputEvent.ALT_DOWN_MASK)); statusBarItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, InputEvent.ALT_DOWN_MASK));
statusBarItem.addActionListener(e -> setStatusBarVisible(statusBarItem.isSelected())); statusBarItem.addActionListener(e -> setStatusBarVisible(statusBarItem.isSelected()));
@@ -332,7 +333,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
rulerItem.addActionListener(e -> { rulerItem.addActionListener(e -> {
boolean en = rulerItem.isSelected(); boolean en = rulerItem.isSelected();
terminalPanel.setCrosshairRulerEnabled(en); terminalPanel.setCrosshairRulerEnabled(en);
haus.nightmare.j3270.config.Settings.setCrosshairRuler(en); Settings.setCrosshairRuler(en);
}); });
viewMenu.add(rulerItem); viewMenu.add(rulerItem);
@@ -342,7 +343,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
ThemeManager.styleMenuItem(blockCursorItem); ThemeManager.styleMenuItem(blockCursorItem);
blockCursorItem.addActionListener(e -> { blockCursorItem.addActionListener(e -> {
terminalPanel.setCursorStyle(TerminalPanel.CursorStyle.BLOCK); terminalPanel.setCursorStyle(TerminalPanel.CursorStyle.BLOCK);
haus.nightmare.j3270.config.Settings.setCursorStyle("BLOCK"); Settings.setCursorStyle("BLOCK");
}); });
cursorGroup.add(blockCursorItem); cursorGroup.add(blockCursorItem);
cursorMenu.add(blockCursorItem); cursorMenu.add(blockCursorItem);
@@ -351,7 +352,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
ThemeManager.styleMenuItem(underlineCursorItem); ThemeManager.styleMenuItem(underlineCursorItem);
underlineCursorItem.addActionListener(e -> { underlineCursorItem.addActionListener(e -> {
terminalPanel.setCursorStyle(TerminalPanel.CursorStyle.UNDERLINE); terminalPanel.setCursorStyle(TerminalPanel.CursorStyle.UNDERLINE);
haus.nightmare.j3270.config.Settings.setCursorStyle("UNDERLINE"); Settings.setCursorStyle("UNDERLINE");
}); });
cursorGroup.add(underlineCursorItem); cursorGroup.add(underlineCursorItem);
cursorMenu.add(underlineCursorItem); cursorMenu.add(underlineCursorItem);
@@ -436,34 +437,34 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
modesMenu.addSeparator(); modesMenu.addSeparator();
fourColorItem = new JCheckBoxMenuItem("Base 4-Color Override", haus.nightmare.j3270.config.Settings.getFourColorOverride()); fourColorItem = new JCheckBoxMenuItem("Base 4-Color Override", Settings.getFourColorOverride());
ThemeManager.styleMenuItem(fourColorItem); ThemeManager.styleMenuItem(fourColorItem);
fourColorItem.addActionListener(e -> { fourColorItem.addActionListener(e -> {
haus.nightmare.j3270.config.Settings.setFourColorOverride(fourColorItem.isSelected()); Settings.setFourColorOverride(fourColorItem.isSelected());
terminalPanel.repaint(); terminalPanel.repaint();
}); });
modesMenu.add(fourColorItem); modesMenu.add(fourColorItem);
numLockItem = new JCheckBoxMenuItem("Numeric Field Lock", haus.nightmare.j3270.config.Settings.getNumericFieldLock()); numLockItem = new JCheckBoxMenuItem("Numeric Field Lock", Settings.getNumericFieldLock());
ThemeManager.styleMenuItem(numLockItem); ThemeManager.styleMenuItem(numLockItem);
numLockItem.addActionListener(e -> { numLockItem.addActionListener(e -> {
haus.nightmare.j3270.config.Settings.setNumericFieldLock(numLockItem.isSelected()); Settings.setNumericFieldLock(numLockItem.isSelected());
terminalPanel.applyModeSettings(); terminalPanel.applyModeSettings();
}); });
modesMenu.add(numLockItem); modesMenu.add(numLockItem);
autoSkipItem = new JCheckBoxMenuItem("Auto-Skip Across Fields", haus.nightmare.j3270.config.Settings.getAutoSkipEnabled()); autoSkipItem = new JCheckBoxMenuItem("Auto-Skip Across Fields", Settings.getAutoSkipEnabled());
ThemeManager.styleMenuItem(autoSkipItem); ThemeManager.styleMenuItem(autoSkipItem);
autoSkipItem.addActionListener(e -> { autoSkipItem.addActionListener(e -> {
haus.nightmare.j3270.config.Settings.setAutoSkipEnabled(autoSkipItem.isSelected()); Settings.setAutoSkipEnabled(autoSkipItem.isSelected());
terminalPanel.applyModeSettings(); terminalPanel.applyModeSettings();
}); });
modesMenu.add(autoSkipItem); modesMenu.add(autoSkipItem);
insertOffAidItem = new JCheckBoxMenuItem("Reset Insert on AID Key", haus.nightmare.j3270.config.Settings.getInsertOffOnAid()); insertOffAidItem = new JCheckBoxMenuItem("Reset Insert on AID Key", Settings.getInsertOffOnAid());
ThemeManager.styleMenuItem(insertOffAidItem); ThemeManager.styleMenuItem(insertOffAidItem);
insertOffAidItem.addActionListener(e -> { insertOffAidItem.addActionListener(e -> {
haus.nightmare.j3270.config.Settings.setInsertOffOnAid(insertOffAidItem.isSelected()); Settings.setInsertOffOnAid(insertOffAidItem.isSelected());
terminalPanel.applyModeSettings(); terminalPanel.applyModeSettings();
}); });
modesMenu.add(insertOffAidItem); modesMenu.add(insertOffAidItem);
@@ -561,13 +562,15 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
// ========== Dialog Openers ========== // ========== Dialog Openers ==========
private void showFindDialog() { public void showFindDialog() {
if (client == null) { if (client == null) {
JOptionPane.showMessageDialog(this, "Connect to a host before using Find.", "Not Connected", JOptionPane.WARNING_MESSAGE); JOptionPane.showMessageDialog(this, "Connect to a host before using Find.", "Not Connected", JOptionPane.WARNING_MESSAGE);
return; return;
} }
if (findDialog == null) { if (findDialog == null) {
findDialog = new FindDialog(this, client, terminalPanel); findDialog = new FindDialog(this, client, terminalPanel);
} else {
findDialog.setClient(client);
} }
findDialog.setVisible(true); findDialog.setVisible(true);
} }
@@ -620,13 +623,15 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
} }
} }
private void showScriptDialog() { public void showScriptDialog() {
if (client == null) { if (client == null) {
JOptionPane.showMessageDialog(this, "Connect to a host before running scripts.", "Not Connected", JOptionPane.WARNING_MESSAGE); JOptionPane.showMessageDialog(this, "Connect to a host before running scripts.", "Not Connected", JOptionPane.WARNING_MESSAGE);
return; return;
} }
if (scriptDialog == null) { if (scriptDialog == null) {
scriptDialog = new ScriptDialog(this, client, terminalPanel); scriptDialog = new ScriptDialog(this, client, terminalPanel);
} else {
scriptDialog.setClient(client);
} }
scriptDialog.setVisible(true); scriptDialog.setVisible(true);
} }
@@ -707,7 +712,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
terminalPanel.refreshScreen(); terminalPanel.refreshScreen();
statusBar.updateStatus(); statusBar.updateStatus();
} }
haus.nightmare.j3270.config.Settings.setCodePage(cpId); Settings.setCodePage(cpId);
} }
private void changeGraphicsMode(GraphicsMode mode) { private void changeGraphicsMode(GraphicsMode mode) {
@@ -715,7 +720,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
client.getDataStreamProcessor().getQueryReplyBuilder().setGraphicsMode(mode); client.getDataStreamProcessor().getQueryReplyBuilder().setGraphicsMode(mode);
terminalPanel.repaint(); terminalPanel.repaint();
} }
haus.nightmare.j3270.config.Settings.setGraphicsMode(mode); Settings.setGraphicsMode(mode);
} }
private void printScreen() { private void printScreen() {
@@ -814,15 +819,15 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
} }
ConnectDialog dialog = new ConnectDialog(this); ConnectDialog dialog = new ConnectDialog(this);
String initialHost = lastHost != null && !lastHost.isEmpty() ? lastHost : haus.nightmare.j3270.config.Settings.getAutoConnectHost(); String initialHost = lastHost != null && !lastHost.isEmpty() ? lastHost : Settings.getAutoConnectHost();
int initialPort = lastPort > 0 ? lastPort : haus.nightmare.j3270.config.Settings.getAutoConnectPort(); int initialPort = lastPort > 0 ? lastPort : Settings.getAutoConnectPort();
dialog.setInitialHost(initialHost); dialog.setInitialHost(initialHost);
dialog.setInitialPort(initialPort); dialog.setInitialPort(initialPort);
dialog.setInitialTls(haus.nightmare.j3270.config.Settings.getAutoConnectTls()); dialog.setInitialTls(Settings.getAutoConnectTls());
dialog.setInitialVerifyCert(haus.nightmare.j3270.config.Settings.getAutoConnectVerifyCert()); dialog.setInitialVerifyCert(Settings.getAutoConnectVerifyCert());
dialog.setInitialCodePage(haus.nightmare.j3270.config.Settings.getCodePage()); dialog.setInitialCodePage(Settings.getCodePage());
dialog.setInitialGraphicsMode(haus.nightmare.j3270.config.Settings.getGraphicsMode()); dialog.setInitialGraphicsMode(Settings.getGraphicsMode());
dialog.setInitialHostType(haus.nightmare.j3270.config.Settings.getDefaultHostType()); dialog.setInitialHostType(Settings.getDefaultHostType());
dialog.setVisible(true); dialog.setVisible(true);
if (dialog.isConfirmed()) { if (dialog.isConfirmed()) {
@@ -835,7 +840,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
public void setConnectionHostType(haus.nightmare.lib3270j.ft.FTConfig.HostType ht) { public void setConnectionHostType(haus.nightmare.lib3270j.ft.FTConfig.HostType ht) {
if (ht == null) return; if (ht == null) return;
haus.nightmare.j3270.config.Settings.setDefaultHostType(ht.name()); Settings.setDefaultHostType(ht.name());
if (client != null && client.getConfig() != null) { if (client != null && client.getConfig() != null) {
client.getConfig().setFtHostType(ht); client.getConfig().setFtHostType(ht);
String profileId = client.getConfig().getSavedProfileId(); String profileId = client.getConfig().getSavedProfileId();
@@ -857,7 +862,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
if (client != null && client.getConfig() != null) { if (client != null && client.getConfig() != null) {
currentHt = client.getConfig().getFtHostType(); currentHt = client.getConfig().getFtHostType();
} else { } else {
String def = haus.nightmare.j3270.config.Settings.getDefaultHostType(); String def = Settings.getDefaultHostType();
try { try {
if (def != null) currentHt = haus.nightmare.lib3270j.ft.FTConfig.HostType.valueOf(def.trim().toUpperCase()); if (def != null) currentHt = haus.nightmare.lib3270j.ft.FTConfig.HostType.valueOf(def.trim().toUpperCase());
} catch (Exception ignored) {} } catch (Exception ignored) {}
@@ -905,6 +910,18 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
if (refreshTimer != null) { if (refreshTimer != null) {
refreshTimer.stop(); refreshTimer.stop();
} }
if (findDialog != null) {
findDialog.dispose();
findDialog = null;
}
if (scriptDialog != null) {
scriptDialog.dispose();
scriptDialog = null;
}
if (fieldInspectorDialog != null) {
fieldInspectorDialog.dispose();
fieldInspectorDialog = null;
}
if (printerSessionDialog != null) { if (printerSessionDialog != null) {
printerSessionDialog.dispose(); printerSessionDialog.dispose();
} }
@@ -985,6 +1002,18 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
return statusBar; return statusBar;
} }
public FindDialog getFindDialog() {
return findDialog;
}
public ScriptDialog getScriptDialog() {
return scriptDialog;
}
public FieldInspectorDialog getFieldInspectorDialog() {
return fieldInspectorDialog;
}
public void connect(ConnectionConfig config) { public void connect(ConnectionConfig config) {
connect(config, true); connect(config, true);
} }
@@ -1004,19 +1033,19 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
lastHost = config.getHost(); lastHost = config.getHost();
lastPort = config.getPort(); lastPort = config.getPort();
haus.nightmare.j3270.config.Settings.setAutoConnectHost(config.getHost()); Settings.setAutoConnectHost(config.getHost());
haus.nightmare.j3270.config.Settings.setAutoConnectPort(config.getPort()); Settings.setAutoConnectPort(config.getPort());
haus.nightmare.j3270.config.Settings.setAutoConnectTls(config.isUseTls()); Settings.setAutoConnectTls(config.isUseTls());
haus.nightmare.j3270.config.Settings.setAutoConnectVerifyCert(config.isTlsVerifyCert()); Settings.setAutoConnectVerifyCert(config.isTlsVerifyCert());
haus.nightmare.j3270.config.Settings.setCodePage(config.getCodePage()); Settings.setCodePage(config.getCodePage());
if (config.getHostType() != null) { if (config.getHostType() != null) {
haus.nightmare.j3270.config.Settings.setDefaultHostType(config.getHostType()); Settings.setDefaultHostType(config.getHostType());
} }
if (FileTransferDialog.getLastTransferState() != null && config.isSavedConnection()) { if (FileTransferDialog.getLastTransferState() != null && config.isSavedConnection()) {
FileTransferDialog.getLastTransferState().hostType = config.getFtHostType(); FileTransferDialog.getLastTransferState().hostType = config.getFtHostType();
} }
if (config.getGraphicsMode() != null) { if (config.getGraphicsMode() != null) {
haus.nightmare.j3270.config.Settings.setGraphicsMode(config.getGraphicsMode()); Settings.setGraphicsMode(config.getGraphicsMode());
} }
Telnet3270Client oldClient = client; Telnet3270Client oldClient = client;
@@ -1045,6 +1074,15 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
terminalPanel.setClient(client); terminalPanel.setClient(client);
statusBar.setClient(client, terminalPanel); statusBar.setClient(client, terminalPanel);
if (findDialog != null) {
findDialog.setClient(client);
}
if (scriptDialog != null) {
scriptDialog.setClient(client);
}
if (fieldInspectorDialog != null) {
fieldInspectorDialog.setClient(client);
}
String tlsIndicator = config.isUseTls() ? (config.isTlsVerifyCert() ? " [TLS]" : " [TLS/Unverified]") : ""; String tlsIndicator = config.isUseTls() ? (config.isTlsVerifyCert() ? " [TLS]" : " [TLS/Unverified]") : "";
setTitle("j3270 — " + config.getHost() + ":" + config.getPort() + tlsIndicator); setTitle("j3270 — " + config.getHost() + ":" + config.getPort() + tlsIndicator);
@@ -1101,6 +1139,15 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
client = null; client = null;
c.removeConnectionListener(this); c.removeConnectionListener(this);
c.removeScreenUpdateListener(this); c.removeScreenUpdateListener(this);
if (findDialog != null) {
findDialog.setClient(null);
}
if (scriptDialog != null) {
scriptDialog.setClient(null);
}
if (fieldInspectorDialog != null) {
fieldInspectorDialog.setClient(null);
}
new Thread(() -> { new Thread(() -> {
try { try {
@@ -1142,7 +1189,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
if (statusBar != null) { if (statusBar != null) {
statusBar.setVisible(visible); statusBar.setVisible(visible);
} }
haus.nightmare.j3270.config.Settings.setStatusBarVisible(visible); Settings.setStatusBarVisible(visible);
if (statusBarItem != null) { if (statusBarItem != null) {
statusBarItem.setSelected(visible); statusBarItem.setSelected(visible);
} }
@@ -1203,6 +1250,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
if (screenUpdatePending.compareAndSet(false, true)) { if (screenUpdatePending.compareAndSet(false, true)) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
screenUpdatePending.set(false); screenUpdatePending.set(false);
terminalPanel.onScreenUpdated();
if (fileTransfer != null) { if (fileTransfer != null) {
fileTransfer.onScreenUpdated(); fileTransfer.onScreenUpdated();
} }
@@ -1224,6 +1272,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
@Override @Override
public void onScreenSizeChanged(int rows, int cols) { public void onScreenSizeChanged(int rows, int cols) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
terminalPanel.onScreenSizeChanged(rows, cols);
terminalPanel.revalidate(); terminalPanel.revalidate();
terminalPanel.repaint(); terminalPanel.repaint();
statusBar.updateStatus(); statusBar.updateStatus();
@@ -1504,7 +1553,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
if (configFile != null) { if (configFile != null) {
try { try {
haus.nightmare.j3270.config.Settings.loadFromIniFile(configFile); Settings.loadFromIniFile(configFile);
} catch (Exception e) { } catch (Exception e) {
System.err.println("Failed to load config file: " + configFile); System.err.println("Failed to load config file: " + configFile);
System.err.println(e.getMessage()); System.err.println(e.getMessage());
@@ -1517,8 +1566,8 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
} catch (Exception e) { } catch (Exception e) {
log.fine("Could not set system look and feel"); log.fine("Could not set system look and feel");
} }
ThemeManager.applyUIManagerDefaults(haus.nightmare.j3270.config.Settings.getJavaUiTheme()); ThemeManager.applyUIManagerDefaults(Settings.getJavaUiTheme());
ThemeManager.setTheme(haus.nightmare.j3270.config.Settings.getJavaUiTheme()); ThemeManager.setTheme(Settings.getJavaUiTheme());
System.setProperty("apple.laf.useScreenMenuBar", "true"); System.setProperty("apple.laf.useScreenMenuBar", "true");
System.setProperty("apple.awt.application.name", "j3270"); System.setProperty("apple.awt.application.name", "j3270");
@@ -1590,12 +1639,12 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
if (finalAutoSysUnlock != null) { if (finalAutoSysUnlock != null) {
config.setAutoSysUnlock(finalAutoSysUnlock); config.setAutoSysUnlock(finalAutoSysUnlock);
} else { } else {
config.setAutoSysUnlock(haus.nightmare.j3270.config.Settings.getAutoSysUnlock()); config.setAutoSysUnlock(Settings.getAutoSysUnlock());
} }
if (finalGraphicsMode != null) { if (finalGraphicsMode != null) {
config.setGraphicsMode(finalGraphicsMode); config.setGraphicsMode(finalGraphicsMode);
} else { } else {
config.setGraphicsMode(haus.nightmare.j3270.config.Settings.getGraphicsMode()); config.setGraphicsMode(Settings.getGraphicsMode());
} }
if (finalProxyUrl != null) { if (finalProxyUrl != null) {
applyProxyUrl(config, finalProxyUrl); applyProxyUrl(config, finalProxyUrl);
@@ -1603,11 +1652,11 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
if (finalHostType != null) { if (finalHostType != null) {
config.setHostType(finalHostType); config.setHostType(finalHostType);
} else { } else {
config.setHostType(haus.nightmare.j3270.config.Settings.getDefaultHostType()); config.setHostType(Settings.getDefaultHostType());
} }
app.connect(config); app.connect(config);
} else { } else {
haus.nightmare.j3270.config.Settings.StartupBehavior behavior = haus.nightmare.j3270.config.Settings Settings.StartupBehavior behavior = Settings
.getStartupBehavior(); .getStartupBehavior();
switch (behavior) { switch (behavior) {
case SHOW_CONNECT: case SHOW_CONNECT:
@@ -1626,22 +1675,22 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
if (finalProxyUrl != null) applyProxyUrl(config, finalProxyUrl); if (finalProxyUrl != null) applyProxyUrl(config, finalProxyUrl);
app.connect(config); app.connect(config);
} else { } else {
String host = haus.nightmare.j3270.config.Settings.getAutoConnectHost(); String host = Settings.getAutoConnectHost();
int port = haus.nightmare.j3270.config.Settings.getAutoConnectPort(); int port = Settings.getAutoConnectPort();
boolean tls = haus.nightmare.j3270.config.Settings.getAutoConnectTls(); boolean tls = Settings.getAutoConnectTls();
boolean verify = haus.nightmare.j3270.config.Settings.getAutoConnectVerifyCert(); boolean verify = Settings.getAutoConnectVerifyCert();
boolean tn3270e = haus.nightmare.j3270.config.Settings.getAutoConnectTn3270e(); boolean tn3270e = Settings.getAutoConnectTn3270e();
if (host != null && !host.isEmpty()) { if (host != null && !host.isEmpty()) {
ConnectionConfig config = new ConnectionConfig(host, port, TerminalModel.IBM_3279_4, tls); ConnectionConfig config = new ConnectionConfig(host, port, TerminalModel.IBM_3279_4, tls);
config.setTlsVerifyCert(verify); config.setTlsVerifyCert(verify);
config.setTn3270eEnabled(finalTn3270e != null ? finalTn3270e : tn3270e); config.setTn3270eEnabled(finalTn3270e != null ? finalTn3270e : tn3270e);
config.setAutoReconnect(haus.nightmare.j3270.config.Settings.getAutoConnectAutoReconnect()); config.setAutoReconnect(Settings.getAutoConnectAutoReconnect());
config.setReconnectMaxRetries(haus.nightmare.j3270.config.Settings.getAutoConnectReconnectMaxRetries()); config.setReconnectMaxRetries(Settings.getAutoConnectReconnectMaxRetries());
config.setAutoSysUnlock(finalAutoSysUnlock != null ? finalAutoSysUnlock : haus.nightmare.j3270.config.Settings.getAutoSysUnlock()); config.setAutoSysUnlock(finalAutoSysUnlock != null ? finalAutoSysUnlock : Settings.getAutoSysUnlock());
if (finalGraphicsMode != null) { if (finalGraphicsMode != null) {
config.setGraphicsMode(finalGraphicsMode); config.setGraphicsMode(finalGraphicsMode);
} else { } else {
config.setGraphicsMode(haus.nightmare.j3270.config.Settings.getGraphicsMode()); config.setGraphicsMode(Settings.getGraphicsMode());
} }
if (finalProxyUrl != null) { if (finalProxyUrl != null) {
applyProxyUrl(config, finalProxyUrl); applyProxyUrl(config, finalProxyUrl);
@@ -1649,7 +1698,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
if (finalHostType != null) { if (finalHostType != null) {
config.setHostType(finalHostType); config.setHostType(finalHostType);
} else { } else {
config.setHostType(haus.nightmare.j3270.config.Settings.getDefaultHostType()); config.setHostType(Settings.getDefaultHostType());
} }
app.connect(config); app.connect(config);
} else { } else {
@@ -21,7 +21,7 @@ import java.util.List;
*/ */
public class FieldInspectorDialog extends JDialog implements ScreenUpdateListener { public class FieldInspectorDialog extends JDialog implements ScreenUpdateListener {
private final Telnet3270Client client; private Telnet3270Client client;
private final TerminalPanel terminalPanel; private final TerminalPanel terminalPanel;
private DefaultTableModel tableModel; private DefaultTableModel tableModel;
@@ -46,13 +46,39 @@ public class FieldInspectorDialog extends JDialog implements ScreenUpdateListene
addWindowListener(new WindowAdapter() { addWindowListener(new WindowAdapter() {
@Override @Override
public void windowClosing(WindowEvent e) { public void windowClosing(WindowEvent e) {
if (client != null) { if (FieldInspectorDialog.this.client != null) {
// Screen update listener removed on close FieldInspectorDialog.this.client.removeScreenUpdateListener(FieldInspectorDialog.this);
} }
} }
}); });
} }
public void setClient(Telnet3270Client newClient) {
if (this.client != null) {
this.client.removeScreenUpdateListener(this);
}
this.client = newClient;
if (this.client != null) {
this.client.addScreenUpdateListener(this);
refreshFields();
} else if (tableModel != null && countLabel != null) {
tableModel.setRowCount(0);
countLabel.setText("Not connected");
}
}
public Telnet3270Client getClient() {
return this.client;
}
@Override
public void dispose() {
if (client != null) {
client.removeScreenUpdateListener(this);
}
super.dispose();
}
private void buildUI() { private void buildUI() {
JPanel mainPanel = new JPanel(new BorderLayout(8, 8)); JPanel mainPanel = new JPanel(new BorderLayout(8, 8));
mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
@@ -15,7 +15,7 @@ import java.awt.event.KeyEvent;
*/ */
public class FindDialog extends JDialog { public class FindDialog extends JDialog {
private final Telnet3270Client client; private Telnet3270Client client;
private final TerminalPanel terminalPanel; private final TerminalPanel terminalPanel;
private JTextField searchField; private JTextField searchField;
@@ -40,6 +40,24 @@ public class FindDialog extends JDialog {
setResizable(false); setResizable(false);
} }
public void setClient(Telnet3270Client client) {
this.client = client;
}
public Telnet3270Client getClient() {
return this.client;
}
public void setSearchText(String text) {
if (searchField != null) {
searchField.setText(text);
}
}
public String getSearchText() {
return searchField != null ? searchField.getText() : "";
}
private void buildUI() { private void buildUI() {
JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(new EmptyBorder(12, 14, 12, 14)); mainPanel.setBorder(new EmptyBorder(12, 14, 12, 14));
@@ -143,6 +143,9 @@ public class HotspotEngine {
private int cachedRows = 0; private int cachedRows = 0;
private int cachedCols = 0; private int cachedCols = 0;
private List<Hotspot> cachedHotspots = Collections.emptyList(); private List<Hotspot> cachedHotspots = Collections.emptyList();
private ScreenBuffer lastScreenBuffer;
private long lastSnapshotVersion = -1;
private long scanCount = 0;
public HotspotEngine() { public HotspotEngine() {
} }
@@ -174,9 +177,19 @@ public class HotspotEngine {
invalidateCache(); invalidateCache();
} }
public void invalidateCache() { public synchronized void invalidateCache() {
cachedScreenText = ""; cachedScreenText = "";
cachedHotspots = Collections.emptyList(); cachedHotspots = Collections.emptyList();
lastScreenBuffer = null;
lastSnapshotVersion = -1;
}
public synchronized void markDirty() {
invalidateCache();
}
public synchronized long getScanCount() {
return scanCount;
} }
/** /**
@@ -186,6 +199,13 @@ public class HotspotEngine {
if (sb == null) return Collections.emptyList(); if (sb == null) return Collections.emptyList();
int rows = sb.getDisplayRows(); int rows = sb.getDisplayRows();
int cols = sb.getDisplayCols(); int cols = sb.getDisplayCols();
long version = sb.getSnapshotVersion();
if (sb == lastScreenBuffer && version == lastSnapshotVersion && rows == cachedRows && cols == cachedCols) {
return cachedHotspots;
}
scanCount++;
// Build plain text representation // Build plain text representation
StringBuilder fullText = new StringBuilder(rows * (cols + 1)); StringBuilder fullText = new StringBuilder(rows * (cols + 1));
@@ -204,7 +224,10 @@ public class HotspotEngine {
} }
} }
return scanText(fullText.toString(), rows, cols); List<Hotspot> results = scanText(fullText.toString(), rows, cols);
lastScreenBuffer = sb;
lastSnapshotVersion = version;
return results;
} }
/** /**
@@ -15,7 +15,7 @@ import java.nio.charset.StandardCharsets;
*/ */
public class ScriptDialog extends JDialog { public class ScriptDialog extends JDialog {
private final Telnet3270Client client; private Telnet3270Client client;
private final TerminalPanel terminalPanel; private final TerminalPanel terminalPanel;
private JTextArea scriptArea; private JTextArea scriptArea;
@@ -32,6 +32,24 @@ public class ScriptDialog extends JDialog {
setLocationRelativeTo(parent); setLocationRelativeTo(parent);
} }
public void setClient(Telnet3270Client client) {
this.client = client;
}
public Telnet3270Client getClient() {
return this.client;
}
public void setScriptText(String text) {
if (scriptArea != null) {
scriptArea.setText(text);
}
}
public String getScriptText() {
return scriptArea != null ? scriptArea.getText() : "";
}
private void buildUI() { private void buildUI() {
JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(new EmptyBorder(12, 14, 12, 14)); mainPanel.setBorder(new EmptyBorder(12, 14, 12, 14));
@@ -64,6 +64,8 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
CHAR_STRINGS[i] = String.valueOf((char) i); CHAR_STRINGS[i] = String.valueOf((char) i);
} }
} }
// Reusable single-character buffer for drawing non-ASCII cells (Phase 27.4 / T1-A)
private final char[] charScratch = new char[1];
public enum CursorStyle { public enum CursorStyle {
BLOCK, BLOCK,
UNDERLINE UNDERLINE
@@ -110,10 +112,14 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
// ========== Phase 25: Hotspots & Mouse Wheel ========== // ========== Phase 25: Hotspots & Mouse Wheel ==========
private final HotspotEngine hotspotEngine = new HotspotEngine(); private final HotspotEngine hotspotEngine = new HotspotEngine();
private HotspotEngine.Hotspot hoveredHotspot = null; private HotspotEngine.Hotspot hoveredHotspot = null;
private static final Color HOTSPOT_HOVER_COLOR = new Color(0, 220, 255);
private static final Stroke HOTSPOT_HOVER_STROKE = new BasicStroke(2.0f);
private boolean hotspotsEnabled = haus.nightmare.j3270.config.Settings.getHotspotsEnabled(); private boolean hotspotsEnabled = haus.nightmare.j3270.config.Settings.getHotspotsEnabled();
private boolean mouseWheelEnabled = haus.nightmare.j3270.config.Settings.getMouseWheelEnabled(); private boolean mouseWheelEnabled = haus.nightmare.j3270.config.Settings.getMouseWheelEnabled();
private String mouseWheelUpAction = haus.nightmare.j3270.config.Settings.getMouseWheelUpKey(); private String mouseWheelUpAction = haus.nightmare.j3270.config.Settings.getMouseWheelUpKey();
private String mouseWheelDownAction = haus.nightmare.j3270.config.Settings.getMouseWheelDownKey(); private String mouseWheelDownAction = haus.nightmare.j3270.config.Settings.getMouseWheelDownKey();
private boolean hotspotsDirty = true;
private boolean[] selectedCellsScratch = new boolean[0];
// ========== Mode and Listener State ========== // ========== Mode and Listener State ==========
private boolean insertMode = false; private boolean insertMode = false;
@@ -400,7 +406,10 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
int col = (e.getX() - ox) / cellWidth; int col = (e.getX() - ox) / cellWidth;
int row = (e.getY() - oy) / cellHeight; int row = (e.getY() - oy) / cellHeight;
if (row >= 0 && row < rows && col >= 0 && col < cols) { if (row >= 0 && row < rows && col >= 0 && col < cols) {
if (hotspotsDirty) {
hotspotEngine.scan(sb); hotspotEngine.scan(sb);
hotspotsDirty = false;
}
HotspotEngine.Hotspot h = hotspotEngine.findHotspot(row, col); HotspotEngine.Hotspot h = hotspotEngine.findHotspot(row, col);
if (h != null) { if (h != null) {
if (hoveredHotspot != h) { if (hoveredHotspot != h) {
@@ -515,6 +524,10 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
// Phase 25.2: Interactive Hotspot click execution (URLs, PF keys, choices) // Phase 25.2: Interactive Hotspot click execution (URLs, PF keys, choices)
if (hotspotsEnabled && client.getConnectionState().isFullSession()) { if (hotspotsEnabled && client.getConnectionState().isFullSession()) {
if (hotspotsDirty) {
hotspotEngine.scan(sb);
hotspotsDirty = false;
}
HotspotEngine.Hotspot h = hotspotEngine.findHotspot(row, col); HotspotEngine.Hotspot h = hotspotEngine.findHotspot(row, col);
if (h != null) { if (h != null) {
if (h.getType() == HotspotEngine.HotspotType.URL) { if (h.getType() == HotspotEngine.HotspotType.URL) {
@@ -1054,7 +1067,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
popup.show(this, e.getX(), e.getY()); popup.show(this, e.getX(), e.getY());
} }
private boolean isCellSelected(int row, int col) { public boolean isCellSelected(int row, int col) {
if (!hasSelection()) return false; if (!hasSelection()) return false;
int r1 = Math.min(selectionStartRow, selectionEndRow); int r1 = Math.min(selectionStartRow, selectionEndRow);
@@ -1066,7 +1079,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
return row >= r1 && row <= r2 && col >= c1 && col <= c2; return row >= r1 && row <= r2 && col >= c1 && col <= c2;
} else { } else {
int cols = 80; int cols = 80;
if (client != null) cols = client.getScreenBuffer().getCols(); if (client != null && client.getScreenBuffer() != null) cols = client.getScreenBuffer().getDisplayCols();
int addr = row * cols + col; int addr = row * cols + col;
int startAddr, endAddr; int startAddr, endAddr;
if (selectionStartRow < selectionEndRow || if (selectionStartRow < selectionEndRow ||
@@ -1081,6 +1094,51 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
} }
} }
public void precomputeSelectionScratch(int rows, int cols) {
int totalCells = rows * cols;
if (selectedCellsScratch == null || selectedCellsScratch.length < totalCells) {
selectedCellsScratch = new boolean[totalCells];
}
java.util.Arrays.fill(selectedCellsScratch, 0, totalCells, false);
if (hasSelection()) {
int r1 = Math.min(selectionStartRow, selectionEndRow);
int r2 = Math.max(selectionStartRow, selectionEndRow);
if (blockSelectMode || trimRectSizingHandles) {
int c1 = Math.min(selectionStartCol, selectionEndCol);
int c2 = Math.max(selectionStartCol, selectionEndCol);
int minR = Math.max(0, r1);
int maxR = Math.min(rows - 1, r2);
int minC = Math.max(0, c1);
int maxC = Math.min(cols - 1, c2);
if (minC <= maxC) {
for (int r = minR; r <= maxR; r++) {
int rowBase = r * cols;
java.util.Arrays.fill(selectedCellsScratch, rowBase + minC, rowBase + maxC + 1, true);
}
}
} else {
int startAddr, endAddr;
if (selectionStartRow < selectionEndRow ||
(selectionStartRow == selectionEndRow && selectionStartCol <= selectionEndCol)) {
startAddr = selectionStartRow * cols + selectionStartCol;
endAddr = selectionEndRow * cols + selectionEndCol;
} else {
startAddr = selectionEndRow * cols + selectionEndCol;
endAddr = selectionStartRow * cols + selectionStartCol;
}
int s = Math.max(0, startAddr);
int e = Math.min(totalCells - 1, endAddr);
if (s <= e) {
java.util.Arrays.fill(selectedCellsScratch, s, e + 1, true);
}
}
}
}
public boolean[] getSelectedCellsScratch() {
return selectedCellsScratch;
}
public void packForModel(int modelRows, int modelCols) { public void packForModel(int modelRows, int modelCols) {
if (isDisplayable() && !SwingUtilities.isEventDispatchThread()) { if (isDisplayable() && !SwingUtilities.isEventDispatchThread()) {
@@ -1687,6 +1745,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
} }
public void applyModeSettings() { public void applyModeSettings() {
hotspotsDirty = true;
if (client != null) { if (client != null) {
ScreenBuffer sb = client.getScreenBuffer(); ScreenBuffer sb = client.getScreenBuffer();
if (sb != null) { if (sb != null) {
@@ -1723,6 +1782,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
} }
public void refreshScreen() { public void refreshScreen() {
hotspotsDirty = true;
if (client != null) { if (client != null) {
client.getScreenBuffer().updateDisplaySnapshot(); client.getScreenBuffer().updateDisplaySnapshot();
} }
@@ -1740,6 +1800,26 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
} }
} }
public void onScreenUpdated() {
hotspotsDirty = true;
}
public void onScreenSizeChanged() {
hotspotsDirty = true;
}
public void onScreenSizeChanged(int rows, int cols) {
hotspotsDirty = true;
}
public boolean isHotspotsDirty() {
return hotspotsDirty;
}
public void setHotspotsDirty(boolean hotspotsDirty) {
this.hotspotsDirty = hotspotsDirty;
}
private void setupFont() { private void setupFont() {
currentFontSize = haus.nightmare.j3270.config.Settings.getFontSize(); currentFontSize = haus.nightmare.j3270.config.Settings.getFontSize();
String fontFamily = haus.nightmare.j3270.config.Settings.getFontFamily(); String fontFamily = haus.nightmare.j3270.config.Settings.getFontFamily();
@@ -1880,6 +1960,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
public void setClient(Telnet3270Client client) { public void setClient(Telnet3270Client client) {
this.client = client; this.client = client;
hotspotsDirty = true;
if (client != null) { if (client != null) {
setupGraphicsPlaneRenderer(); setupGraphicsPlaneRenderer();
updateCellSize(); updateCellSize();
@@ -2076,6 +2157,9 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
g2.fillRect(cx, oy, cellWidth, gridH); g2.fillRect(cx, oy, cellWidth, gridH);
} }
// Phase 28.2 / T2-B: Precompute selection scratch lookup once per frame
precomputeSelectionScratch(rows, cols);
// Layer 3: Character / Text Plane // Layer 3: Character / Text Plane
byte currentFA = 0; byte currentFA = 0;
ExtendedAttribute currentFieldEa = null; ExtendedAttribute currentFieldEa = null;
@@ -2097,7 +2181,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
if (ea.isFieldAttribute()) { if (ea.isFieldAttribute()) {
currentFA = ea.fa; currentFA = ea.fa;
currentFieldEa = ea; currentFieldEa = ea;
if (isCellSelected(row, col)) { if (selectedCellsScratch[baddr]) {
g2.setColor(SELECTION_COLOR); g2.setColor(SELECTION_COLOR);
g2.fillRect(x, y, cellWidth, cellHeight); g2.fillRect(x, y, cellWidth, cellHeight);
} }
@@ -2142,7 +2226,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
// Field Outlining (0xC2 / TY_FOUTLINE) // Field Outlining (0xC2 / TY_FOUTLINE)
drawOutlining(g2, ea, currentFieldEa, baddr, row, col, x, y, cellWidth, cellHeight, rows, cols, sb, fgColor); drawOutlining(g2, ea, currentFieldEa, baddr, row, col, x, y, cellWidth, cellHeight, rows, cols, sb, fgColor);
if (isCellSelected(row, col)) { if (selectedCellsScratch[baddr]) {
g2.setColor(SELECTION_COLOR); g2.setColor(SELECTION_COLOR);
g2.fillRect(x, y, cellWidth, cellHeight); g2.fillRect(x, y, cellWidth, cellHeight);
} }
@@ -2194,7 +2278,8 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
if (ch < 128) { if (ch < 128) {
g2.drawString(CHAR_STRINGS[ch], x, textY); g2.drawString(CHAR_STRINGS[ch], x, textY);
} else { } else {
g2.drawString(String.valueOf(ch), x, textY); charScratch[0] = ch;
g2.drawChars(charScratch, 0, 1, x, textY);
} }
} }
} }
@@ -2215,7 +2300,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
} }
// Draw selection highlight (40% alpha blend over cell) // Draw selection highlight (40% alpha blend over cell)
if (isCellSelected(row, col)) { if (selectedCellsScratch[baddr]) {
g2.setColor(SELECTION_COLOR); g2.setColor(SELECTION_COLOR);
g2.fillRect(x, y, cellWidth, cellHeight); g2.fillRect(x, y, cellWidth, cellHeight);
} }
@@ -2261,8 +2346,8 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable, I
if (hotspotsEnabled && hoveredHotspot != null) { if (hotspotsEnabled && hoveredHotspot != null) {
Color oldColor = g2.getColor(); Color oldColor = g2.getColor();
Stroke oldStroke = g2.getStroke(); Stroke oldStroke = g2.getStroke();
g2.setColor(new Color(0, 220, 255)); // Bright cyan hotspot underline g2.setColor(HOTSPOT_HOVER_COLOR); // Bright cyan hotspot underline
g2.setStroke(new BasicStroke(2.0f)); g2.setStroke(HOTSPOT_HOVER_STROKE);
int hr0 = hoveredHotspot.getStartRow(); int hr0 = hoveredHotspot.getStartRow();
int hr1 = hoveredHotspot.getEndRow(); int hr1 = hoveredHotspot.getEndRow();
+42
View File
@@ -1,5 +1,11 @@
plugins { plugins {
id 'java-library' id 'java-library'
id 'maven-publish'
}
java {
withSourcesJar()
withJavadocJar()
} }
dependencies { dependencies {
@@ -17,3 +23,39 @@ jar {
'Implementation-Version': archiveVersion 'Implementation-Version': archiveVersion
} }
} }
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
groupId = 'haus.nightmare.j3270'
artifactId = 'lib3270j'
version = project.version
pom {
name = 'lib3270j'
description = 'Pure Java IBM 3270 terminal emulation and protocol library with Host On-Demand (HOD) parity'
url = 'https://github.com/rudi/j3270'
licenses {
license {
name = 'The Unlicense'
url = 'https://unlicense.org/'
distribution = 'repo'
}
}
developers {
developer {
id = 'rudi'
name = 'Rudi'
}
}
scm {
connection = 'scm:git:git://github.com/rudi/j3270.git'
developerConnection = 'scm:git:ssh://github.com/rudi/j3270.git'
url = 'https://github.com/rudi/j3270'
}
}
}
}
}
@@ -8,6 +8,12 @@ import java.util.Arrays;
* Reusable, resizable byte buffer that provides zero-copy access to its internal * Reusable, resizable byte buffer that provides zero-copy access to its internal
* array and supports ByteBuffer slicing to minimize heap allocations during * array and supports ByteBuffer slicing to minimize heap allocations during
* network stream processing. * network stream processing.
* <p>
* <b>Threading Contract:</b> This class is NOT thread-safe and is designed for
* high-throughput single-threaded or thread-confined stream accumulation (such as
* Telnet network receiver loops matching IBM Host On-Demand {@code sArray} and
* {@code sb_buffer}). External synchronization must be used if instances are shared
* across threads.
*/ */
public class FastByteBuffer extends OutputStream { public class FastByteBuffer extends OutputStream {
@@ -31,13 +37,13 @@ public class FastByteBuffer extends OutputStream {
} }
@Override @Override
public synchronized void write(int b) { public void write(int b) {
ensureCapacity(count + 1); ensureCapacity(count + 1);
buf[count++] = (byte) b; buf[count++] = (byte) b;
} }
@Override @Override
public synchronized void write(byte[] b, int off, int len) { public void write(byte[] b, int off, int len) {
if (b == null || len <= 0) return; if (b == null || len <= 0) return;
ensureCapacity(count + len); ensureCapacity(count + len);
System.arraycopy(b, off, buf, count, len); System.arraycopy(b, off, buf, count, len);
@@ -48,29 +54,29 @@ public class FastByteBuffer extends OutputStream {
* Direct reference to internal buffer array. * Direct reference to internal buffer array.
* Use {@link #size()} to determine active length. * Use {@link #size()} to determine active length.
*/ */
public synchronized byte[] buffer() { public byte[] buffer() {
return buf; return buf;
} }
public synchronized int size() { public int size() {
return count; return count;
} }
public synchronized void reset() { public void reset() {
count = 0; count = 0;
} }
/** /**
* Creates a read-only ByteBuffer view wrapping active bytes without copying. * Creates a read-only ByteBuffer view wrapping active bytes without copying.
*/ */
public synchronized ByteBuffer asByteBuffer() { public ByteBuffer asByteBuffer() {
return ByteBuffer.wrap(buf, 0, count).asReadOnlyBuffer(); return ByteBuffer.wrap(buf, 0, count).asReadOnlyBuffer();
} }
/** /**
* Creates a read-only ByteBuffer slice for a sub-range without copying. * Creates a read-only ByteBuffer slice for a sub-range without copying.
*/ */
public synchronized ByteBuffer slice(int offset, int length) { public ByteBuffer slice(int offset, int length) {
if (offset < 0 || length < 0 || offset + length > count) { if (offset < 0 || length < 0 || offset + length > count) {
throw new IndexOutOfBoundsException("offset=" + offset + " length=" + length + " size=" + count); throw new IndexOutOfBoundsException("offset=" + offset + " length=" + length + " size=" + count);
} }
@@ -80,7 +86,7 @@ public class FastByteBuffer extends OutputStream {
/** /**
* Produces a copied byte array if an isolated copy is explicitly required. * Produces a copied byte array if an isolated copy is explicitly required.
*/ */
public synchronized byte[] toByteArray() { public byte[] toByteArray() {
return Arrays.copyOf(buf, count); return Arrays.copyOf(buf, count);
} }
} }
@@ -110,6 +110,14 @@ public class ExtendedAttribute {
*/ */
public char ucs4; public char ucs4;
public ExtendedAttribute() {}
public ExtendedAttribute(ExtendedAttribute other) {
if (other != null) {
copyFrom(other);
}
}
/** Clear all attributes. */ /** Clear all attributes. */
public void clear() { public void clear() {
ec = 0; ec = 0;
@@ -197,48 +197,99 @@ public class ScreenBuffer {
return buffer[addr]; return buffer[addr];
} }
/**
* Immutable snapshot container for tear-free, lock-free screen display.
* Published atomically to UI thread / EDT via volatile reference release.
*/
public static final class DisplaySnapshot {
public final ExtendedAttribute[] cells;
public final int rows;
public final int cols;
public final int cursorAddress;
public final long version;
public DisplaySnapshot(ExtendedAttribute[] cells, int rows, int cols, int cursorAddress) {
this(cells, rows, cols, cursorAddress, 0L);
}
public DisplaySnapshot(ExtendedAttribute[] cells, int rows, int cols, int cursorAddress, long version) {
this.cells = cells;
this.rows = rows;
this.cols = cols;
this.cursorAddress = cursorAddress;
this.version = version;
}
public ExtendedAttribute getCell(int addr) {
if (cells == null || addr < 0 || addr >= cells.length) {
return null;
}
return cells[addr];
}
public int getRows() { return rows; }
public int getCols() { return cols; }
public int getCursorAddress() { return cursorAddress; }
public long getVersion() { return version; }
}
private volatile DisplaySnapshot currentSnapshot;
private ExtendedAttribute[] displaySnapshot; private ExtendedAttribute[] displaySnapshot;
private int displayRows; private int displayRows;
private int displayCols; private int displayCols;
private int displayCursorAddress; private int displayCursorAddress;
private long snapshotVersion = 0;
/** /**
* Atomically creates a snapshot of the current presentation buffer for tear-free rendering. * Atomically creates an immutable snapshot of the current presentation buffer for tear-free rendering.
* Takes ~2 microseconds and eliminates mutual thread contention with the UI thread. * Published via a volatile release fence, eliminating mutual monitor contention with the UI paint loop.
*/ */
public synchronized void updateDisplaySnapshot() { public synchronized void updateDisplaySnapshot() {
int size = rows * cols; int size = rows * cols;
if (displaySnapshot == null || displaySnapshot.length < size) { ExtendedAttribute[] snapshotCells = new ExtendedAttribute[size];
displaySnapshot = new ExtendedAttribute[size];
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
displaySnapshot[i] = new ExtendedAttribute(); ExtendedAttribute ea = new ExtendedAttribute();
} ea.copyFrom(buffer[i]);
} snapshotCells[i] = ea;
for (int i = 0; i < size; i++) {
displaySnapshot[i].copyFrom(buffer[i]);
} }
this.displayRows = rows; this.displayRows = rows;
this.displayCols = cols; this.displayCols = cols;
this.displayCursorAddress = cursorAddress; this.displayCursorAddress = cursorAddress;
this.snapshotVersion++;
this.displaySnapshot = snapshotCells;
this.currentSnapshot = new DisplaySnapshot(snapshotCells, rows, cols, cursorAddress, snapshotVersion);
} }
public synchronized ExtendedAttribute getDisplayCell(int addr) { public DisplaySnapshot getDisplaySnapshot() {
if (displaySnapshot == null || addr < 0 || addr >= displayRows * displayCols) { return currentSnapshot;
}
public long getSnapshotVersion() {
DisplaySnapshot snap = this.currentSnapshot;
return snap != null ? snap.version : snapshotVersion;
}
public ExtendedAttribute getDisplayCell(int addr) {
DisplaySnapshot snap = this.currentSnapshot;
if (snap == null || addr < 0 || addr >= snap.cells.length) {
return getCell(addr); return getCell(addr);
} }
return displaySnapshot[addr]; return snap.cells[addr];
} }
public synchronized int getDisplayRows() { public int getDisplayRows() {
return displayRows > 0 ? displayRows : rows; DisplaySnapshot snap = this.currentSnapshot;
return (snap != null && snap.rows > 0) ? snap.rows : (displayRows > 0 ? displayRows : rows);
} }
public synchronized int getDisplayCols() { public int getDisplayCols() {
return displayCols > 0 ? displayCols : cols; DisplaySnapshot snap = this.currentSnapshot;
return (snap != null && snap.cols > 0) ? snap.cols : (displayCols > 0 ? displayCols : cols);
} }
public synchronized int getDisplayCursorAddress() { public int getDisplayCursorAddress() {
return displayRows > 0 ? displayCursorAddress : cursorAddress; DisplaySnapshot snap = this.currentSnapshot;
return (snap != null && snap.rows > 0) ? snap.cursorAddress : (displayRows > 0 ? displayCursorAddress : cursorAddress);
} }
// ========== Dimension accessors ========== // ========== Dimension accessors ==========
@@ -346,6 +397,10 @@ public class ScreenBuffer {
oldAddr = this.cursorAddress; oldAddr = this.cursorAddress;
this.cursorAddress = addr; this.cursorAddress = addr;
this.displayCursorAddress = addr; this.displayCursorAddress = addr;
DisplaySnapshot snap = this.currentSnapshot;
if (snap != null) {
this.currentSnapshot = new DisplaySnapshot(snap.cells, snap.rows, snap.cols, addr, snap.version);
}
} }
if (oldAddr != addr) { if (oldAddr != addr) {
notifyCursorMoved(oldAddr, addr); notifyCursorMoved(oldAddr, addr);
@@ -49,6 +49,9 @@ public class TelnetFSM {
// 3270 input buffer (accumulated between telnet framing) // 3270 input buffer (accumulated between telnet framing)
private final haus.nightmare.lib3270j.datastream.FastByteBuffer ibuf = new haus.nightmare.lib3270j.datastream.FastByteBuffer(32768); private final haus.nightmare.lib3270j.datastream.FastByteBuffer ibuf = new haus.nightmare.lib3270j.datastream.FastByteBuffer(32768);
// Reusable single-byte scratch buffer for plain NVT data dispatch (Phase 27.5 / T1-C)
private final byte[] nvtSingleByteScratch = new byte[1];
// Sub-negotiation buffer // Sub-negotiation buffer
private final ByteArrayOutputStream sbbuf = new ByteArrayOutputStream(4096); private final ByteArrayOutputStream sbbuf = new ByteArrayOutputStream(4096);
@@ -324,7 +327,8 @@ public class TelnetFSM {
boolean isPlainNvt = (connectionState == ConnectionState.CONNECTED_NVT || connectionState == ConnectionState.CONNECTED_NVT_CHAR) boolean isPlainNvt = (connectionState == ConnectionState.CONNECTED_NVT || connectionState == ConnectionState.CONNECTED_NVT_CHAR)
&& !(hisOpts[TELOPT_BINARY] && hisOpts[TELOPT_EOR]); && !(hisOpts[TELOPT_BINARY] && hisOpts[TELOPT_EOR]);
if (isPlainNvt) { if (isPlainNvt) {
nvtProcessor.processNVTData(new byte[] { (byte) c }, 0, 1); nvtSingleByteScratch[0] = (byte) c;
nvtProcessor.processNVTData(nvtSingleByteScratch, 0, 1);
return; return;
} }
+13 -2
View File
@@ -413,11 +413,19 @@ if (Test-Path $libResDir) {
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# 7. Package Standalone Executable JAR # 7. Package Standalone JARs
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
Write-LogInfo "Packaging standalone lib3270j library JAR..."
$libManifestFile = Join-Path $BuildDir "LIB_MANIFEST.MF"
$libManifestText = "Manifest-Version: 1.0`r`nImplementation-Title: lib3270j`r`nImplementation-Version: 1.2.4`r`nCreated-By: j3270 setup.ps1`r`n`r`n"
[System.IO.File]::WriteAllText($libManifestFile, $libManifestText)
$libJarFile = Join-Path $BuildDir "lib3270j.jar"
& $detectedJdk.Jar cvfm $libJarFile $libManifestFile -C $LibBuildDir . | Out-Null
Write-LogInfo "Packaging standalone executable JAR..." Write-LogInfo "Packaging standalone executable JAR..."
$manifestFile = Join-Path $BuildDir "MANIFEST.MF" $manifestFile = Join-Path $BuildDir "MANIFEST.MF"
$manifestText = "Manifest-Version: 1.0`r`nMain-Class: haus.nightmare.j3270.J3270App`r`nImplementation-Title: j3270`r`nImplementation-Version: 0.1.0`r`nCreated-By: j3270 setup.ps1`r`n`r`n" $manifestText = "Manifest-Version: 1.0`r`nMain-Class: haus.nightmare.j3270.J3270App`r`nImplementation-Title: j3270`r`nImplementation-Version: 1.2.4`r`nCreated-By: j3270 setup.ps1`r`n`r`n"
[System.IO.File]::WriteAllText($manifestFile, $manifestText) [System.IO.File]::WriteAllText($manifestFile, $manifestText)
$jarFile = Join-Path $BuildDir "j3270.jar" $jarFile = Join-Path $BuildDir "j3270.jar"
@@ -428,10 +436,13 @@ if (-not (Test-Path $jarFile)) {
exit 1 exit 1
} }
$libJarItem = Get-Item $libJarFile
$libJarSizeKB = [math]::Round($libJarItem.Length / 1KB, 1)
$jarItem = Get-Item $jarFile $jarItem = Get-Item $jarFile
$jarSizeKB = [math]::Round($jarItem.Length / 1KB, 1) $jarSizeKB = [math]::Round($jarItem.Length / 1KB, 1)
Write-Host "`n=== Build Successful ===`n" -ForegroundColor Green Write-Host "`n=== Build Successful ===`n" -ForegroundColor Green
Write-LogSuccess "Library JAR created: $libJarFile (${libJarSizeKB} KB)"
Write-LogSuccess "Executable JAR created: $jarFile (${jarSizeKB} KB)" Write-LogSuccess "Executable JAR created: $jarFile (${jarSizeKB} KB)"
Write-Host @" Write-Host @"
+18 -3
View File
@@ -369,15 +369,28 @@ if [ -d "$SCRIPT_DIR/lib3270j/src/main/resources" ]; then
fi fi
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# 5. Package Standalone Executable JAR # 5. Package Standalone JARs
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
log_info "Packaging standalone lib3270j library JAR..."
cat << 'EOF' > "$BUILD_DIR/LIB_MANIFEST.MF"
Manifest-Version: 1.0
Implementation-Title: lib3270j
Implementation-Version: 1.2.4
Created-By: j3270 setup.sh
EOF
"$JAR_BIN" cvfm "$BUILD_DIR/lib3270j.jar" "$BUILD_DIR/LIB_MANIFEST.MF" \
-C "$BUILD_DIR/lib3270j" . > /dev/null
log_info "Packaging standalone executable JAR..." log_info "Packaging standalone executable JAR..."
cat << 'EOF' > "$BUILD_DIR/MANIFEST.MF" cat << 'EOF' > "$BUILD_DIR/MANIFEST.MF"
Manifest-Version: 1.0 Manifest-Version: 1.0
Main-Class: haus.nightmare.j3270.J3270App Main-Class: haus.nightmare.j3270.J3270App
Implementation-Title: j3270 Implementation-Title: j3270
Implementation-Version: 0.1.0 Implementation-Version: 1.2.4
Created-By: j3270 setup.sh Created-By: j3270 setup.sh
EOF EOF
@@ -391,8 +404,10 @@ if [ ! -f "$BUILD_DIR/j3270.jar" ]; then
exit 1 exit 1
fi fi
JAR_SIZE=$(ls -lh "$BUILD_DIR/j3270.jar" | awk '{print $5}') LIB_JAR_SIZE=$(du -h "$BUILD_DIR/lib3270j.jar" | awk '{print $1}')
JAR_SIZE=$(du -h "$BUILD_DIR/j3270.jar" | awk '{print $1}')
printf "\n%b=== Build Successful ===%b\n" "$BOLD" "$RESET" printf "\n%b=== Build Successful ===%b\n" "$BOLD" "$RESET"
log_success "Library JAR: $BUILD_DIR/lib3270j.jar ($LIB_JAR_SIZE)"
log_success "Executable JAR: $BUILD_DIR/j3270.jar ($JAR_SIZE)" log_success "Executable JAR: $BUILD_DIR/j3270.jar ($JAR_SIZE)"
printf "\n%bTo run j3270:%b\n" "$BOLD" "$RESET" printf "\n%bTo run j3270:%b\n" "$BOLD" "$RESET"