|
6 | 6 | loaded by adafruit_bitmap_font |
7 | 7 | """ |
8 | 8 | import array |
| 9 | +import atexit |
| 10 | +import json |
9 | 11 |
|
10 | 12 | import displayio |
11 | | -import pathlib |
| 13 | + |
12 | 14 | import supervisor |
13 | 15 | import sys |
14 | 16 | import usb |
| 17 | +import adafruit_pathlib as pathlib |
15 | 18 | from adafruit_bitmap_font import bitmap_font |
16 | 19 | from adafruit_display_text.text_box import TextBox |
17 | 20 | from adafruit_display_text.bitmap_label import Label |
|
22 | 25 | from adafruit_anchored_group import AnchoredGroup |
23 | 26 |
|
24 | 27 | display = supervisor.runtime.display |
| 28 | + |
| 29 | +scale = 1 |
| 30 | +if display.width > 360: |
| 31 | + scale = 2 |
| 32 | + |
25 | 33 | font_file = "/fonts/terminal.lvfontbin" |
26 | 34 | font = bitmap_font.load_font(font_file) |
27 | | -main_group = displayio.Group() |
| 35 | +main_group = displayio.Group(scale=scale) |
28 | 36 | display.root_group = main_group |
29 | 37 |
|
30 | 38 | background_bmp = displayio.Bitmap(display.width, display.height, 1) |
|
43 | 51 | mouse_tg = displayio.TileGrid(mouse_bmp, pixel_shader=mouse_bmp.pixel_shader) |
44 | 52 |
|
45 | 53 | # move it to the center of the display |
46 | | -mouse_tg.x = display.width // 2 |
47 | | -mouse_tg.y = display.height // 2 |
| 54 | +mouse_tg.x = display.width // (2 * scale) |
| 55 | +mouse_tg.y = display.height // (2 * scale) |
48 | 56 | # 046d:c52f |
49 | 57 |
|
50 | 58 |
|
|
98 | 106 | mouse = device |
99 | 107 | print(f"mouse interface: {mouse_interface_index} endpoint_address: {hex(mouse_endpoint_address)}") |
100 | 108 |
|
| 109 | +mouse_was_attached = None |
101 | 110 | if mouse is not None: |
102 | 111 | # detach the kernel driver if needed |
103 | 112 | if mouse.is_kernel_driver_active(0): |
| 113 | + mouse_was_attached = True |
104 | 114 | mouse.detach_kernel_driver(0) |
| 115 | + else: |
| 116 | + mouse_was_attached = False |
105 | 117 |
|
106 | 118 | # set configuration on the mouse so we can use it |
107 | 119 | mouse.set_configuration() |
108 | 120 |
|
109 | 121 | mouse_buf = array.array("b", [0] * 8) |
110 | | -WIDTH = 300 |
111 | | -HEIGHT = 192 |
| 122 | +WIDTH = 280 |
| 123 | +HEIGHT = 182 |
112 | 124 |
|
113 | 125 | config = { |
114 | 126 | "menu_title": "Launcher Menu", |
|
152 | 164 |
|
153 | 165 | default_icon_bmp, default_icon_palette = adafruit_imageload.load("launcher_assets/default_icon.bmp") |
154 | 166 | default_icon_palette.make_transparent(0) |
155 | | -menu_grid = GridLayout(x=10, y=26, width=WIDTH, height=HEIGHT, grid_size=(config["width"], config["height"]), |
| 167 | +menu_grid = GridLayout(x=40, y=16, width=WIDTH, height=HEIGHT, grid_size=(config["width"], config["height"]), |
156 | 168 | divider_lines=False) |
157 | 169 | main_group.append(menu_grid) |
158 | 170 |
|
159 | 171 | menu_title_txt = Label(font, text=config["menu_title"]) |
160 | 172 | menu_title_txt.anchor_point = (0.5, 0.5) |
161 | | -menu_title_txt.anchored_position = (display.width // 2, 2) |
| 173 | +menu_title_txt.anchored_position = (display.width // (2 * scale), 2) |
162 | 174 | main_group.append(menu_title_txt) |
163 | 175 |
|
164 | 176 | app_titles = [] |
165 | 177 | apps = [] |
166 | 178 | app_path = pathlib.Path("/apps") |
167 | 179 | i = 0 |
| 180 | + |
| 181 | +pages = [{}] |
| 182 | + |
| 183 | +cur_file_index = 0 |
| 184 | +cur_page = 0 |
168 | 185 | for path in app_path.iterdir(): |
169 | 186 | print(path) |
| 187 | + cell_group = AnchoredGroup() |
| 188 | + |
170 | 189 | code_file = path / "code.py" |
171 | 190 | if not code_file.exists(): |
172 | 191 | continue |
173 | | - cell_group = AnchoredGroup() |
174 | | - icon_file = path / "icon.bmp" |
| 192 | + |
| 193 | + metadata_file = path / "metadata.json" |
| 194 | + if not metadata_file.exists(): |
| 195 | + metadata_file = None |
| 196 | + metadata = None |
| 197 | + if metadata_file is not None: |
| 198 | + with open(metadata_file.absolute(), "r") as f: |
| 199 | + metadata = json.load(f) |
| 200 | + |
| 201 | + if metadata is not None and "icon" in metadata: |
| 202 | + icon_file = path / metadata["icon"] |
| 203 | + else: |
| 204 | + icon_file = path / "icon.bmp" |
| 205 | + |
175 | 206 | if not icon_file.exists(): |
176 | 207 | icon_file = None |
| 208 | + |
| 209 | + if metadata is not None and "title" in metadata: |
| 210 | + title = metadata["title"] |
| 211 | + else: |
| 212 | + title = path.name |
| 213 | + |
177 | 214 | apps.append({ |
178 | | - "title": path.name, |
179 | | - "icon": str(icon_file.absolute()), |
| 215 | + "title": title, |
| 216 | + "icon": str(icon_file.absolute()) if icon_file is not None else None, |
180 | 217 | "file": str(code_file.absolute()) |
181 | 218 | }) |
182 | 219 | if apps[-1]["icon"] is None: |
|
203 | 240 | right_palette.make_transparent(0) |
204 | 241 |
|
205 | 242 | left_tg = AnchoredTileGrid(bitmap=left_bmp, pixel_shader=left_palette) |
206 | | -left_tg.anchor_point = (0, 1.0) |
207 | | -left_tg.anchored_position = (10, display.height - 2) |
| 243 | +left_tg.anchor_point = (0, 0.5) |
| 244 | +left_tg.anchored_position = (4, (display.height // 2 // scale) - 2) |
208 | 245 |
|
209 | 246 | right_tg = AnchoredTileGrid(bitmap=right_bmp, pixel_shader=right_palette) |
210 | | -right_tg.anchor_point = (1.0, 1.0) |
211 | | -right_tg.anchored_position = (display.width - 10, display.height - 2) |
| 247 | +right_tg.anchor_point = (1.0, 0.5) |
| 248 | +right_tg.anchored_position = ((display.width // scale) - 4, (display.height // 2 // scale) - 2) |
212 | 249 |
|
213 | 250 | main_group.append(left_tg) |
214 | 251 | main_group.append(right_tg) |
|
217 | 254 | main_group.append(mouse_tg) |
218 | 255 |
|
219 | 256 | selected = 0 |
| 257 | + |
| 258 | + |
| 259 | +def atexit_callback(): |
| 260 | + """ |
| 261 | + re-attach USB devices to kernel if needed. |
| 262 | + :return: |
| 263 | + """ |
| 264 | + print("inside atexit callback") |
| 265 | + if mouse_was_attached and not mouse.is_kernel_driver_active(0): |
| 266 | + mouse.attach_kernel_driver(0) |
| 267 | + |
| 268 | +atexit.register(atexit_callback) |
| 269 | + |
| 270 | +# print(f"apps: {apps}") |
220 | 271 | while True: |
221 | 272 | index = None |
222 | 273 |
|
|
241 | 292 | # attempt to read data from the mouse |
242 | 293 | # 10ms timeout, so we don't block long if there |
243 | 294 | # is no data |
244 | | - count = mouse.read(mouse_endpoint_address, mouse_buf, timeout=10) |
| 295 | + count = mouse.read(mouse_endpoint_address, mouse_buf, timeout=20) |
245 | 296 | except usb.core.USBTimeoutError: |
246 | 297 | # skip the rest of the loop if there is no data |
247 | 298 | count = 0 |
248 | 299 |
|
249 | 300 | # update the mouse tilegrid x and y coordinates |
250 | 301 | # based on the delta values read from the mouse |
251 | 302 | if count > 0: |
252 | | - mouse_tg.x = max(0, min(display.width - 1, mouse_tg.x + mouse_buf[1])) |
253 | | - mouse_tg.y = max(0, min(display.height - 1, mouse_tg.y + mouse_buf[2])) |
| 303 | + mouse_tg.x = max(0, min((display.width // scale) - 1, mouse_tg.x + mouse_buf[1])) |
| 304 | + mouse_tg.y = max(0, min((display.height // scale) - 1, mouse_tg.y + mouse_buf[2])) |
254 | 305 |
|
255 | 306 | if mouse_buf[0] & (1 << 0) != 0: |
256 | 307 | clicked_cell = menu_grid.which_cell_contains((mouse_tg.x, mouse_tg.y)) |
257 | 308 | if clicked_cell is not None: |
258 | 309 | index = clicked_cell[1] * config["width"] + clicked_cell[0] |
259 | 310 |
|
260 | 311 | if index is not None: |
261 | | - supervisor.set_next_code_file(config["apps"][index]["file"], sticky_on_reload=True, reload_on_error=True, |
262 | | - working_directory="/apps/matrix") |
| 312 | + # print("index", index) |
| 313 | + # print(f"selected: {apps[index]}") |
| 314 | + launch_file = apps[index]["file"] |
| 315 | + supervisor.set_next_code_file(launch_file, sticky_on_reload=True, reload_on_error=True, |
| 316 | + working_directory="/".join(launch_file.split("/")[:-1])) |
263 | 317 |
|
264 | 318 | if mouse and not mouse.is_kernel_driver_active(0): |
265 | 319 | mouse.attach_kernel_driver(0) |
|
0 commit comments