feat: implement flashing support (0x08/0x09) with 1Hz timer
Some checks failed
Build Windows / Build Windows (push) Has been cancelled
Build Linux / Build Linux (push) Has been cancelled

This commit is contained in:
2026-02-21 12:49:20 +01:00
parent 6d88b59e04
commit 38884c8f33

View File

@@ -63,6 +63,12 @@ class TeletextCanvas(QWidget):
self.service = None # Reference to TeletextService for DRCS/Shared data
self.subset_idx = 0 # Default English
# Flashing state
self.flash_on = True
self.flash_timer = QTimer(self)
self.flash_timer.timeout.connect(self.toggle_flash)
self.flash_timer.start(500) # 500ms toggle (1Hz flash rate)
# Teletext is 40 columns x 25 rows
# We will render to a fixed size QImage and scale it
self.cols = 40
@@ -87,6 +93,12 @@ class TeletextCanvas(QWidget):
self.cursor_is_graphics = False # Tracked during draw
# Blinking cursor timer could be added, for now static inverted is fine or toggle on timer elsewhere
def toggle_flash(self):
self.flash_on = not self.flash_on
if self.page:
self.redraw()
self.update()
def get_byte_at(self, x, y):
if not self.page: return 0
@@ -274,6 +286,7 @@ class TeletextCanvas(QWidget):
hold_graphics = False
held_char = 0x20 # Space
double_height = False
flashing = False
last_visible_idx = -1
bg_segments = [(0, bg)] # Track BG changes: (index, color)
@@ -364,6 +377,10 @@ class TeletextCanvas(QWidget):
break
painter.fillRect(k * self.cell_w, y, self.cell_w, self.cell_h * 2, cell_bg)
elif byte_val == 0x08: # Start Flashing
flashing = True
elif byte_val == 0x09: # Steady
flashing = False
elif byte_val == 0x19: # Contiguous Graphics
contiguous = True
elif byte_val == 0x1A: # Separated Graphics
@@ -410,45 +427,60 @@ class TeletextCanvas(QWidget):
# Draw Foreground
if draw_fg:
# Calculate height
# For Mosaics, we use the height param.
# For Alphanumerics, we scale the painter.
if is_control:
# "Set-at" spacing attribute? Teletext control codes occupy a space
# unless "Hold Graphics" replaces it with previous graphic char.
if hold_graphics and graphics_mode:
if double_height:
self.draw_mosaic(painter, x, y, held_char, fg, contiguous, height=self.cell_h * 2)
else:
self.draw_mosaic(painter, x, y, held_char, fg, contiguous)
else:
# Draw space (nothing, since we filled BG)
pass
else:
# Check for DRCS
drcs_char = None
if self.page and hasattr(self, 'parent') and hasattr(self.parent(), 'service'):
# Try to find drcs in service
service = self.parent().service
# For now, assume Set 0 if we have drcs_data for this code
drcs_char = service.drcs_data.get((0, byte_val))
# If flashing is active and flash is in 'off' state, skip drawing FG
if not (flashing and not self.flash_on):
# Calculate height
# For Mosaics, we use the height param.
# For Alphanumerics, we scale the painter.
# Alternatively, if we just want it to work in the test where we might not have parent()
# We can store a reference to the service in the canvas
if not drcs_char and hasattr(self, 'service') and self.service:
drcs_char = self.service.drcs_data.get((0, byte_val))
if drcs_char:
self.draw_drcs(painter, x, y, drcs_char, fg, double_height)
elif graphics_mode:
# Mosaic Graphics
h_mos = self.cell_h * 2 if double_height else self.cell_h
if (0x20 <= byte_val <= 0x3F) or (0x60 <= byte_val <= 0x7F):
self.draw_mosaic(painter, x, y, byte_val, fg, contiguous, height=h_mos)
held_char = byte_val
if is_control:
# "Set-at" spacing attribute? Teletext control codes occupy a space
# unless "Hold Graphics" replaces it with previous graphic char.
if hold_graphics and graphics_mode:
if double_height:
self.draw_mosaic(painter, x, y, held_char, fg, contiguous, height=self.cell_h * 2)
else:
self.draw_mosaic(painter, x, y, held_char, fg, contiguous)
else:
# Capital letter in graphics mode? Usually shows char?
# Draw space (nothing, since we filled BG)
pass
else:
# Check for DRCS
drcs_char = None
if self.page and hasattr(self, 'parent') and hasattr(self.parent(), 'service'):
# Try to find drcs in service
service = self.parent().service
# For now, assume Set 0 if we have drcs_data for this code
drcs_char = service.drcs_data.get((0, byte_val))
# Alternatively, if we just want it to work in the test where we might not have parent()
# We can store a reference to the service in the canvas
if not drcs_char and hasattr(self, 'service') and self.service:
drcs_char = self.service.drcs_data.get((0, byte_val))
if drcs_char:
self.draw_drcs(painter, x, y, drcs_char, fg, double_height)
elif graphics_mode:
# Mosaic Graphics
h_mos = self.cell_h * 2 if double_height else self.cell_h
if (0x20 <= byte_val <= 0x3F) or (0x60 <= byte_val <= 0x7F):
self.draw_mosaic(painter, x, y, byte_val, fg, contiguous, height=h_mos)
held_char = byte_val
else:
# Capital letter in graphics mode? Usually shows char?
char = get_char(byte_val, self.subset_idx)
painter.setPen(fg)
if double_height:
painter.save()
painter.translate(x, y)
painter.scale(1, 2)
painter.drawText(QRect(0, 0, self.cell_w, self.cell_h), Qt.AlignmentFlag.AlignCenter, char)
painter.restore()
else:
painter.drawText(QRect(x, y, self.cell_w, self.cell_h), Qt.AlignmentFlag.AlignCenter, char)
held_char = 0x20
else:
# Alphanumeric
char = get_char(byte_val, self.subset_idx)
painter.setPen(fg)
if double_height:
@@ -459,19 +491,6 @@ class TeletextCanvas(QWidget):
painter.restore()
else:
painter.drawText(QRect(x, y, self.cell_w, self.cell_h), Qt.AlignmentFlag.AlignCenter, char)
held_char = 0x20
else:
# Alphanumeric
char = get_char(byte_val, self.subset_idx)
painter.setPen(fg)
if double_height:
painter.save()
painter.translate(x, y)
painter.scale(1, 2)
painter.drawText(QRect(0, 0, self.cell_w, self.cell_h), Qt.AlignmentFlag.AlignCenter, char)
painter.restore()
else:
painter.drawText(QRect(x, y, self.cell_w, self.cell_h), Qt.AlignmentFlag.AlignCenter, char)
# Draw Cursor
# Invert the cell at cursor position
if draw_fg and self.cursor_visible and c == self.cursor_x and row == self.cursor_y: