]> Repositories - hackapet/Adafruit_Blinka_Displayio.git/blobdiff - displayio/_displaycore.py
Initial eink implementation - not complete
[hackapet/Adafruit_Blinka_Displayio.git] / displayio / _displaycore.py
index ccda898e66a6c268f748921560817244307ac51b..96c47a83a2e8e7a62744abbdae0d71eb982dac51 100644 (file)
@@ -23,18 +23,27 @@ __repo__ = "https://github.com/adafruit/Adafruit_Blinka_Displayio.git"
 
 
 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,
@@ -169,25 +178,14 @@ class _DisplayCore:
         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
@@ -229,15 +227,7 @@ class _DisplayCore:
         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
 
@@ -247,23 +237,25 @@ class _DisplayCore:
     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
@@ -281,22 +273,120 @@ class _DisplayCore:
 
         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
+
+        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_width < 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
         """
+        print(len(data))
+        if isinstance(data, memoryview):
+            data = data.tobytes()
+        print(data)
         self._send(data_type, chip_select, data)
 
-    def begin_transaction(self) -> None:
+    def begin_transaction(self) -> bool:
         """
         Begin Bus Transaction
         """
-        self._begin_transaction()
+        return self._begin_transaction()
 
     def end_transaction(self) -> None:
         """