import time
-import circuitpython_typing
+import struct
+from circuitpython_typing import WriteableBuffer, ReadableBuffer
from paralleldisplay import ParallelBus
from ._fourwire import FourWire
from ._group import Group
from ._i2cdisplay import I2CDisplay
-from ._structs import ColorspaceStruct, TransformStruct, RectangleStruct
+from ._structs import ColorspaceStruct, TransformStruct
from ._area import Area
from ._displaybus import _DisplayBus
+from ._helpers import bswap16
+from ._constants import (
+ CHIP_SELECT_UNTOUCHED,
+ CHIP_SELECT_TOGGLE_EVERY_BYTE,
+ DISPLAY_COMMAND,
+ DISPLAY_DATA,
+ NO_COMMAND,
+)
class _DisplayCore:
- # pylint: disable=too-many-arguments, too-many-instance-attributes, too-many-locals
+ # pylint: disable=too-many-arguments, too-many-instance-attributes, too-many-locals, too-many-branches, too-many-statements
def __init__(
self,
if bus:
if isinstance(bus, (FourWire, I2CDisplay, ParallelBus)):
self._bus_reset = bus.reset
+ self._bus_free = bus._free
self._begin_transaction = bus._begin_transaction
self._send = bus._send
self._end_transaction = bus._end_transaction
self.rotation = rotation
self.transform = TransformStruct()
+ self.set_rotation(rotation)
+
def set_rotation(self, rotation: int) -> None:
"""
Sets the rotation of the display as an int in degrees.
"""
# pylint: disable=protected-access, too-many-branches
- transposed = self.rotation in (90, 270)
- will_be_transposed = rotation in (90, 270)
- if transposed != will_be_transposed:
- self.width, self.height = self.height, self.width
-
height = self.height
width = self.width
self.transform.y = height
self.transform.dy = -1
- if self.current_group is not None:
- self.current_group._update_transform(self.transform)
-
- def show(self, root_group: Group) -> bool:
- # pylint: disable=protected-access
-
+ def set_root_group(self, root_group: Group) -> bool:
"""
Switches to displaying the given group of layers. When group is `None`, the
default CircuitPython terminal will be shown.
:param Optional[displayio.Group] root_group: The group to show.
"""
-
- """
- # TODO: Implement Supervisor
- if root_group is None:
- circuitpython_splash = _Supervisor().circuitpython_splash
- if not circuitpython_splash._in_group:
- root_group = circuitpython_splash
- elif self.current_group == circuitpython_splash:
- return True
- """
+ # pylint: disable=protected-access
if root_group == self.current_group:
return True
self.refresh_in_progress = False
self.last_refresh = time.monotonic() * 1000
- def get_refresh_areas(self) -> list:
- """Get a list of areas to be refreshed"""
- subrectangles = []
- if self.current_group is not None:
- # Eventually calculate dirty rectangles here
- subrectangles.append(RectangleStruct(0, 0, self.width, self.height))
- return subrectangles
-
- def release(self) -> None:
+ def release_display_core(self) -> None:
"""Release the display from the current group"""
# pylint: disable=protected-access
def fill_area(
self,
area: Area,
- mask: circuitpython_typing.WriteableBuffer,
- buffer: circuitpython_typing.WriteableBuffer,
+ mask: WriteableBuffer,
+ buffer: WriteableBuffer,
) -> bool:
- # pylint: disable=protected-access
"""Call the current group's fill area function"""
-
- return self.current_group._fill_area(self.colorspace, area, mask, buffer)
+ if self.current_group is not None:
+ return self.current_group._fill_area( # pylint: disable=protected-access
+ self.colorspace, area, mask, buffer
+ )
+ return False
def clip_area(self, area: Area, clipped: Area) -> bool:
"""Shrink the area to the region shared by the two areas"""
- # pylint: disable=protected-access
- overlaps = self.area._compute_overlap(area, clipped)
+ overlaps = self.area.compute_overlap(area, clipped)
if not overlaps:
return False
- # Expand the area if we have multiple pixels per byte and we need to byte align the bounds
+ # Expand the area if we have multiple pixels per byte and we need to byte
+ # align the bounds
if self.colorspace.depth < 8:
pixels_per_byte = (
8 // self.colorspace.depth * self.colorspace.bytes_per_cell
return True
+ def set_region_to_update(self, area: Area) -> None:
+ """Set the region to update"""
+ region_x1 = area.x1 + self.colstart
+ region_x2 = area.x2 + self.colstart
+ region_y1 = area.y1 + self.rowstart
+ region_y2 = area.y2 + self.rowstart
+
+ if self.colorspace.depth < 8:
+ pixels_per_byte = 8 // self.colorspace.depth
+ if self.colorspace.pixels_in_byte_share_row:
+ region_x1 //= pixels_per_byte * self.colorspace.bytes_per_cell
+ region_x2 //= pixels_per_byte * self.colorspace.bytes_per_cell
+ else:
+ region_y1 //= pixels_per_byte * self.colorspace.bytes_per_cell
+ region_y2 //= pixels_per_byte * self.colorspace.bytes_per_cell
+ print(region_y2)
+ region_x2 -= 1
+ region_y2 -= 1
+
+ chip_select = CHIP_SELECT_UNTOUCHED
+ if self.always_toggle_chip_select or self.data_as_commands:
+ chip_select = CHIP_SELECT_TOGGLE_EVERY_BYTE
+
+ # Set column
+ self.begin_transaction()
+ data = bytearray([self.column_command])
+ data_type = DISPLAY_DATA
+ if not self.data_as_commands:
+ self.send(DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, data)
+ data = bytearray(0)
+ else:
+ data_type = DISPLAY_COMMAND
+
+ if self.ram_width < 0x100: # Single Byte Bounds
+ data += struct.pack(">BB", region_x1, region_x2)
+ else:
+ if self.address_little_endian:
+ region_x1 = bswap16(region_x1)
+ region_x2 = bswap16(region_x2)
+ data += struct.pack(">HH", region_x1, region_x2)
+
+ # Quirk for SH1107 "SH1107_addressing"
+ # Column lower command = 0x00, Column upper command = 0x10
+ if self.sh1107_addressing:
+ data = struct.pack(
+ ">BB",
+ ((region_x1 >> 4) & 0xF0) | 0x10, # 0x10 to 0x17
+ region_x1 & 0x0F, # 0x00 to 0x0F
+ )
+
+ self.send(data_type, chip_select, data)
+ self.end_transaction()
+
+ if self.set_current_column_command != NO_COMMAND:
+ self.begin_transaction()
+ self.send(
+ DISPLAY_COMMAND, chip_select, bytes([self.set_current_column_command])
+ )
+ # Only send the first half of data because it is the first coordinate.
+ self.send(DISPLAY_DATA, chip_select, data[: len(data) // 2])
+ self.end_transaction()
+
+ # Set row
+ self.begin_transaction()
+ data = bytearray([self.row_command])
+
+ if not self.data_as_commands:
+ self.send(DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, data)
+ data = bytearray(0)
+ if self.ram_height < 0x100: # Single Byte Bounds
+ data += struct.pack(">BB", region_y1, region_y2)
+ else:
+ if self.address_little_endian:
+ region_y1 = bswap16(region_y1)
+ region_y2 = bswap16(region_y2)
+ data += struct.pack(">HH", region_y1, region_y2)
+
+ # Quirk for SH1107 "SH1107_addressing"
+ # Page address command = 0xB0
+ if self.sh1107_addressing:
+ data = struct.pack(">B", 0xB0 | region_y1)
+
+ self.send(data_type, chip_select, data)
+ self.end_transaction()
+
+ if self.set_current_row_command != NO_COMMAND:
+ self.begin_transaction()
+ self.send(
+ DISPLAY_COMMAND, chip_select, bytes([self.set_current_row_command])
+ )
+ # Only send the first half of data because it is the first coordinate.
+ self.send(DISPLAY_DATA, chip_select, data[: len(data) // 2])
+ self.end_transaction()
+
def send(
self,
data_type: int,
chip_select: int,
- data: circuitpython_typing.ReadableBuffer,
+ data: ReadableBuffer,
) -> None:
"""
Send the data to the current bus
"""
self._send(data_type, chip_select, data)
- def begin_transaction(self) -> None:
+ def bus_free(self) -> bool:
+ """
+ Check if the bus is free
+ """
+ return self._bus_free()
+
+ def begin_transaction(self) -> bool:
"""
Begin Bus Transaction
"""
- self._begin_transaction()
+ return self._begin_transaction()
def end_transaction(self) -> None:
"""