|
27 | 27 |
|
28 | 28 | #~autogen |
29 | 29 |
|
| 30 | +import os |
30 | 31 | import os.path |
31 | 32 | import fnmatch |
32 | 33 | import numbers |
33 | 34 | import platform |
34 | 35 | import fcntl |
35 | 36 | import array |
| 37 | +import mmap |
| 38 | +import ctypes |
| 39 | +from PIL import Image, ImageDraw |
36 | 40 |
|
37 | 41 | #------------------------------------------------------------------------------ |
38 | 42 | # Guess platform we are running on |
@@ -1987,3 +1991,214 @@ def status(self): |
1987 | 1991 |
|
1988 | 1992 | #~autogen |
1989 | 1993 |
|
| 1994 | +class FbMem(object): |
| 1995 | + |
| 1996 | + """The framebuffer memory object. |
| 1997 | +
|
| 1998 | + Made of: |
| 1999 | + - the framebuffer file descriptor |
| 2000 | + - the fix screen info struct |
| 2001 | + - the var screen info struct |
| 2002 | + - the mapped memory |
| 2003 | + """ |
| 2004 | + |
| 2005 | + #------------------------------------------------------------------- |
| 2006 | + # The code is adapted from |
| 2007 | + # https://github.com/LinkCareServices/cairotft/blob/master/cairotft/linuxfb.py |
| 2008 | + # |
| 2009 | + # The original code came with the following license: |
| 2010 | + #------------------------------------------------------------------- |
| 2011 | + # Copyright (c) 2012 Kurichan |
| 2012 | + # |
| 2013 | + # This program is free software. It comes without any warranty, to |
| 2014 | + # the extent permitted by applicable law. You can redistribute it |
| 2015 | + # and/or modify it under the terms of the Do What The Fuck You Want |
| 2016 | + # To Public License, Version 2, as published by Sam Hocevar. See |
| 2017 | + # http://sam.zoy.org/wtfpl/COPYING for more details. |
| 2018 | + #------------------------------------------------------------------- |
| 2019 | + |
| 2020 | + |
| 2021 | + __slots__ = ('fid', 'fix_info', 'var_info', 'mmap') |
| 2022 | + |
| 2023 | + FBIOGET_VSCREENINFO = 0x4600 |
| 2024 | + FBIOGET_FSCREENINFO = 0x4602 |
| 2025 | + |
| 2026 | + class FixScreenInfo(ctypes.Structure): |
| 2027 | + |
| 2028 | + """The fb_fix_screeninfo from fb.h.""" |
| 2029 | + |
| 2030 | + _fields_ = [ |
| 2031 | + ('id_name', ctypes.c_char * 16), |
| 2032 | + ('smem_start', ctypes.c_ulong), |
| 2033 | + ('smem_len', ctypes.c_uint32), |
| 2034 | + ('type', ctypes.c_uint32), |
| 2035 | + ('type_aux', ctypes.c_uint32), |
| 2036 | + ('visual', ctypes.c_uint32), |
| 2037 | + ('xpanstep', ctypes.c_uint16), |
| 2038 | + ('ypanstep', ctypes.c_uint16), |
| 2039 | + ('ywrapstep', ctypes.c_uint16), |
| 2040 | + ('line_length', ctypes.c_uint32), |
| 2041 | + ('mmio_start', ctypes.c_ulong), |
| 2042 | + ('mmio_len', ctypes.c_uint32), |
| 2043 | + ('accel', ctypes.c_uint32), |
| 2044 | + ('reserved', ctypes.c_uint16 * 3), |
| 2045 | + ] |
| 2046 | + |
| 2047 | + |
| 2048 | + class VarScreenInfo(ctypes.Structure): |
| 2049 | + |
| 2050 | + class FbBitField(ctypes.Structure): |
| 2051 | + |
| 2052 | + """The fb_bitfield struct from fb.h.""" |
| 2053 | + |
| 2054 | + _fields_ = [ |
| 2055 | + ('offset', ctypes.c_uint32), |
| 2056 | + ('length', ctypes.c_uint32), |
| 2057 | + ('msb_right', ctypes.c_uint32), |
| 2058 | + ] |
| 2059 | + |
| 2060 | + |
| 2061 | + """The fb_var_screeninfo struct from fb.h.""" |
| 2062 | + |
| 2063 | + _fields_ = [ |
| 2064 | + ('xres', ctypes.c_uint32), |
| 2065 | + ('yres', ctypes.c_uint32), |
| 2066 | + ('xres_virtual', ctypes.c_uint32), |
| 2067 | + ('yres_virtual', ctypes.c_uint32), |
| 2068 | + ('xoffset', ctypes.c_uint32), |
| 2069 | + ('yoffset', ctypes.c_uint32), |
| 2070 | + |
| 2071 | + ('bits_per_pixel', ctypes.c_uint32), |
| 2072 | + ('grayscale', ctypes.c_uint32), |
| 2073 | + |
| 2074 | + ('red', FbBitField), |
| 2075 | + ('green', FbBitField), |
| 2076 | + ('blue', FbBitField), |
| 2077 | + ('transp', FbBitField), |
| 2078 | + ] |
| 2079 | + |
| 2080 | + |
| 2081 | + def __init__(self, fbdev=None): |
| 2082 | + """Create the FbMem framebuffer memory object.""" |
| 2083 | + fid = FbMem._open_fbdev(fbdev) |
| 2084 | + fix_info = FbMem._get_fix_info(fid) |
| 2085 | + fbmmap = FbMem._map_fb_memory(fid, fix_info) |
| 2086 | + self.fid = fid |
| 2087 | + self.fix_info = fix_info |
| 2088 | + self.var_info = FbMem._get_var_info(fid) |
| 2089 | + self.mmap = fbmmap |
| 2090 | + |
| 2091 | + |
| 2092 | + def __del__(self): |
| 2093 | + """Close the FbMem framebuffer memory object.""" |
| 2094 | + self.mmap.close() |
| 2095 | + FbMem._close_fbdev(self.fid) |
| 2096 | + |
| 2097 | + |
| 2098 | + @staticmethod |
| 2099 | + def _open_fbdev(fbdev=None): |
| 2100 | + """Return the framebuffer file descriptor. |
| 2101 | +
|
| 2102 | + Try to use the FRAMEBUFFER |
| 2103 | + environment variable if fbdev is not given. Use '/dev/fb0' by |
| 2104 | + default. |
| 2105 | + """ |
| 2106 | + dev = fbdev or os.getenv('FRAMEBUFFER', '/dev/fb0') |
| 2107 | + fbfid = os.open(dev, os.O_RDWR) |
| 2108 | + return fbfid |
| 2109 | + |
| 2110 | + |
| 2111 | + @staticmethod |
| 2112 | + def _close_fbdev(fbfid): |
| 2113 | + """Close the framebuffer file descriptor.""" |
| 2114 | + os.close(fbfid) |
| 2115 | + |
| 2116 | + |
| 2117 | + @staticmethod |
| 2118 | + def _get_fix_info(fbfid): |
| 2119 | + """Return the fix screen info from the framebuffer file descriptor.""" |
| 2120 | + fix_info = FbMem.FixScreenInfo() |
| 2121 | + fcntl.ioctl(fbfid, FbMem.FBIOGET_FSCREENINFO, fix_info) |
| 2122 | + return fix_info |
| 2123 | + |
| 2124 | + |
| 2125 | + @staticmethod |
| 2126 | + def _get_var_info(fbfid): |
| 2127 | + """Return the var screen info from the framebuffer file descriptor.""" |
| 2128 | + var_info = FbMem.VarScreenInfo() |
| 2129 | + fcntl.ioctl(fbfid, FbMem.FBIOGET_VSCREENINFO, var_info) |
| 2130 | + return var_info |
| 2131 | + |
| 2132 | + |
| 2133 | + @staticmethod |
| 2134 | + def _map_fb_memory(fbfid, fix_info): |
| 2135 | + """Map the framebuffer memory.""" |
| 2136 | + return mmap.mmap( |
| 2137 | + fbfid, |
| 2138 | + fix_info.smem_len, |
| 2139 | + mmap.MAP_SHARED, |
| 2140 | + mmap.PROT_READ | mmap.PROT_WRITE, |
| 2141 | + offset=0 |
| 2142 | + ) |
| 2143 | + |
| 2144 | +class LCD(FbMem): |
| 2145 | + """ |
| 2146 | + A convenience wrapper for the FbMem class. |
| 2147 | + Provides drawing functions from the python imaging library (PIL). |
| 2148 | + """ |
| 2149 | + |
| 2150 | + def __init__(self): |
| 2151 | + FbMem.__init__(self) |
| 2152 | + |
| 2153 | + def alignup(n, m): |
| 2154 | + r = n % m |
| 2155 | + if r == 0: return n |
| 2156 | + return n - r + m |
| 2157 | + |
| 2158 | + self._img = Image.new("1", (alignup(self.xres, 32), self.yres), "white") |
| 2159 | + |
| 2160 | + self.draw = ImageDraw.Draw(self._img) |
| 2161 | + |
| 2162 | + @property |
| 2163 | + def xres(self): |
| 2164 | + """ |
| 2165 | + Horizontal LCD screen resolution |
| 2166 | + """ |
| 2167 | + return self.var_info.xres |
| 2168 | + |
| 2169 | + @property |
| 2170 | + def yres(self): |
| 2171 | + """ |
| 2172 | + Vertical LCD screen resolution |
| 2173 | + """ |
| 2174 | + return self.var_info.yres |
| 2175 | + |
| 2176 | + @property |
| 2177 | + def shape(self): |
| 2178 | + """ |
| 2179 | + Dimensions of the LCD screen. |
| 2180 | + """ |
| 2181 | + return (self.xres, self.yres) |
| 2182 | + |
| 2183 | + @property |
| 2184 | + def draw(self): |
| 2185 | + """ |
| 2186 | + Returns a handle to PIL.ImageDraw.Draw class associated with LCD. |
| 2187 | + Example:: |
| 2188 | + lcd.draw.rectangle((10,10,60,20), fill='black') |
| 2189 | + """ |
| 2190 | + return self.draw |
| 2191 | + |
| 2192 | + def clear(self): |
| 2193 | + """ |
| 2194 | + Clears the LCD screen |
| 2195 | + """ |
| 2196 | + self.draw.rectangle(((0,0), self.shape), fill="white") |
| 2197 | + |
| 2198 | + def update(self): |
| 2199 | + """ |
| 2200 | + Applies pending changes to the LCD. |
| 2201 | + Nothing will be drawn on the screen until this function is called. |
| 2202 | + """ |
| 2203 | + self.mmap[:] = self._img.tobytes("raw", "1;IR") |
| 2204 | + |
0 commit comments