ibm5250 0.1.0.dev2__tar.gz → 0.1.0.dev4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ibm5250
3
- Version: 0.1.0.dev2
3
+ Version: 0.1.0.dev4
4
4
  Summary: IBM 5250 terminal automation library
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.11
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ibm5250"
3
- version = "0.1.0.dev2"
3
+ version = "0.1.0.dev4"
4
4
  description = "IBM 5250 terminal automation library"
5
5
  requires-python = ">=3.11"
6
6
 
@@ -197,6 +197,10 @@ class TN5250Connection:
197
197
  self._ttype_will_sent = False
198
198
  self._negotiated_ttype = "IBM-3477-FG" # screen model reported to IBM i
199
199
 
200
+ # Byte 9 (opcode/var-header length) of the last received TN5250E record.
201
+ # Used to detect header-only Cancel-Invite records (opcode 0x0A).
202
+ self._last_recv_opcode: int = 0x03
203
+
200
204
  # TLS
201
205
  if use_tls and tls_context is None:
202
206
  tls_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
@@ -342,6 +346,7 @@ class TN5250Connection:
342
346
  log.debug("Short TN5250E frame (%d bytes), skipping", len(frame))
343
347
  continue
344
348
  hdr = TN5250EHeader.from_bytes(frame)
349
+ self._last_recv_opcode = frame[9] # byte 9: opcode / var_len
345
350
  payload = bytes(frame[_TN5250E_HEADER_LEN:])
346
351
  log.debug(
347
352
  "← TN5250E type=%02X data_type=%02X payload=%d bytes",
@@ -361,6 +366,7 @@ class TN5250Connection:
361
366
  frame_bytes[:2], "big"
362
367
  ) == len(frame_bytes):
363
368
  hdr = TN5250EHeader.from_bytes(frame_bytes)
369
+ self._last_recv_opcode = frame_bytes[9] # byte 9: opcode
364
370
  var_hdr_len = frame_bytes[
365
371
  9
366
372
  ] # RFC 2877 variable-header length field
@@ -139,7 +139,7 @@ class Order:
139
139
  ERASE_TO_ADDRESS = (
140
140
  0x12 # Erase to Address — 2 address bytes (fill with nulls)
141
141
  )
142
- INSERT_CURSOR = 0x13 # Insert Cursor — no operands; marks cursor pos
142
+ INSERT_CURSOR = 0x13 # Insert Cursor — 2 address bytes; marks cursor pos
143
143
  MOVE_CURSOR = 0x14 # Move Cursor — 2 address bytes
144
144
  GROUP_ERASE_TO_ADDRESS = 0x16 # Group Erase to Address — 2 address bytes (fill with nulls, within field group)
145
145
  START_FIELD = 0x1D # Start Field — 2-byte FFW [+ 2-byte FCW pairs]
@@ -78,7 +78,7 @@ After the command + CC bytes, the rest of the record is a stream of **orders** i
78
78
  | **SBA** — Set Buffer Address (`0x11`) + row + col | Move the write position to a specific screen cell |
79
79
  | **SF** — Start Field (`0x1D`) + FFW [+ FCWs] | Define a new input/output field at the current position. The FFW (Field Format Word, 2 bytes) defines field type — bypass/input, numeric, non-display, etc. Optional FCW (Field Control Word) pairs add behaviours like mandatory entry, right-adjust, or monocase. |
80
80
  | **MF** — Modify Field (`0x2C`) + FFW [+ FCWs] | Modify an existing field's attributes |
81
- | **IC** — Insert Cursor (`0x13`) | Place the cursor at the current position |
81
+ | **IC** — Insert Cursor (`0x13`) | Place the cursor at the 2-byte address that follows (falls back to the current position only when no address bytes remain) |
82
82
  | **MC** — Move Cursor (`0x14`) + row + col | Move the cursor to a specific position |
83
83
  | **RA** — Repeat to Address (`0x3C`) + row + col + char | Repeat a character from current position up to the target |
84
84
  | **EA** — Erase to Address (`0x12`) + row + col | Erase (fill with nulls) from current position to target |
@@ -369,9 +369,26 @@ class DataStreamParser:
369
369
 
370
370
  case Order.INSERT_CURSOR:
371
371
  byte_offset += 1
372
- yield DSEvent(
373
- type=DSEventType.SET_CURSOR, position=current_buffer_position
374
- )
372
+ # 5250 IC carries a 2-byte cursor address. Only 3270-style
373
+ # operandless IC (synthetic/edge streams) omits it, which we
374
+ # detect by there being fewer than 2 bytes left.
375
+ if byte_offset + 2 > total_bytes:
376
+ yield DSEvent(
377
+ type=DSEventType.SET_CURSOR,
378
+ position=current_buffer_position,
379
+ )
380
+ else:
381
+ address_high, address_low = (
382
+ data_view[byte_offset],
383
+ data_view[byte_offset + 1],
384
+ )
385
+ byte_offset += 2
386
+ row, col = max(1, address_high) - 1, max(1, address_low) - 1
387
+ yield from self._maybe_resize(row, col)
388
+ yield DSEvent(
389
+ type=DSEventType.SET_CURSOR,
390
+ position=row * self.cols + col,
391
+ )
375
392
 
376
393
  case Order.MOVE_CURSOR:
377
394
  byte_offset += 1
@@ -13,7 +13,7 @@ wheels = [
13
13
 
14
14
  [[package]]
15
15
  name = "ibm5250"
16
- version = "0.1.0.dev2"
16
+ version = "0.1.0.dev4"
17
17
  source = { editable = "." }
18
18
 
19
19
  [package.dev-dependencies]
File without changes
File without changes
File without changes