Alternate screen size support.
This is a relatively big new feature, which introduces small breaking changes to the API: * NegotiateTelnet() now returns (DevInfo, error) instead of just error. The function signature for the Tx type now has one additional DevInfo parameter. * RunTransactions() requires one additional parameter, the DevInfo (which is allowed to be nil). Otherwise, existing behavior continues to work as-is and everything still defaults to operating on the default 24x80 terminal size. See example5 for a larger-terminal-aware application. Pending some time to make sure no new bugs cropped up in this release and that the alternate screen size support works in the wild, I anticipate this will become the v1.0.0 release of go3270.
This commit is contained in:
1 parent
3a38ce6e4a
commit
b6fa48f37f
19 files changed
+820
-126
No files matched your search
@@ -0,0 +1,124 @@
|
||||
// This file is part of https://github.com/racingmars/go3270/
|
||||
// Copyright 2025 by Matthew R. Wilson, licensed under the MIT license. See
|
||||
// LICENSE in the project root for license information.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/racingmars/go3270"
|
||||
)
|
||||
|
||||
var biglayout = go3270.Screen{
|
||||
// Column will be calculated at runtime for the following field:
|
||||
{Row: 0, Intense: true, Content: "3270 Screen Size Example"},
|
||||
|
||||
{Row: 2, Col: 0,
|
||||
Content: "This screen is using the full size that your terminal supports."},
|
||||
|
||||
{Row: 4, Col: 0, Content: "Terminal Type . . ."},
|
||||
{Row: 4, Col: 21, Name: "termtype", Intense: true},
|
||||
|
||||
{Row: 5, Col: 0, Content: "Rows . . . . . . . ."},
|
||||
{Row: 5, Col: 21, Name: "rows", Intense: true},
|
||||
|
||||
{Row: 6, Col: 0, Content: "Columns . . . . . ."},
|
||||
{Row: 6, Col: 21, Name: "cols", Intense: true},
|
||||
|
||||
{Row: 8, Col: 0, Content: "To visit a default sized screen, press"},
|
||||
{Row: 8, Col: 39, Content: "PF1", Color: go3270.Yellow, Intense: true},
|
||||
|
||||
{Row: 9, Col: 0, Content: "To exit and disconnect, press"},
|
||||
{Row: 9, Col: 30, Content: "PF3", Color: go3270.Yellow, Intense: true},
|
||||
|
||||
// a blank field for error messages
|
||||
{Row: 11, Col: 0, Intense: true, Color: go3270.Red, Name: "errormsg"},
|
||||
}
|
||||
|
||||
func bigscreen(conn net.Conn, devinfo go3270.DevInfo, data any) (
|
||||
go3270.Tx, any, error) {
|
||||
|
||||
rows, cols := devinfo.AltDimensions()
|
||||
termtype := devinfo.TerminalType()
|
||||
|
||||
// Make a local copy of the screen definition that we can append lines to.
|
||||
screen := make(go3270.Screen, len(biglayout))
|
||||
copy(screen, biglayout)
|
||||
|
||||
// Center the title on any screen width
|
||||
screen[0].Col = (cols / 2) - (len(biglayout[0].Content) / 2)
|
||||
|
||||
// We'll start writing "data lines" at row 13 up to the penultimate row on
|
||||
// the terminal
|
||||
for i := 13; i < rows-1; i++ {
|
||||
newfield := go3270.Field{Row: i, Col: 0,
|
||||
Content: fmt.Sprintf("This is data row %d.", i-12)}
|
||||
if i == rows-2 {
|
||||
newfield.Content += " (The last.)"
|
||||
}
|
||||
screen = append(screen, newfield)
|
||||
|
||||
// And demonstrate that we can position to the full width, too.
|
||||
newfield = go3270.Field{Row: i, Col: cols - 5, Content: "<**>"}
|
||||
screen = append(screen, newfield)
|
||||
}
|
||||
|
||||
// And an input field on the last row, to make sure field buffer address
|
||||
// decoding works on larger screens.
|
||||
newfield := go3270.Field{Row: rows - 1, Col: 0,
|
||||
Content: "Enter data here:", Color: go3270.Pink, Intense: true}
|
||||
screen = append(screen, newfield)
|
||||
newfield = go3270.Field{Row: rows - 1, Col: 17, Name: "inputdata",
|
||||
Write: true}
|
||||
screen = append(screen, newfield)
|
||||
newfield = go3270.Field{Row: rows - 1, Col: cols - 1} // "stop" field
|
||||
screen = append(screen, newfield)
|
||||
|
||||
fieldValues := map[string]string{
|
||||
"termtype": termtype,
|
||||
"rows": strconv.Itoa(rows),
|
||||
"cols": strconv.Itoa(cols),
|
||||
}
|
||||
|
||||
if data != nil {
|
||||
fieldValues["errormsg"] = fmt.Sprintf("You said: %s", data.(string))
|
||||
}
|
||||
|
||||
resp, err := go3270.HandleScreenAlt(
|
||||
screen, // the screen to display
|
||||
nil, // (no) rules to enforce
|
||||
fieldValues, // pre-populated values in fields
|
||||
[]go3270.AID{ // keys we accept -- validating
|
||||
go3270.AIDEnter,
|
||||
},
|
||||
[]go3270.AID{ // keys we accept -- non-validating
|
||||
go3270.AIDPF1,
|
||||
go3270.AIDPF3,
|
||||
},
|
||||
"errormsg", // name of field to put error messages in
|
||||
rows-1, 18, // cursor coordinates
|
||||
conn, // network connection
|
||||
devinfo, // device info for alternate screen size support
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
switch resp.AID {
|
||||
case go3270.AIDEnter:
|
||||
// Re-run current transaction, echoing back input
|
||||
return bigscreen, resp.Values["inputdata"], err
|
||||
case go3270.AIDPF1:
|
||||
// Go to default screen size transaction
|
||||
return normalscreen, nil, nil
|
||||
case go3270.AIDPF3:
|
||||
// Exit
|
||||
return nil, nil, nil
|
||||
default:
|
||||
// re-run current transaction
|
||||
return bigscreen, nil, nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// This file is part of https://github.com/racingmars/go3270/
|
||||
// Copyright 2025 by Matthew R. Wilson, licensed under the MIT license. See
|
||||
// LICENSE in the project root for license information.
|
||||
|
||||
// Example 5 demonstrates support for larger-than-default alternate screen
|
||||
// sizes in terminals that are larger than 24x80.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/racingmars/go3270"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ln, err := net.Listen("tcp", ":3270")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("LISTENING ON PORT 3270 FOR CONNECTIONS")
|
||||
fmt.Println("Press Ctrl-C to end server.")
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
go handle(conn)
|
||||
}
|
||||
}
|
||||
|
||||
// handle is the handler for individual user connections.
|
||||
func handle(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
// Always begin new connection by negotiating the telnet options
|
||||
devinfo, err := go3270.NegotiateTelnet(conn)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = go3270.RunTransactions(conn, devinfo, bigscreen, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// This file is part of https://github.com/racingmars/go3270/
|
||||
// Copyright 2025 by Matthew R. Wilson, licensed under the MIT license. See
|
||||
// LICENSE in the project root for license information.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/racingmars/go3270"
|
||||
)
|
||||
|
||||
var normallayout = go3270.Screen{
|
||||
{Row: 0, Col: 28, Intense: true, Content: "3270 Screen Size Example"},
|
||||
|
||||
{Row: 2, Col: 0,
|
||||
Content: "This screen is using the default size that all terminals support, 24x80."},
|
||||
{Row: 3, Col: 0,
|
||||
Content: "But I know the following information about your particular terminal:"},
|
||||
|
||||
{Row: 4, Col: 0, Content: "Terminal Type . . ."},
|
||||
{Row: 4, Col: 21, Name: "termtype", Intense: true},
|
||||
|
||||
{Row: 5, Col: 0, Content: "Rows . . . . . . . ."},
|
||||
{Row: 5, Col: 21, Name: "rows", Intense: true},
|
||||
{Row: 5, Col: 28, Content: "(but currently using 24)"},
|
||||
|
||||
{Row: 6, Col: 0, Content: "Columns . . . . . ."},
|
||||
{Row: 6, Col: 21, Name: "cols", Intense: true},
|
||||
{Row: 6, Col: 28, Content: "(but currently using 80)"},
|
||||
|
||||
{Row: 8, Col: 0, Content: "To visit a large sized screen, press"},
|
||||
{Row: 8, Col: 37, Content: "PF1", Color: go3270.Yellow, Intense: true},
|
||||
|
||||
{Row: 9, Col: 0, Content: "To exit and disconnect, press"},
|
||||
{Row: 9, Col: 30, Content: "PF3", Color: go3270.Yellow, Intense: true},
|
||||
|
||||
// a blank field for error messages
|
||||
{Row: 11, Col: 0, Intense: true, Color: go3270.Red, Name: "errormsg"},
|
||||
}
|
||||
|
||||
func normalscreen(conn net.Conn, devinfo go3270.DevInfo, data any) (
|
||||
go3270.Tx, any, error) {
|
||||
|
||||
rows, cols := devinfo.AltDimensions()
|
||||
termtype := devinfo.TerminalType()
|
||||
|
||||
// Make a local copy of the screen definition that we can append lines to.
|
||||
screen := make(go3270.Screen, len(normallayout))
|
||||
copy(screen, normallayout)
|
||||
|
||||
// We'll start writing "data lines" at row 13 up to 24
|
||||
for i := 13; i < 24; i++ {
|
||||
newfield := go3270.Field{Row: i, Col: 0,
|
||||
Content: fmt.Sprintf("This is data row %d.", i-12)}
|
||||
if i == 23 {
|
||||
newfield.Content += " (The last.)"
|
||||
}
|
||||
screen = append(screen, newfield)
|
||||
|
||||
newfield = go3270.Field{Row: i, Col: 80 - 5, Content: "<**>"}
|
||||
screen = append(screen, newfield)
|
||||
}
|
||||
|
||||
fieldValues := map[string]string{
|
||||
"termtype": termtype,
|
||||
"rows": strconv.Itoa(rows),
|
||||
"cols": strconv.Itoa(cols),
|
||||
}
|
||||
|
||||
// We can call the old HandleScreen(), or we could have used the new
|
||||
// HandleScreenAlt() and provided a nil DevInfo.
|
||||
resp, err := go3270.HandleScreen(
|
||||
screen, // the screen to display
|
||||
nil, // (no) rules to enforce
|
||||
fieldValues, // pre-populated values in fields
|
||||
nil, // keys we accept -- validating
|
||||
[]go3270.AID{ // keys we accept -- non-validating
|
||||
go3270.AIDPF1,
|
||||
go3270.AIDPF3,
|
||||
},
|
||||
"errormsg", // name of field to put error messages in
|
||||
1, 1, // cursor coordinates
|
||||
conn, // network connection
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
switch resp.AID {
|
||||
case go3270.AIDPF1:
|
||||
// Go to big screen size transaction
|
||||
return bigscreen, nil, nil
|
||||
case go3270.AIDPF3:
|
||||
// Exit
|
||||
return nil, nil, nil
|
||||
default:
|
||||
// re-run current transaction
|
||||
return normalscreen, nil, nil
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user