mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-08-19 07:13:49 +08:00
ui: screensaver (#1551)
* param to control stock vs sp ui
* init styles
* SP Toggles
* Lint
* optimizations
* Panels. With Icons. And Scroller.
* patience, grasshopper
* more patience, grasshopper
* sp raylib preview
* fix callback
* fix ui preview
* add ui previews
* Option Control
* Need this
* introducing ui_state_sp for py
* param to control stock vs sp ui
* better
* add ui_update callback
* better padding
* this
* listitem -> listitemsp
* Revert "add ui_update callback"
This reverts commit 4da32cc009.
* add show_description method
* remove padding from line separator.
like, WHY? 😩😩
* simplify
* I. SAID. SIMPLIFY.
* AAARGGGGGG.....
* init
* option control value fix
* add all controls
* hide all controls
* lint
* scroller -> scroller_tici
* scroller -> scroller_tici
* ui: `GuiApplicationExt`
* add to readme
* use gui_app.sunnypilot_ui()
* use gui_app.sunnypilot_ui()
* use gui_app.sunnypilot_ui()
* optimizations
* Removed hide for now
* why? because!
* oops
* better
* add param, timeout
* add toggle
* not doing this
* we doing this now @devtekve? FINE!!
* it wasn't me
* really @devtekve? REALLY??
* refresh controls
* ugh
* changes
* fix
* inline everything again
* handle default
* use ui_state param
* better
* fix
* lint
* better
* solve for lint
* default on pls
* screen saver hue distance and increase minimum color shift
* debounce screen saver color changes
* fix dismiss flow
---------
Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
Co-authored-by: DevTekVE <devtekve@gmail.com>
This commit is contained in:
@@ -180,6 +180,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"QuietMode", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"RainbowMode", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"RocketFuel", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"ScreenSaverEnabled", {PERSISTENT | BACKUP, BOOL, "1"}},
|
||||
{"ScreenSaverTimeout", {PERSISTENT | BACKUP, INT, "300"}},
|
||||
{"ShowAdvancedControls", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"ShowTurnSignals", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
{"StandstillTimer", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||
|
||||
@@ -9,7 +9,7 @@ from enum import IntEnum
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.system.ui.sunnypilot.widgets.list_view import option_item_sp
|
||||
from openpilot.system.ui.sunnypilot.widgets.list_view import toggle_item_sp, option_item_sp
|
||||
from openpilot.sunnypilot.system.params_migration import ONROAD_BRIGHTNESS_TIMER_VALUES
|
||||
|
||||
|
||||
@@ -61,10 +61,26 @@ class DisplayLayout(Widget):
|
||||
f"{value} s" if value < 60 else f"{int(value/60)} m"),
|
||||
inline=True
|
||||
)
|
||||
self._screensaver_toggle = toggle_item_sp(
|
||||
param="ScreenSaverEnabled",
|
||||
title=lambda: tr("Screen Saver"),
|
||||
description=lambda: tr("Show a screen saver when the device is offroad and idle, instead of turning the screen off."),
|
||||
)
|
||||
self._screensaver_timeout = option_item_sp(
|
||||
param="ScreenSaverTimeout",
|
||||
title=lambda: tr("Screen Saver Duration"),
|
||||
description=lambda: tr("How long the screen saver runs before the screen turns off."),
|
||||
min_value=60,
|
||||
max_value=600,
|
||||
value_change_step=60,
|
||||
label_callback=lambda value: f"{int(value/60)} m"
|
||||
)
|
||||
items = [
|
||||
self._onroad_brightness,
|
||||
self._onroad_brightness_timer,
|
||||
self._interactivity_timeout,
|
||||
self._screensaver_toggle,
|
||||
self._screensaver_timeout,
|
||||
]
|
||||
return items
|
||||
|
||||
@@ -87,6 +103,8 @@ class DisplayLayout(Widget):
|
||||
brightness_val = self._onroad_brightness.action_item.current_value
|
||||
self._onroad_brightness_timer.action_item.set_enabled(brightness_val not in (OnroadBrightness.AUTO, OnroadBrightness.AUTO_DARK))
|
||||
|
||||
self._screensaver_timeout.set_visible(self._screensaver_toggle.action_item.get_state())
|
||||
|
||||
def _render(self, rect):
|
||||
self._scroller.render(rect)
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.ui.sunnypilot.layouts.settings.display import OnroadBrightness
|
||||
from openpilot.sunnypilot.sunnylink.sunnylink_state import SunnylinkState
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.sunnypilot.widgets.screen_saver import ScreenSaverSP
|
||||
|
||||
OpenpilotState = log.SelfdriveState.OpenpilotState
|
||||
MADSState = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
|
||||
@@ -38,6 +39,9 @@ class UIStateSP:
|
||||
|
||||
self.sunnylink_state = SunnylinkState()
|
||||
|
||||
self.screensaver = ScreenSaverSP(params=self.params)
|
||||
self.screensaver_enabled: bool = False
|
||||
|
||||
self.active_bundle = None
|
||||
self.blindspot: bool = False
|
||||
self.chevron_metrics = None
|
||||
@@ -170,6 +174,7 @@ class UIStateSP:
|
||||
self.turn_signals = self.params.get_bool("ShowTurnSignals")
|
||||
self.boot_offroad_mode = self.params.get("DeviceBootMode", return_default=True)
|
||||
self.always_offroad = self.params.get_bool("OffroadMode")
|
||||
self.screensaver_enabled = self.params.get_bool("ScreenSaverEnabled")
|
||||
|
||||
if not self._sp_initialized:
|
||||
self._sp_initialized = True
|
||||
@@ -230,10 +235,26 @@ class UIStateSP:
|
||||
|
||||
|
||||
class DeviceSP:
|
||||
def __init__(self):
|
||||
self._blocked_by_screensaver: bool = False
|
||||
|
||||
def _set_awake(self, on: bool, _ui_state=None):
|
||||
self._blocked_by_screensaver = False
|
||||
|
||||
if _ui_state.boot_offroad_mode == 1 and not on:
|
||||
_ui_state.params.put_bool("OffroadMode", True)
|
||||
|
||||
if not on and _ui_state.screensaver_enabled:
|
||||
if _ui_state.screensaver.was_dismissed:
|
||||
if gui_app.get_active_widget() == _ui_state.screensaver:
|
||||
gui_app.pop_widget()
|
||||
elif _ui_state.screensaver.is_active:
|
||||
self._blocked_by_screensaver = True
|
||||
else:
|
||||
_ui_state.screensaver.initialize()
|
||||
gui_app.push_widget(_ui_state.screensaver)
|
||||
self._blocked_by_screensaver = True
|
||||
|
||||
@staticmethod
|
||||
def set_onroad_brightness(_ui_state, awake: bool, cur_brightness: float) -> float:
|
||||
if not awake or not _ui_state.started:
|
||||
|
||||
@@ -340,6 +340,8 @@ class Device(DeviceSP):
|
||||
def _set_awake(self, on: bool, _ui_state=None):
|
||||
if on != self._awake:
|
||||
super()._set_awake(on, _ui_state or ui_state)
|
||||
if self._blocked_by_screensaver:
|
||||
return
|
||||
self._awake = on
|
||||
cloudlog.debug(f"setting display power {int(on)}")
|
||||
HARDWARE.set_display_power(on)
|
||||
|
||||
@@ -1286,6 +1286,67 @@
|
||||
"label": "2 m"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "ScreenSaverEnabled",
|
||||
"widget": "toggle",
|
||||
"title": "Screen Saver",
|
||||
"description": "Show a screen saver when the device is offroad and idle, instead of turning the screen off."
|
||||
},
|
||||
{
|
||||
"key": "ScreenSaverTimeout",
|
||||
"widget": "multiple_button",
|
||||
"title": "Screen Saver Duration",
|
||||
"description": "How long the screen saver runs before the screen turns off.",
|
||||
"options": [
|
||||
{
|
||||
"value": 60,
|
||||
"label": "1 m"
|
||||
},
|
||||
{
|
||||
"value": 120,
|
||||
"label": "2 m"
|
||||
},
|
||||
{
|
||||
"value": 180,
|
||||
"label": "3 m"
|
||||
},
|
||||
{
|
||||
"value": 240,
|
||||
"label": "4 m"
|
||||
},
|
||||
{
|
||||
"value": 300,
|
||||
"label": "5 m"
|
||||
},
|
||||
{
|
||||
"value": 360,
|
||||
"label": "6 m"
|
||||
},
|
||||
{
|
||||
"value": 420,
|
||||
"label": "7 m"
|
||||
},
|
||||
{
|
||||
"value": 480,
|
||||
"label": "8 m"
|
||||
},
|
||||
{
|
||||
"value": 540,
|
||||
"label": "9 m"
|
||||
},
|
||||
{
|
||||
"value": 600,
|
||||
"label": "10 m"
|
||||
}
|
||||
],
|
||||
"enablement": [
|
||||
{
|
||||
"type": "param",
|
||||
"key": "ScreenSaverEnabled",
|
||||
"equals": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -128,3 +128,36 @@ sections:
|
||||
label: 1 m
|
||||
- value: 120
|
||||
label: 2 m
|
||||
- key: ScreenSaverEnabled
|
||||
widget: toggle
|
||||
title: Screen Saver
|
||||
description: Show a screen saver when the device is offroad and idle, instead of turning the screen off.
|
||||
- key: ScreenSaverTimeout
|
||||
widget: multiple_button
|
||||
title: Screen Saver Duration
|
||||
description: How long the screen saver runs before the screen turns off.
|
||||
options:
|
||||
- value: 60
|
||||
label: 1 m
|
||||
- value: 120
|
||||
label: 2 m
|
||||
- value: 180
|
||||
label: 3 m
|
||||
- value: 240
|
||||
label: 4 m
|
||||
- value: 300
|
||||
label: 5 m
|
||||
- value: 360
|
||||
label: 6 m
|
||||
- value: 420
|
||||
label: 7 m
|
||||
- value: 480
|
||||
label: 8 m
|
||||
- value: 540
|
||||
label: 9 m
|
||||
- value: 600
|
||||
label: 10 m
|
||||
enablement:
|
||||
- type: param
|
||||
key: ScreenSaverEnabled
|
||||
equals: true
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
"""
|
||||
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
|
||||
This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
|
||||
import pyray as rl
|
||||
|
||||
from openpilot.common.hardware import HARDWARE
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
|
||||
|
||||
class ScreenSaverSP(Widget):
|
||||
def __init__(self, params: Params | None = None):
|
||||
super().__init__()
|
||||
self.set_rect(rl.Rectangle(0, 0, gui_app.width, gui_app.height))
|
||||
self._params = params or Params()
|
||||
self._is_mici = HARDWARE.get_device_type() == 'mici' or (HARDWARE.get_device_type() == "pc" and os.getenv("BIG") != "1")
|
||||
|
||||
self.x = 0.0
|
||||
self.y = 100.0
|
||||
self.vx = 120.0 if self._is_mici else 300.0
|
||||
self.vy = 70.0 if self._is_mici else 200.0
|
||||
self._hue = 150
|
||||
self.color = rl.color_from_hsv(self._hue, 1, 1)
|
||||
|
||||
self.text = "sunnypilot"
|
||||
self.font_size = 50 if self._is_mici else 200
|
||||
self._start_time = None
|
||||
self._dismiss = False
|
||||
self._screensaver_timeout = 300
|
||||
self._hit_last_frame = False
|
||||
|
||||
@property
|
||||
def is_active(self) -> bool:
|
||||
return self._start_time is not None and not self._dismiss
|
||||
|
||||
@property
|
||||
def was_dismissed(self) -> bool:
|
||||
return self._dismiss
|
||||
|
||||
def initialize(self):
|
||||
self._screensaver_timeout = self._params.get("ScreenSaverTimeout", return_default=True)
|
||||
if self._start_time is None:
|
||||
self._start_time = time.monotonic()
|
||||
self._dismiss = False
|
||||
|
||||
def hide_event(self):
|
||||
super().hide_event()
|
||||
self._dismiss = False
|
||||
self._start_time = None
|
||||
|
||||
def _handle_mouse_release(self, mouse_pos):
|
||||
self._dismiss = True
|
||||
self._start_time = None
|
||||
gui_app.pop_widget()
|
||||
return super()._handle_mouse_release(mouse_pos)
|
||||
|
||||
def _update_state(self):
|
||||
super()._update_state()
|
||||
|
||||
self.font = gui_app.font(FontWeight.AUDIOWIDE)
|
||||
text_size = measure_text_cached(self.font, self.text, self.font_size, 0)
|
||||
self.logo_width = text_size.x
|
||||
self.logo_height = text_size.y
|
||||
|
||||
if self._start_time and time.monotonic() - self._start_time > self._screensaver_timeout:
|
||||
self._dismiss = True
|
||||
self._start_time = None
|
||||
|
||||
dt = rl.get_frame_time()
|
||||
|
||||
self.x += self.vx * dt
|
||||
self.y += self.vy * dt
|
||||
|
||||
hit_x = hit_y = False
|
||||
if self.x + self.logo_width > self.rect.width:
|
||||
self.vx *= -1
|
||||
self.x = self.rect.width - self.logo_width
|
||||
hit_x = True
|
||||
elif self.x < 0:
|
||||
self.vx *= -1
|
||||
self.x = 0
|
||||
hit_x = True
|
||||
|
||||
if self.y + self.logo_height > self.rect.height:
|
||||
self.vy *= -1
|
||||
self.y = self.rect.height - self.logo_height
|
||||
hit_y = True
|
||||
elif self.y < 0:
|
||||
self.vy *= -1
|
||||
self.y = 0
|
||||
hit_y = True
|
||||
|
||||
hit = hit_x or hit_y
|
||||
if hit and not self._hit_last_frame:
|
||||
while self._hue_dist((new_hue := rl.get_random_value(0, 360)), self._hue) < 120:
|
||||
pass
|
||||
self._hue = new_hue
|
||||
self.color = rl.color_from_hsv(self._hue, 1, 1)
|
||||
self._hit_last_frame = hit
|
||||
|
||||
@staticmethod
|
||||
def _hue_dist(a, b):
|
||||
d = abs(a - b)
|
||||
return min(d, 360 - d)
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
self.set_rect(rect)
|
||||
rl.clear_background(rl.BLACK)
|
||||
rl.draw_text_ex(self.font, self.text, rl.Vector2(int(self.x), int(self.y)), self.font_size, 0, self.color)
|
||||
return -1
|
||||
Reference in New Issue
Block a user