From f76690ef99a55dc138c21f57fb1cf511de53d2a1 Mon Sep 17 00:00:00 2001 From: "Matthew R. Wilson" Date: Wed, 4 Nov 2020 15:33:00 -0800 Subject: [PATCH] Add support for extended field attributes --- example/example.go | 8 ++-- screen.go | 97 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 90 insertions(+), 15 deletions(-) diff --git a/example/example.go b/example/example.go index 82fcf48..9fa3cb3 100644 --- a/example/example.go +++ b/example/example.go @@ -25,10 +25,10 @@ var screen1 = go3270.Screen{ {Row: 0, Col: 27, Intense: true, Content: "3270 Example Application"}, {Row: 2, Col: 0, Content: "Welcome to the go3270 example application. Please enter your name."}, {Row: 4, Col: 0, Content: "First Name . . ."}, - {Row: 4, Col: 19, Name: "fname", Write: true}, + {Row: 4, Col: 19, Name: "fname", Write: true, Highlighting: go3270.Underscore}, {Row: 4, Col: 40}, // field "stop" character {Row: 5, Col: 0, Content: "Last Name . . . ."}, - {Row: 5, Col: 19, Name: "lname", Write: true}, + {Row: 5, Col: 19, Name: "lname", Write: true, Highlighting: go3270.Underscore}, {Row: 5, Col: 40}, // field "stop" character, {Row: 6, Col: 0, Content: "Password . . . ."}, {Row: 6, Col: 19, Name: "password", Write: true, Hidden: true}, @@ -36,7 +36,7 @@ var screen1 = go3270.Screen{ {Row: 8, Col: 0, Content: "Press"}, {Row: 8, Col: 6, Intense: true, Content: "enter"}, {Row: 8, Col: 12, Content: "to submit your name."}, - {Row: 10, Col: 0, Intense: true, Name: "errormsg"}, // a blank field for error messages + {Row: 10, Col: 0, Intense: true, Color: go3270.Red, Name: "errormsg"}, // a blank field for error messages {Row: 22, Col: 0, Content: "PF3 Exit"}, } @@ -53,6 +53,8 @@ var screen2 = go3270.Screen{ {Row: 8, Col: 12, Content: "to enter your name again, or"}, {Row: 8, Col: 41, Intense: true, Content: "PF3"}, {Row: 8, Col: 45, Content: "to quit and disconnect."}, + {Row: 11, Col: 0, Color: go3270.Turquoise, Highlighting: go3270.ReverseVideo, Content: "Here is a field with extended attributes."}, + {Row: 11, Col: 42}, // remember to "stop" fields with a regular field, to clear the reverse video for example {Row: 22, Col: 0, Content: "PF3 Exit"}, } diff --git a/screen.go b/screen.go index d690386..f9a4dfd 100644 --- a/screen.go +++ b/screen.go @@ -34,11 +34,44 @@ type Field struct { // password input field). Hidden bool + // Color is the field color. The default value is the default color. + Color Color + + // Highlighting is the highlight attribute for the field. The default value + // is the default (i.e. no) highlighting. + Highlighting Highlight + // Name is the name of this field, which is used to get the user-entered // data. All writeable fields on a screen must have a unique name. Name string } +// Color is a 3270 extended field attribute color value +type Color byte + +// The valid 3270 colors +const ( + DefaultColor Color = 0 + Blue Color = 0xf1 + Red Color = 0xf2 + Pink Color = 0xf3 + Green Color = 0xf4 + Turquoise Color = 0xf5 + Yellow Color = 0xf6 + White Color = 0xf7 +) + +// Highlight is a 3270 extended field attribute highlighting method +type Highlight byte + +// The valid 3270 highlights +const ( + DefaultHighlight Highlight = 0 + Blink Highlight = 0xf1 + ReverseVideo Highlight = 0xf2 + Underscore Highlight = 0xf4 +) + // Screen is an array of Fields which compose a complete 3270 screen. // No checking is performed for lack of overlapping fields, unique field // names, @@ -73,7 +106,7 @@ func ShowScreen(screen Screen, values map[string]string, crow, ccol int, } b.Write(sba(fld.Row, fld.Col)) - b.Write(sf(fld.Write, fld.Intense, fld.Hidden)) + b.Write(buildField(fld)) // Use fld.Content, unless the field is named and appears in the // value map. @@ -125,28 +158,68 @@ func sba(row, col int) []byte { return result } -// sf is the "start field" 3270 command -func sf(write, intense, hidden bool) []byte { - result := make([]byte, 2) - result[0] = 0x1d // SF +// buildField will return either an sf or sfe command depending for the +// field. +func buildField(f Field) []byte { + var buf bytes.Buffer + if f.Color == DefaultColor && f.Highlighting == DefaultHighlight { + // this is a traditional field, issue a normal sf command + buf.WriteByte(0x1d) // sf - "start field" + buf.WriteByte(sfAttribute(f.Write, f.Intense, f.Hidden)) + return buf.Bytes() + } + + // Otherwise, this needs an extended attribute field + buf.WriteByte(0x29) // sfe - "start field extended" + var paramCount byte = 1 // we will always have the basic field attribute + if f.Color != DefaultColor { + paramCount++ + } + if f.Highlighting != DefaultHighlight { + paramCount++ + } + buf.WriteByte(paramCount) + + // Write the basic field attribute + buf.WriteByte(0xc0) + buf.WriteByte(sfAttribute(f.Write, f.Intense, f.Hidden)) + + // Write the highlighting attribute + if f.Highlighting != DefaultHighlight { + buf.WriteByte(0x41) + buf.WriteByte(byte(f.Highlighting)) + } + + // Write the color attribute + if f.Color != DefaultColor { + buf.WriteByte(0x42) + buf.WriteByte(byte(f.Color)) + } + + return buf.Bytes() +} + +// sfAttribute builds the attribute byte for the "start field" 3270 command +func sfAttribute(write, intense, hidden bool) byte { + var attribute byte if !write { - result[1] |= 1 << 5 // set "bit 2" + attribute |= 1 << 5 // set "bit 2" } else { // The MDT bit -- we always want writable field values returned, // even if unchanged - result[1] |= 1 // set "bit 7" + attribute |= 1 // set "bit 7" } if intense { - result[1] |= 1 << 3 // set "bit 4" + attribute |= 1 << 3 // set "bit 4" } if hidden { - result[1] |= 1 << 3 // set "bit 4" - result[1] |= 1 << 2 // set "bit 5" + attribute |= 1 << 3 // set "bit 4" + attribute |= 1 << 2 // set "bit 5" } // Fill in top 2 bits with appropriate values - result[1] = codes[result[1]] - return result + attribute = codes[attribute] + return attribute } // ic is the "insert cursor" 3270 command. This function will include the