ncurseswwin/form/
callbacks.rs1use std::{collections::HashMap, sync::Mutex};
24use strum::IntoEnumIterator;
25use strum_macros::EnumIter;
26use ncursesw::{SCREEN, form::FORM};
27use crate::form::Form;
28
29static MODULE_PATH: &str = "ncurseswwin::form::callbacks::";
30
31#[derive(PartialEq, Eq, Hash)]
32struct FormKey {
33 form: FORM
34}
35
36impl FormKey {
37 fn new(form: FORM) -> Self {
38 Self { form }
39 }
40}
41
42unsafe impl Send for FormKey { }
43unsafe impl Sync for FormKey { }
44
45#[derive(Copy, Clone, PartialEq, Eq, Hash)]
46struct FormValue {
47 screen: Option<SCREEN>
48}
49
50impl FormValue {
51 fn new(screen: Option<SCREEN>) -> Self {
52 Self { screen }
53 }
54
55 fn screen(&self) -> Option<SCREEN> {
56 self.screen
57 }
58}
59
60unsafe impl Send for FormValue { }
61unsafe impl Sync for FormValue { }
62
63#[derive(Clone, Copy, EnumIter, Debug, PartialEq, Eq, Hash)]
64pub(in crate::form) enum CallbackType {
65 FieldInit,
66 FieldTerm,
67 FormInit,
68 FormTerm
69}
70
71#[derive(PartialEq, Eq, Hash)]
72struct CallbackKey {
73 form: Option<FORM>,
74 callback_type: CallbackType
75}
76
77impl CallbackKey {
78 fn new(form: Option<FORM>, callback_type: CallbackType) -> Self {
79 Self { form, callback_type }
80 }
81}
82
83unsafe impl Send for CallbackKey { }
84unsafe impl Sync for CallbackKey { }
85
86type Callback = Option<Box<dyn Fn(&Form) + Send>>;
87
88lazy_static! {
89 static ref FORMSCREENS: Mutex<HashMap<FormKey, FormValue>> = Mutex::new(HashMap::new());
90 static ref CALLBACKS: Mutex<HashMap<CallbackKey, Callback>> = Mutex::new(HashMap::new());
91}
92
93macro_rules! extern_form_callback {
94 ($func: ident, $cb_t: ident) => {
95 pub(in crate::form) extern fn $func(form: FORM) {
96 form_callback(form, CallbackType::$cb_t)
97 }
98 }
99}
100
101extern_form_callback!(extern_field_init, FieldInit);
102extern_form_callback!(extern_field_term, FieldTerm);
103extern_form_callback!(extern_form_init, FormInit);
104extern_form_callback!(extern_form_term, FormTerm);
105
106fn form_callback(form: FORM, cb_type: CallbackType) {
107 let get_form = || -> Form {
108 let screen = FORMSCREENS
109 .lock()
110 .unwrap_or_else(|_| panic!("{}form_callback({:p}, {:?}) : FORMSCREENS.lock() failed!!!", MODULE_PATH, form, cb_type))
111 .get(&FormKey::new(form))
112 .unwrap_or_else(|| panic!("{}form_callback({:p}, {:?}) : FORMSCREENS.lock().get() failed!!!", MODULE_PATH, form, cb_type))
113 .screen();
114
115 Form::_from(screen, form, unsafe { (*form).field }, false)
116 };
117
118 let callbacks = CALLBACKS
119 .lock()
120 .unwrap_or_else(|_| panic!("{}form_callback({:p}, {:?}) : CALLBACKS.lock() failed!!!", MODULE_PATH, form, cb_type));
121
122 if let Some(ref callback) = callbacks
123 .get(&CallbackKey::new(Some(form), cb_type))
124 .unwrap_or(&None)
125 {
126 callback(&get_form())
127 } else if let Some(ref callback) = callbacks
128 .get(&CallbackKey::new(None, cb_type))
129 .unwrap_or(&None)
130 {
131 callback(&get_form())
132 } else {
133 panic!("{}form_callback({:p}, {:?}) : callbacks.lock().get() returned None!!!", MODULE_PATH, form, cb_type)
134 }
135}
136
137pub(in crate::form) fn set_form_screen(form: FORM, screen: Option<SCREEN>) {
138 FORMSCREENS
139 .lock()
140 .unwrap_or_else(|_| panic!("{}set_form_screen({:p}) : FORMSCREENS.lock() failed!!!", MODULE_PATH, form))
141 .insert(FormKey::new(form), FormValue::new(screen));
142}
143
144pub(in crate::form) fn set_form_callback<F>(form: Option<FORM>, cb_type: CallbackType, func: F)
145 where F: Fn(&Form) + 'static + Send
146{
147 CALLBACKS
148 .lock()
149 .unwrap_or_else(|_| panic!("{}set_form_callback() : CALLBACKS.lock() failed!!!", MODULE_PATH))
150 .insert(CallbackKey::new(form, cb_type), Some(Box::new(move |form| func(form))));
151}
152
153pub(in crate::form) fn form_tidyup(form: FORM) {
154 let mut form_screens = FORMSCREENS
155 .lock()
156 .unwrap_or_else(|_| panic!("{}form_tidyup({:p}) : FORMSCREENS.lock() failed!!!", MODULE_PATH, form));
157
158 form_screens.remove(&FormKey::new(form));
159 form_screens.shrink_to_fit();
160
161 let mut callbacks = CALLBACKS
162 .lock()
163 .unwrap_or_else(|_| panic!("{}form_tidyup({:p}) : CALLBACKS.lock() failed!!!", MODULE_PATH, form));
164
165 let mut shrink_to_fit = false;
166
167 for cb_type in CallbackType::iter() {
168 if callbacks.remove(&CallbackKey::new(Some(form), cb_type)).is_some() {
169 shrink_to_fit = true;
170 }
171 }
172
173 if shrink_to_fit {
174 callbacks.shrink_to_fit();
175 }
176}