1pub mod action_label;
13pub mod battery;
14pub mod button;
15pub mod calculator;
16pub mod clock;
17pub mod common;
18pub mod device_auth;
19pub mod dialog;
20pub mod dictionary;
21pub mod file_chooser;
22pub mod filler;
23pub mod frontlight;
24pub mod github;
25pub mod home;
26pub mod icon;
27pub mod image;
28pub mod input_field;
29pub mod intermission;
30pub mod key;
31pub mod keyboard;
32pub mod label;
33pub mod labeled_icon;
34pub mod menu;
35pub mod menu_entry;
36pub mod named_input;
37pub mod navigation;
38pub mod notification;
39pub mod ota;
40
41pub use self::notification::NotificationEvent;
42pub mod page_label;
43pub mod preset;
44pub mod presets_list;
45pub mod progress_bar;
46pub mod reader;
47pub mod rotation_values;
48pub mod rounded_button;
49pub mod search_bar;
50pub mod settings_editor;
51pub mod sketch;
52pub mod slider;
53pub mod startup;
54pub mod toggle;
55pub mod toggleable_keyboard;
56pub mod top_bar;
57pub mod touch_events;
58
59use self::calculator::LineOrigin;
60use self::github::GithubEvent;
61use self::key::KeyKind;
62use crate::color::Color;
63use crate::context::Context;
64use crate::document::{Location, TextLocation};
65use crate::font::Fonts;
66use crate::framebuffer::{Framebuffer, UpdateMode};
67use crate::geom::{Boundary, CycleDir, LinearDir, Rectangle};
68use crate::gesture::GestureEvent;
69use crate::input::{DeviceEvent, FingerStatus};
70use crate::metadata::{
71 Info, Margin, PageScheme, ScrollMode, SimpleStatus, SortMethod, TextAlign, ZoomMode,
72};
73use crate::settings::{
74 self, ButtonScheme, FinishedAction, FirstColumn, RotationLock, SecondColumn,
75};
76use crate::view::ota::OtaEntryId;
77use downcast_rs::{impl_downcast, Downcast};
78use fxhash::FxHashMap;
79use std::collections::VecDeque;
80use std::fmt::{self, Debug};
81use std::ops::{Deref, DerefMut};
82use std::path::PathBuf;
83use std::sync::atomic::{AtomicU64, Ordering};
84use std::sync::mpsc::Sender;
85use std::time::{Duration, Instant};
86use tracing::error;
87use unic_langid::LanguageIdentifier;
88
89pub const THICKNESS_SMALL: f32 = 1.0;
91pub const THICKNESS_MEDIUM: f32 = 2.0;
92pub const THICKNESS_LARGE: f32 = 3.0;
93
94pub const BORDER_RADIUS_SMALL: f32 = 6.0;
96pub const BORDER_RADIUS_MEDIUM: f32 = 9.0;
97pub const BORDER_RADIUS_LARGE: f32 = 12.0;
98
99pub const SMALL_BAR_HEIGHT: f32 = 121.0;
102pub const BIG_BAR_HEIGHT: f32 = 163.0;
103
104pub const CLOSE_IGNITION_DELAY: Duration = Duration::from_millis(150);
105
106pub type Bus = VecDeque<Event>;
107pub type Hub = Sender<Event>;
108
109pub trait View: Downcast {
110 fn handle_event(
111 &mut self,
112 evt: &Event,
113 hub: &Hub,
114 bus: &mut Bus,
115 rq: &mut RenderQueue,
116 context: &mut Context,
117 ) -> bool;
118 fn render(&self, fb: &mut dyn Framebuffer, rect: Rectangle, fonts: &mut Fonts);
119 fn rect(&self) -> &Rectangle;
120 fn rect_mut(&mut self) -> &mut Rectangle;
121 fn children(&self) -> &Vec<Box<dyn View>>;
122 fn children_mut(&mut self) -> &mut Vec<Box<dyn View>>;
123 fn id(&self) -> Id;
124
125 fn render_rect(&self, _rect: &Rectangle) -> Rectangle {
126 *self.rect()
127 }
128
129 fn resize(
130 &mut self,
131 rect: Rectangle,
132 _hub: &Hub,
133 _rq: &mut RenderQueue,
134 _context: &mut Context,
135 ) {
136 *self.rect_mut() = rect;
137 }
138
139 fn child(&self, index: usize) -> &dyn View {
140 self.children()[index].as_ref()
141 }
142
143 fn child_mut(&mut self, index: usize) -> &mut dyn View {
144 self.children_mut()[index].as_mut()
145 }
146
147 fn len(&self) -> usize {
148 self.children().len()
149 }
150
151 fn might_skip(&self, _evt: &Event) -> bool {
152 false
153 }
154
155 fn might_rotate(&self) -> bool {
156 true
157 }
158
159 fn is_background(&self) -> bool {
160 false
161 }
162
163 fn view_id(&self) -> Option<ViewId> {
164 None
165 }
166}
167
168impl_downcast!(View);
169
170impl Debug for Box<dyn View> {
171 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
172 write!(f, "Box<dyn View>")
173 }
174}
175
176#[cfg_attr(feature = "otel", tracing::instrument(skip(view, hub, parent_bus, rq, context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
183pub fn handle_event(
184 view: &mut dyn View,
185 evt: &Event,
186 hub: &Hub,
187 parent_bus: &mut Bus,
188 rq: &mut RenderQueue,
189 context: &mut Context,
190) -> bool {
191 if view.len() > 0 {
192 let mut captured = false;
193
194 if view.might_skip(evt) {
195 return captured;
196 }
197
198 let mut child_bus: Bus = VecDeque::with_capacity(1);
199
200 for i in (0..view.len()).rev() {
201 if handle_event(view.child_mut(i), evt, hub, &mut child_bus, rq, context) {
202 captured = true;
203 break;
204 }
205 }
206
207 let mut temp_bus: Bus = VecDeque::with_capacity(1);
208
209 child_bus
210 .retain(|child_evt| !view.handle_event(child_evt, hub, &mut temp_bus, rq, context));
211
212 parent_bus.append(&mut child_bus);
213 parent_bus.append(&mut temp_bus);
214
215 captured || view.handle_event(evt, hub, parent_bus, rq, context)
216 } else {
217 view.handle_event(evt, hub, parent_bus, rq, context)
218 }
219}
220
221#[cfg_attr(feature = "otel", tracing::instrument(skip(view, ids, rects, bgs, fb, fonts, updating), fields(wait = wait)))]
225pub fn render(
226 view: &dyn View,
227 wait: bool,
228 ids: &FxHashMap<Id, Vec<Rectangle>>,
229 rects: &mut Vec<Rectangle>,
230 bgs: &mut Vec<Rectangle>,
231 fb: &mut dyn Framebuffer,
232 fonts: &mut Fonts,
233 updating: &mut Vec<UpdateData>,
234) {
235 let mut render_rects = Vec::new();
236
237 if view.len() == 0 || view.is_background() {
238 for rect in ids
239 .get(&view.id())
240 .cloned()
241 .into_iter()
242 .flatten()
243 .chain(rects.iter().filter_map(|r| r.intersection(view.rect())))
244 .chain(bgs.iter().filter_map(|r| r.intersection(view.rect())))
245 {
246 let render_rect = view.render_rect(&rect);
247
248 if wait {
249 updating.retain(|update| {
250 let overlaps = render_rect.overlaps(&update.rect);
251 if overlaps && !update.has_completed() {
252 fb.wait(update.token)
253 .map_err(|e| {
254 error!("Can't wait for {}, {}: {:#}", update.token, update.rect, e)
255 })
256 .ok();
257 }
258 !overlaps
259 });
260 }
261
262 view.render(fb, rect, fonts);
263 render_rects.push(render_rect);
264
265 if *view.rect() == render_rect {
267 break;
268 }
269 }
270 } else {
271 bgs.extend(ids.get(&view.id()).cloned().into_iter().flatten());
272 }
273
274 for rect in render_rects.into_iter() {
276 if rects.is_empty() {
277 rects.push(rect);
278 } else {
279 if let Some(last) = rects.last_mut() {
280 if rect.extends(last) {
281 last.absorb(&rect);
282 let mut i = rects.len();
283 while i > 1 && rects[i - 1].extends(&rects[i - 2]) {
284 if let Some(rect) = rects.pop() {
285 if let Some(last) = rects.last_mut() {
286 last.absorb(&rect);
287 }
288 }
289 i -= 1;
290 }
291 } else {
292 let mut i = rects.len();
293 while i > 0 && !rects[i - 1].contains(&rect) {
294 i -= 1;
295 }
296 if i == 0 {
297 rects.push(rect);
298 }
299 }
300 }
301 }
302 }
303
304 for i in 0..view.len() {
305 render(view.child(i), wait, ids, rects, bgs, fb, fonts, updating);
306 }
307}
308
309#[inline]
310pub fn process_render_queue(
311 view: &dyn View,
312 rq: &mut RenderQueue,
313 context: &mut Context,
314 updating: &mut Vec<UpdateData>,
315) {
316 for ((mode, wait), pairs) in rq.drain() {
317 let mut ids = FxHashMap::default();
318 let mut rects = Vec::new();
319 let mut bgs = Vec::new();
320
321 for (id, rect) in pairs.into_iter().rev() {
322 if let Some(id) = id {
323 ids.entry(id).or_insert_with(Vec::new).push(rect);
324 } else {
325 bgs.push(rect);
326 }
327 }
328
329 render(
330 view,
331 wait,
332 &ids,
333 &mut rects,
334 &mut bgs,
335 context.fb.as_mut(),
336 &mut context.fonts,
337 updating,
338 );
339
340 for rect in rects {
341 match context.fb.update(&rect, mode) {
342 Ok(token) => {
343 updating.push(UpdateData {
344 token,
345 rect,
346 time: Instant::now(),
347 });
348 }
349 Err(err) => {
350 error!("Can't update {}: {:#}.", rect, err);
351 }
352 }
353 }
354 }
355}
356
357#[inline]
358pub fn wait_for_all(updating: &mut Vec<UpdateData>, context: &mut Context) {
359 for update in updating.drain(..) {
360 if update.has_completed() {
361 continue;
362 }
363 context
364 .fb
365 .wait(update.token)
366 .map_err(|e| error!("Can't wait for {}, {}: {:#}", update.token, update.rect, e))
367 .ok();
368 }
369}
370
371#[derive(Debug, Clone, PartialEq, Eq)]
372pub enum ToggleEvent {
373 View(ViewId),
374 Setting(settings_editor::ToggleSettings),
375}
376
377#[derive(Debug, Clone)]
378pub enum Event {
379 Device(DeviceEvent),
380 Gesture(GestureEvent),
381 Keyboard(KeyboardEvent),
382 Key(KeyKind),
383 Open(Box<Info>),
384 OpenHtml(String, Option<String>),
385 LoadPixmap(usize),
386 Update(UpdateMode),
387 RefreshBookPreview(PathBuf),
388 Invalid(PathBuf),
389 Notification(NotificationEvent),
390 Page(CycleDir),
391 ResultsPage(CycleDir),
392 GoTo(usize),
393 GoToLocation(Location),
394 ResultsGoTo(usize),
395 CropMargins(Box<Margin>),
396 Chapter(CycleDir),
397 SelectDirectory(PathBuf),
398 ToggleSelectDirectory(PathBuf),
399 NavigationBarResized(i32),
400 Focus(Option<ViewId>),
442 Select(EntryId),
443 PropagateSelect(EntryId),
444 EditLanguages,
445 Define(String),
446 Submit(ViewId, String),
447 Slider(SliderId, f32, FingerStatus),
448 ToggleNear(ViewId, Rectangle),
449 ToggleInputHistoryMenu(ViewId, Rectangle),
450 ToggleBookMenu(Rectangle, usize),
451 TogglePresetMenu(Rectangle, usize),
452 SubMenu(Rectangle, Vec<EntryKind>),
453 OpenSettingsCategory(settings_editor::Category),
454 SelectSettingsCategory(settings_editor::Category),
455 UpdateSettings(Box<settings::Settings>),
456 EditLibrary(usize),
457 UpdateLibrary(usize, Box<settings::LibrarySettings>),
458 AddLibrary,
459 DeleteLibrary(usize),
460 ProcessLine(LineOrigin, String),
461 History(CycleDir, bool),
462 Toggle(ToggleEvent),
463 Show(ViewId),
464 Close(ViewId),
465 CloseSub(ViewId),
466 Search(String),
467 SearchResult(usize, Vec<Boundary>),
468 FetcherAddDocument(u32, Box<Info>),
469 FetcherRemoveDocument(u32, PathBuf),
470 FetcherSearch {
471 id: u32,
472 path: Option<PathBuf>,
473 query: Option<String>,
474 sort_by: Option<(SortMethod, bool)>,
475 },
476 CheckFetcher(u32),
477 EndOfSearch,
478 Finished,
479 ClockTick,
480 BatteryTick,
481 ToggleFrontlight,
482 Load(PathBuf),
483 LoadPreset(usize),
484 Scroll(i32),
485 Save,
486 Guess,
487 CheckBattery,
488 SetWifi(bool),
489 MightSuspend,
490 PrepareSuspend,
491 Suspend,
492 Share,
493 PrepareShare,
494 Validate,
495 Cancel,
496 Reseed,
497 Back,
498 Quit,
499 WakeUp,
500 Hold(EntryId),
501 FileChooserClosed(Option<PathBuf>),
504 Github(GithubEvent),
506 Settings(settings_editor::SettingsEvent),
508 OpenNamedInput {
513 view_id: ViewId,
515 label: String,
517 max_chars: usize,
519 initial_text: String,
521 },
522 OtaDownloadProgress {
527 label: String,
528 percent: u8,
529 },
530 StartStableReleaseDownload,
535}
536
537#[derive(Debug, Clone, Eq, PartialEq)]
538pub enum AppCmd {
539 Sketch,
540 Calculator,
541 Dictionary { query: String, language: String },
542 SettingsEditor,
543 TouchEvents,
544 RotationValues,
545}
546
547#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
548pub enum ViewId {
549 Home,
550 Reader,
551 SortMenu,
552 MainMenu,
553 TitleMenu,
554 SelectionMenu,
555 AnnotationMenu,
556 BatteryMenu,
557 ClockMenu,
558 SearchTargetMenu,
559 InputHistoryMenu,
560 KeyboardLayoutMenu,
561 Frontlight,
562 Dictionary,
563 FontSizeMenu,
564 TextAlignMenu,
565 FontFamilyMenu,
566 MarginWidthMenu,
567 ContrastExponentMenu,
568 ContrastGrayMenu,
569 LineHeightMenu,
570 DirectoryMenu,
571 BookMenu,
572 LibraryMenu,
573 PageMenu,
574 PresetMenu,
575 MarginCropperMenu,
576 SearchMenu,
577 SettingsMenu,
579 SettingsValueMenu,
580 SettingsCategoryEditor,
581 LibraryEditor,
582 LibraryRename,
583 LibraryRenameInput,
584 AutoSuspendInput,
585 AutoPowerOffInput,
586 SettingsRetentionInput,
587 IntermissionSuspendInput,
588 IntermissionPowerOffInput,
589 IntermissionShareInput,
590 OtlpEndpointInput,
591 SketchMenu,
592 RenameDocument,
593 RenameDocumentInput,
594 GoToPage,
595 GoToPageInput,
596 GoToResultsPage,
597 GoToResultsPageInput,
598 NamePage,
599 NamePageInput,
600 EditNote,
601 EditNoteInput,
602 EditLanguages,
603 EditLanguagesInput,
604 HomeSearchInput,
605 ReaderSearchInput,
606 DictionarySearchInput,
607 CalculatorInput,
608 SearchBar,
609 AddressBar,
610 AddressBarInput,
611 Keyboard,
612 AboutDialog,
613 ShareDialog,
614 MarginCropper,
615 TopBottomBars,
616 TableOfContents,
617 MessageNotif(Id),
618 SubMenu(u8),
619 Ota(ota::OtaViewId),
620 FileChooser,
621}
622
623#[derive(Debug, Copy, Clone, Eq, PartialEq)]
624pub enum SliderId {
625 FontSize,
626 LightIntensity,
627 LightWarmth,
628 ContrastExponent,
629 ContrastGray,
630}
631
632impl SliderId {
633 pub fn label(self) -> String {
634 match self {
635 SliderId::LightIntensity => "Intensity".to_string(),
636 SliderId::LightWarmth => "Warmth".to_string(),
637 SliderId::FontSize => "Font Size".to_string(),
638 SliderId::ContrastExponent => "Contrast Exponent".to_string(),
639 SliderId::ContrastGray => "Contrast Gray".to_string(),
640 }
641 }
642}
643
644#[derive(Debug, Clone)]
645pub enum Align {
646 Left(i32),
647 Right(i32),
648 Center,
649}
650
651impl Align {
652 #[inline]
653 pub fn offset(&self, width: i32, container_width: i32) -> i32 {
654 match *self {
655 Align::Left(dx) => dx,
656 Align::Right(dx) => container_width - width - dx,
657 Align::Center => (container_width - width) / 2,
658 }
659 }
660}
661
662#[derive(Debug, Copy, Clone)]
663pub enum KeyboardEvent {
664 Append(char),
665 Partial(char),
666 Move { target: TextKind, dir: LinearDir },
667 Delete { target: TextKind, dir: LinearDir },
668 Submit,
669}
670
671#[derive(Debug, Copy, Clone)]
672pub enum TextKind {
673 Char,
674 Word,
675 Extremum,
676}
677
678#[derive(Debug, Clone)]
679pub enum EntryKind {
680 Message(String, Option<String>),
681 Command(String, EntryId),
682 CheckBox(String, EntryId, bool),
683 RadioButton(String, EntryId, bool),
684 SubMenu(String, Vec<EntryKind>),
685 More(Vec<EntryKind>),
686 Separator,
687}
688
689#[derive(Debug, Clone, Eq, PartialEq)]
690pub enum EntryId {
691 About,
692 SystemInfo,
693 OpenDocumentation,
694 LoadLibrary(usize),
695 Load(PathBuf),
696 Flush,
697 Save,
698 Import,
699 CleanUp,
700 Sort(SortMethod),
701 ReverseOrder,
702 EmptyTrash,
703 Rename(PathBuf),
704 Remove(PathBuf),
705 CopyTo(PathBuf, usize),
706 MoveTo(PathBuf, usize),
707 AddDirectory(PathBuf),
708 SelectDirectory(PathBuf),
709 ToggleSelectDirectory(PathBuf),
710 SetStatus(PathBuf, SimpleStatus),
711 SearchAuthor(String),
712 RemovePreset(usize),
713 FirstColumn(FirstColumn),
714 SecondColumn(SecondColumn),
715 ThumbnailPreviews,
716 ApplyCroppings(usize, PageScheme),
717 RemoveCroppings,
718 SetZoomMode(ZoomMode),
719 SetScrollMode(ScrollMode),
720 SetPageName,
721 RemovePageName,
722 HighlightSelection,
723 AnnotateSelection,
724 DefineSelection,
725 SearchForSelection,
726 AdjustSelection,
727 Annotations,
728 Bookmarks,
729 RemoveAnnotation([TextLocation; 2]),
730 EditAnnotationNote([TextLocation; 2]),
731 RemoveAnnotationNote([TextLocation; 2]),
732 GoTo(usize),
733 GoToSelectedPageName,
734 SearchDirection(LinearDir),
735 SetButtonScheme(ButtonScheme),
736 SetFontFamily(String),
737 SetFontSize(i32),
738 SetTextAlign(TextAlign),
739 SetMarginWidth(i32),
740 SetLineHeight(i32),
741 SetContrastExponent(i32),
742 SetContrastGray(i32),
743 SetRotationLock(Option<RotationLock>),
744 SetSearchTarget(Option<String>),
745 SetInputText(ViewId, String),
746 SetKeyboardLayout(String),
747 SetLocale(Option<LanguageIdentifier>),
748 EditLibraryName,
750 EditLibraryPath,
751 DeleteLibrary(usize),
752 SetFinishedAction(FinishedAction),
753 SetLibraryFinishedAction(usize, FinishedAction),
754 ClearLibraryFinishedAction(usize),
755 SetIntermission(settings::IntermKind, settings::IntermissionDisplay),
756 EditIntermissionImage(settings::IntermKind),
757 ToggleShowHidden,
758 #[deprecated(note = "Use ToggleEvent::Settings instead")]
759 ToggleSleepCover,
760 #[deprecated(note = "Use ToggleEvent::Settings instead")]
761 ToggleAutoShare,
762 EditAutoSuspend,
763 EditAutoPowerOff,
764 EditSettingsRetention,
765 SetLogLevel(tracing::Level),
766 EditOtlpEndpoint,
767 ToggleFuzzy,
768 ToggleInverted,
769 ToggleDithered,
770 ToggleWifi,
771 Rotate(i8),
772 Launch(AppCmd),
773 SetPenSize(i32),
774 SetPenColor(Color),
775 TogglePenDynamism,
776 ReloadDictionaries,
777 New,
778 Refresh,
779 TakeScreenshot,
780 Restart,
781 Reboot,
782 Quit,
783 Suspend,
784 PowerOff,
785 CheckForUpdates,
786 FileEntry(PathBuf),
787 Ota(OtaEntryId),
788}
789
790impl EntryKind {
791 pub fn is_separator(&self) -> bool {
792 matches!(*self, EntryKind::Separator)
793 }
794
795 pub fn text(&self) -> &str {
796 match *self {
797 EntryKind::Message(ref s, ..)
798 | EntryKind::Command(ref s, ..)
799 | EntryKind::CheckBox(ref s, ..)
800 | EntryKind::RadioButton(ref s, ..)
801 | EntryKind::SubMenu(ref s, ..) => s,
802 EntryKind::More(..) => "More",
803 _ => "",
804 }
805 }
806
807 pub fn get(&self) -> Option<bool> {
808 match *self {
809 EntryKind::CheckBox(_, _, v) | EntryKind::RadioButton(_, _, v) => Some(v),
810 _ => None,
811 }
812 }
813
814 pub fn set(&mut self, value: bool) {
815 match *self {
816 EntryKind::CheckBox(_, _, ref mut v) | EntryKind::RadioButton(_, _, ref mut v) => {
817 *v = value
818 }
819 _ => (),
820 }
821 }
822}
823
824pub struct RenderData {
825 pub id: Option<Id>,
826 pub rect: Rectangle,
827 pub mode: UpdateMode,
828 pub wait: bool,
829}
830
831impl RenderData {
832 pub fn new(id: Id, rect: Rectangle, mode: UpdateMode) -> RenderData {
833 RenderData {
834 id: Some(id),
835 rect,
836 mode,
837 wait: true,
838 }
839 }
840
841 pub fn no_wait(id: Id, rect: Rectangle, mode: UpdateMode) -> RenderData {
842 RenderData {
843 id: Some(id),
844 rect,
845 mode,
846 wait: false,
847 }
848 }
849
850 pub fn expose(rect: Rectangle, mode: UpdateMode) -> RenderData {
851 RenderData {
852 id: None,
853 rect,
854 mode,
855 wait: true,
856 }
857 }
858}
859
860pub struct UpdateData {
861 pub token: u32,
862 pub time: Instant,
863 pub rect: Rectangle,
864}
865
866pub const MAX_UPDATE_DELAY: Duration = Duration::from_millis(600);
867
868impl UpdateData {
869 pub fn has_completed(&self) -> bool {
870 self.time.elapsed() >= MAX_UPDATE_DELAY
871 }
872}
873
874type RQ = FxHashMap<(UpdateMode, bool), Vec<(Option<Id>, Rectangle)>>;
875pub struct RenderQueue(RQ);
876
877impl RenderQueue {
878 pub fn new() -> RenderQueue {
879 RenderQueue(FxHashMap::default())
880 }
881
882 pub fn add(&mut self, data: RenderData) {
883 self.entry((data.mode, data.wait))
884 .or_insert_with(|| Vec::new())
885 .push((data.id, data.rect));
886 }
887
888 #[cfg(test)]
889 pub fn is_empty(&self) -> bool {
890 self.0.is_empty()
891 }
892
893 #[cfg(test)]
894 pub fn len(&self) -> usize {
895 self.0.values().map(|v| v.len()).sum()
896 }
897}
898
899impl Default for RenderQueue {
900 fn default() -> Self {
901 Self::new()
902 }
903}
904
905impl Deref for RenderQueue {
906 type Target = RQ;
907
908 fn deref(&self) -> &Self::Target {
909 &self.0
910 }
911}
912
913impl DerefMut for RenderQueue {
914 fn deref_mut(&mut self) -> &mut Self::Target {
915 &mut self.0
916 }
917}
918
919pub static ID_FEEDER: IdFeeder = IdFeeder::new(1);
920pub struct IdFeeder(AtomicU64);
921pub type Id = u64;
922
923impl IdFeeder {
924 pub const fn new(id: Id) -> Self {
925 IdFeeder(AtomicU64::new(id))
926 }
927
928 pub fn next(&self) -> Id {
929 self.0.fetch_add(1, Ordering::Relaxed)
930 }
931}