Commit | Line | Data |
---|---|---|
399cb110 | 1 | /* |
35089dc7 JM |
2 | This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). |
3 | Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. | |
4 | Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |
399cb110 | 5 | You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. |
35089dc7 JM |
6 | */ |
7 | ||
8 | #include "libs/Kernel.h" | |
9 | #include "Panel.h" | |
ca6effd7 JM |
10 | #include "PanelScreen.h" |
11 | ||
35089dc7 JM |
12 | #include "libs/nuts_bolts.h" |
13 | #include "libs/utils.h" | |
35089dc7 | 14 | #include "Button.h" |
ca6effd7 | 15 | |
35089dc7 | 16 | #include "modules/utils/player/PlayerPublicAccess.h" |
ca6effd7 JM |
17 | #include "screens/CustomScreen.h" |
18 | #include "screens/MainMenuScreen.h" | |
61134a65 JM |
19 | #include "SlowTicker.h" |
20 | #include "Gcode.h" | |
21 | #include "Pauser.h" | |
22 | #include "PublicData.h" | |
35089dc7 JM |
23 | |
24 | #include "panels/I2CLCD.h" | |
25 | #include "panels/VikiLCD.h" | |
1c2b594a | 26 | #include "panels/Smoothiepanel.h" |
357d0eda | 27 | #include "panels/ReprapDiscountGLCD.h" |
84e7fd5a | 28 | #include "panels/ST7565.h" |
f140cf3a | 29 | #include "version.h" |
7af0714f | 30 | #include "checksumm.h" |
8d54c34c | 31 | #include "ConfigValue.h" |
35089dc7 | 32 | |
ca6effd7 JM |
33 | #define panel_checksum CHECKSUM("panel") |
34 | #define enable_checksum CHECKSUM("enable") | |
35 | #define lcd_checksum CHECKSUM("lcd") | |
36 | #define i2c_lcd_checksum CHECKSUM("i2c_lcd") | |
37 | #define viki_lcd_checksum CHECKSUM("viki_lcd") | |
38 | #define smoothiepanel_checksum CHECKSUM("smoothiepanel") | |
39 | #define panelolu2_checksum CHECKSUM("panelolu2") | |
40 | #define rrd_glcd_checksum CHECKSUM("reprap_discount_glcd") | |
41 | #define st7565_glcd_checksum CHECKSUM("st7565_glcd") | |
42 | ||
fa3f7b30 JM |
43 | #define menu_offset_checksum CHECKSUM("menu_offset") |
44 | #define encoder_resolution_checksum CHECKSUM("encoder_resolution") | |
45 | #define jog_x_feedrate_checksum CHECKSUM("alpha_jog_feedrate") | |
46 | #define jog_y_feedrate_checksum CHECKSUM("beta_jog_feedrate") | |
47 | #define jog_z_feedrate_checksum CHECKSUM("gamma_jog_feedrate") | |
f1e696a3 | 48 | #define longpress_delay_checksum CHECKSUM("longpress_delay") |
ca6effd7 JM |
49 | |
50 | #define hotend_temp_checksum CHECKSUM("hotend_temperature") | |
51 | #define bed_temp_checksum CHECKSUM("bed_temperature") | |
52 | ||
862fc625 JM |
53 | Panel::Panel() |
54 | { | |
35089dc7 JM |
55 | this->counter_changed = false; |
56 | this->click_changed = false; | |
57 | this->refresh_flag = false; | |
58 | this->enter_menu_mode(); | |
862fc625 | 59 | this->lcd = NULL; |
58d6d841 | 60 | this->do_buttons = false; |
862fc625 JM |
61 | this->idle_time = 0; |
62 | this->start_up = true; | |
63 | this->current_screen = NULL; | |
7aa9fef3 | 64 | strcpy(this->playing_file, "Playing file"); |
35089dc7 JM |
65 | } |
66 | ||
862fc625 JM |
67 | Panel::~Panel() |
68 | { | |
58d6d841 | 69 | delete this->lcd; |
35089dc7 JM |
70 | } |
71 | ||
862fc625 JM |
72 | void Panel::on_module_loaded() |
73 | { | |
35089dc7 | 74 | // Exit if this module is not enabled |
314ab8f7 | 75 | if ( !THEKERNEL->config->value( panel_checksum, enable_checksum )->by_default(false)->as_bool() ) { |
58d6d841 JM |
76 | delete this; |
77 | return; | |
399cb110 | 78 | } |
35089dc7 | 79 | |
58d6d841 JM |
80 | // Initialise the LCD, see which LCD to use |
81 | if (this->lcd != NULL) delete this->lcd; | |
314ab8f7 | 82 | int lcd_cksm = get_checksum(THEKERNEL->config->value(panel_checksum, lcd_checksum)->by_default("i2c")->as_string()); |
58d6d841 JM |
83 | |
84 | // Note checksums are not const expressions when in debug mode, so don't use switch | |
862fc625 | 85 | if (lcd_cksm == i2c_lcd_checksum) { |
58d6d841 | 86 | this->lcd = new I2CLCD(); |
862fc625 | 87 | } else if (lcd_cksm == viki_lcd_checksum) { |
58d6d841 | 88 | this->lcd = new VikiLCD(); |
e0107f6b | 89 | this->lcd->set_variant(0); |
862fc625 | 90 | } else if (lcd_cksm == panelolu2_checksum) { |
e0107f6b JM |
91 | this->lcd = new VikiLCD(); |
92 | this->lcd->set_variant(1); | |
862fc625 | 93 | } else if (lcd_cksm == smoothiepanel_checksum) { |
1c2b594a | 94 | this->lcd = new Smoothiepanel(); |
862fc625 | 95 | } else if (lcd_cksm == rrd_glcd_checksum) { |
357d0eda | 96 | this->lcd = new ReprapDiscountGLCD(); |
862fc625 | 97 | } else if (lcd_cksm == st7565_glcd_checksum) { |
84e7fd5a | 98 | this->lcd = new ST7565(); |
862fc625 | 99 | } else { |
58d6d841 JM |
100 | // no lcd type defined |
101 | return; | |
102 | } | |
103 | ||
ca6effd7 JM |
104 | this->custom_screen= new CustomScreen(); // this needs to be called here as it needs the config cache loaded |
105 | ||
58d6d841 JM |
106 | // some panels may need access to this global info |
107 | this->lcd->setPanel(this); | |
108 | ||
f65ce58f | 109 | // the number of screen lines the panel supports |
862fc625 | 110 | this->screen_lines = this->lcd->get_screen_lines(); |
399cb110 | 111 | |
19fb4629 JM |
112 | // some encoders may need more clicks to move menu, this is a divisor and is in config as it is |
113 | // an end user usability issue | |
314ab8f7 | 114 | this->menu_offset = THEKERNEL->config->value( panel_checksum, menu_offset_checksum )->by_default(0)->as_number(); |
19fb4629 | 115 | |
fa3f7b30 | 116 | // override default encoder resolution if needed |
314ab8f7 | 117 | this->encoder_click_resolution = THEKERNEL->config->value( panel_checksum, encoder_resolution_checksum )->by_default(this->lcd->getEncoderResolution())->as_number(); |
fa3f7b30 | 118 | |
58d6d841 | 119 | // load jogging feedrates in mm/min |
1ad23cd3 MM |
120 | jogging_speed_mm_min[0] = THEKERNEL->config->value( panel_checksum, jog_x_feedrate_checksum )->by_default(3000.0f)->as_number(); |
121 | jogging_speed_mm_min[1] = THEKERNEL->config->value( panel_checksum, jog_y_feedrate_checksum )->by_default(3000.0f)->as_number(); | |
122 | jogging_speed_mm_min[2] = THEKERNEL->config->value( panel_checksum, jog_z_feedrate_checksum )->by_default(300.0f )->as_number(); | |
f19a841a JM |
123 | |
124 | // load the default preset temeratures | |
1ad23cd3 MM |
125 | default_hotend_temperature = THEKERNEL->config->value( panel_checksum, hotend_temp_checksum )->by_default(185.0f )->as_number(); |
126 | default_bed_temperature = THEKERNEL->config->value( panel_checksum, bed_temp_checksum )->by_default(60.0f )->as_number(); | |
f19a841a | 127 | |
399cb110 | 128 | |
95195a29 JM |
129 | this->up_button.up_attach( this, &Panel::on_up ); |
130 | this->down_button.up_attach( this, &Panel::on_down ); | |
ab4abea9 | 131 | this->click_button.up_attach( this, &Panel::on_select ); |
95195a29 | 132 | this->back_button.up_attach( this, &Panel::on_back ); |
52500e58 | 133 | this->pause_button.up_attach( this, &Panel::on_pause ); |
35089dc7 | 134 | |
355c5796 L |
135 | |
136 | //setting longpress_delay | |
137 | int longpress_delay = THEKERNEL->config->value( panel_checksum, longpress_delay_checksum )->by_default(0)->as_number(); | |
138 | this->up_button.set_longpress_delay(longpress_delay); | |
f1e696a3 | 139 | this->down_button.set_longpress_delay(longpress_delay); |
355c5796 L |
140 | // this->click_button.set_longpress_delay(longpress_delay); |
141 | // this->back_button.set_longpress_delay(longpress_delay); | |
142 | // this->pause_button.set_longpress_delay(longpress_delay); | |
61134a65 JM |
143 | |
144 | ||
314ab8f7 MM |
145 | THEKERNEL->slow_ticker->attach( 100, this, &Panel::button_tick ); |
146 | THEKERNEL->slow_ticker->attach( 1000, this, &Panel::encoder_check ); | |
399cb110 | 147 | |
767e7c51 JM |
148 | // Register for events |
149 | this->register_for_event(ON_IDLE); | |
150 | this->register_for_event(ON_MAIN_LOOP); | |
399cb110 | 151 | this->register_for_event(ON_GCODE_RECEIVED); |
767e7c51 | 152 | |
35089dc7 | 153 | // Refresh timer |
314ab8f7 | 154 | THEKERNEL->slow_ticker->attach( 20, this, &Panel::refresh_tick ); |
35089dc7 JM |
155 | } |
156 | ||
157 | // Enter a screen, we only care about it now | |
862fc625 JM |
158 | void Panel::enter_screen(PanelScreen *screen) |
159 | { | |
35089dc7 JM |
160 | screen->panel = this; |
161 | this->current_screen = screen; | |
162 | this->reset_counter(); | |
163 | this->current_screen->on_enter(); | |
164 | } | |
165 | ||
166 | // Reset the counter | |
862fc625 JM |
167 | void Panel::reset_counter() |
168 | { | |
35089dc7 JM |
169 | *this->counter = 0; |
170 | this->counter_changed = false; | |
171 | } | |
172 | ||
173 | // Indicate the idle loop we want to call the refresh hook in the current screen | |
5ff3e912 | 174 | // called 20 times a second |
862fc625 JM |
175 | uint32_t Panel::refresh_tick(uint32_t dummy) |
176 | { | |
58d6d841 JM |
177 | this->refresh_flag = true; |
178 | this->idle_time++; | |
179 | return 0; | |
35089dc7 JM |
180 | } |
181 | ||
446deda2 | 182 | // Encoder pins changed in interrupt |
862fc625 JM |
183 | uint32_t Panel::encoder_check(uint32_t dummy) |
184 | { | |
58d6d841 JM |
185 | // TODO if encoder reads go through i2c like on smoothie panel this needs to be |
186 | // optionally done in idle loop, however when reading encoder directly it needs to be done | |
187 | // frequently, smoothie panel will return an actual delta count so won't miss any if polled slowly | |
b6e81799 | 188 | // NOTE this code will not work if change is not -1,0,-1 anything greater (as in above case) will not work properly |
58d6d841 | 189 | static int encoder_counter = 0; |
35089dc7 | 190 | int change = lcd->readEncoderDelta(); |
58d6d841 | 191 | encoder_counter += change; |
b6e81799 | 192 | |
862fc625 | 193 | if ( change != 0 && encoder_counter % this->encoder_click_resolution == 0 ) { |
58d6d841 JM |
194 | this->counter_changed = true; |
195 | (*this->counter) += change; | |
862fc625 | 196 | this->idle_time = 0; |
35089dc7 | 197 | } |
58d6d841 | 198 | return 0; |
35089dc7 JM |
199 | } |
200 | ||
201 | // Read and update each button | |
862fc625 JM |
202 | uint32_t Panel::button_tick(uint32_t dummy) |
203 | { | |
58d6d841 JM |
204 | this->do_buttons = true; |
205 | return 0; | |
35089dc7 JM |
206 | } |
207 | ||
862fc625 | 208 | void Panel::on_gcode_received(void *argument) |
399cb110 | 209 | { |
862fc625 JM |
210 | Gcode *gcode = static_cast<Gcode *>(argument); |
211 | if ( gcode->has_m) { | |
212 | if ( gcode->m == 117 ) { // set LCD message | |
213 | this->message = get_arguments(gcode->command); | |
214 | if (this->message.size() > 20) this->message = this->message.substr(0, 20); | |
399cb110 JM |
215 | gcode->mark_as_taken(); |
216 | } | |
217 | } | |
218 | } | |
219 | ||
35089dc7 | 220 | // on main loop, we can send gcodes or do anything that waits in this loop |
862fc625 JM |
221 | void Panel::on_main_loop(void *argument) |
222 | { | |
223 | if (this->current_screen != NULL) { | |
f140cf3a JM |
224 | this->current_screen->on_main_loop(); |
225 | this->lcd->on_main_loop(); | |
226 | } | |
35089dc7 JM |
227 | } |
228 | ||
c96fd70a JM |
229 | |
230 | #define ohw_logo_antipixel_width 80 | |
231 | #define ohw_logo_antipixel_height 15 | |
161164be | 232 | static const uint8_t ohw_logo_antipixel_bits[] = { |
862fc625 JM |
233 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, |
234 | 0x00, 0x00, 0x00, 0x01, 0x80, 0x0C, 0x00, 0x33, 0x18, 0xBB, 0xFF, 0xFF, 0xFF, 0xFD, 0x80, 0x5E, | |
235 | 0x80, 0x2D, 0x6B, 0x9B, 0xFF, 0xFF, 0xFF, 0xFD, 0x80, 0xFF, 0xC0, 0x2D, 0x18, 0xAB, 0xFF, 0xFF, | |
236 | 0xFF, 0xFD, 0x80, 0xFF, 0xC0, 0x2D, 0x7B, 0xB3, 0xFF, 0xFF, 0xFF, 0xFD, 0x80, 0x7F, 0x80, 0x33, | |
237 | 0x78, 0xBB, 0xFF, 0xFF, 0xFF, 0xFD, 0x81, 0xF3, 0xE0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, | |
238 | 0x81, 0xF3, 0xE0, 0x3F, 0xFD, 0xB3, 0x18, 0xDD, 0x98, 0xC5, 0x81, 0xF3, 0xE0, 0x3F, 0xFD, 0xAD, | |
239 | 0x6B, 0x5D, 0x6B, 0x5D, 0x80, 0x73, 0x80, 0x3F, 0xFC, 0x21, 0x1B, 0x55, 0x08, 0xC5, 0x80, 0xF3, | |
240 | 0xC0, 0x3F, 0xFD, 0xAD, 0x5B, 0x49, 0x6A, 0xDD, 0x80, 0xE1, 0xC0, 0x3F, 0xFD, 0xAD, 0x68, 0xDD, | |
241 | 0x6B, 0x45, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, | |
242 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF | |
c96fd70a JM |
243 | }; |
244 | ||
35089dc7 JM |
245 | // On idle things, we don't want to do shit in interrupts |
246 | // don't queue gcodes in this | |
862fc625 JM |
247 | void Panel::on_idle(void *argument) |
248 | { | |
249 | if (this->start_up) { | |
f140cf3a JM |
250 | this->lcd->init(); |
251 | ||
ebc64506 JM |
252 | Version v; |
253 | string build(v.get_build()); | |
254 | string date(v.get_build_date()); | |
255 | this->lcd->clear(); | |
862fc625 JM |
256 | this->lcd->setCursor(0, 0); this->lcd->printf("Welcome to Smoothie"); |
257 | this->lcd->setCursor(0, 1); this->lcd->printf("%s", build.substr(0, 20).c_str()); | |
258 | this->lcd->setCursor(0, 2); this->lcd->printf("%s", date.substr(0, 20).c_str()); | |
259 | this->lcd->setCursor(0, 3); this->lcd->printf("Please wait...."); | |
ebc64506 | 260 | |
862fc625 | 261 | if (this->lcd->hasGraphics()) { |
c96fd70a JM |
262 | this->lcd->bltGlyph(24, 40, ohw_logo_antipixel_width, ohw_logo_antipixel_height, ohw_logo_antipixel_bits); |
263 | } | |
264 | ||
f54d25cb JM |
265 | this->lcd->on_refresh(true); // tell lcd to display now |
266 | ||
f140cf3a | 267 | // Default top screen |
ca6effd7 | 268 | this->top_screen= new MainMenuScreen(); |
f140cf3a | 269 | this->top_screen->set_panel(this); |
ca6effd7 | 270 | this->custom_screen->set_parent(this->top_screen); |
862fc625 | 271 | this->start_up = false; |
5ff3e912 | 272 | return; |
f140cf3a | 273 | } |
35089dc7 | 274 | |
ca6effd7 | 275 | MainMenuScreen *mms= static_cast<MainMenuScreen*>(this->top_screen); |
58d6d841 | 276 | // after being idle for a while switch to Watch screen |
5ff3e912 | 277 | if (this->current_screen != NULL && this->idle_time > this->current_screen->idle_timeout_secs()*20) { |
862fc625 | 278 | this->idle_time = 0; |
ca6effd7 JM |
279 | if (mms->watch_screen != this->current_screen) { |
280 | this->enter_screen(mms->watch_screen); | |
58d6d841 JM |
281 | // TODO do we need to reset any state? |
282 | } | |
399cb110 | 283 | |
58d6d841 JM |
284 | return; |
285 | } | |
399cb110 | 286 | |
5ff3e912 JM |
287 | if(current_screen == NULL && this->idle_time > 20*4) { |
288 | this->enter_screen(mms->watch_screen); | |
289 | return; | |
290 | } | |
291 | ||
862fc625 | 292 | if (this->do_buttons) { |
58d6d841 JM |
293 | // we don't want to do I2C in interrupt mode |
294 | this->do_buttons = false; | |
295 | ||
296 | // read the actual buttons | |
862fc625 JM |
297 | int but = lcd->readButtons(); |
298 | if (but != 0) { | |
299 | this->idle_time = 0; | |
5ff3e912 JM |
300 | if(current_screen == NULL) { |
301 | // we were in startup screen so go to watch screen | |
302 | this->enter_screen(mms->watch_screen); | |
303 | return; | |
304 | } | |
f140cf3a JM |
305 | } |
306 | ||
58d6d841 | 307 | // fire events if the buttons are active and debounce is satisfied |
862fc625 JM |
308 | this->up_button.check_signal(but & BUTTON_UP); |
309 | this->down_button.check_signal(but & BUTTON_DOWN); | |
310 | this->back_button.check_signal(but & BUTTON_LEFT); | |
311 | this->click_button.check_signal(but & BUTTON_SELECT); | |
312 | this->pause_button.check_signal(but & BUTTON_PAUSE); | |
313 | } | |
399cb110 | 314 | |
35089dc7 | 315 | // If we are in menu mode and the position has changed |
862fc625 | 316 | if ( this->mode == MENU_MODE && this->counter_change() ) { |
35089dc7 JM |
317 | this->menu_update(); |
318 | } | |
319 | ||
320 | // If we are in control mode | |
862fc625 | 321 | if ( this->mode == CONTROL_MODE && this->counter_change() ) { |
58d6d841 | 322 | this->control_value_update(); |
35089dc7 JM |
323 | } |
324 | ||
325 | // If we must refresh | |
862fc625 | 326 | if ( this->refresh_flag ) { |
35089dc7 | 327 | this->refresh_flag = false; |
862fc625 | 328 | if (this->current_screen != NULL) { |
f140cf3a JM |
329 | this->current_screen->on_refresh(); |
330 | this->lcd->on_refresh(); | |
331 | } | |
35089dc7 JM |
332 | } |
333 | } | |
334 | ||
335 | // Hooks for button clicks | |
862fc625 JM |
336 | uint32_t Panel::on_up(uint32_t dummy) |
337 | { | |
25121d12 | 338 | // this is simulating encoder clicks, but needs to be inverted to |
b6e81799 JM |
339 | // increment values on up,increment by |
340 | int inc = (this->mode == CONTROL_MODE) ? 1 : -(this->menu_offset+1); | |
25121d12 | 341 | *this->counter += inc; |
58d6d841 JM |
342 | this->counter_changed = true; |
343 | return 0; | |
35089dc7 | 344 | } |
5ff3e912 | 345 | |
862fc625 JM |
346 | uint32_t Panel::on_down(uint32_t dummy) |
347 | { | |
b6e81799 | 348 | int inc = (this->mode == CONTROL_MODE) ? -1 : (this->menu_offset+1); |
25121d12 | 349 | *this->counter += inc; |
58d6d841 JM |
350 | this->counter_changed = true; |
351 | return 0; | |
35089dc7 JM |
352 | } |
353 | ||
354 | // on most menu screens will go back to previous higher menu | |
862fc625 JM |
355 | uint32_t Panel::on_back(uint32_t dummy) |
356 | { | |
357 | if (this->mode == MENU_MODE && this->current_screen != NULL && this->current_screen->parent != NULL) { | |
58d6d841 JM |
358 | this->enter_screen(this->current_screen->parent); |
359 | } | |
360 | return 0; | |
35089dc7 JM |
361 | } |
362 | ||
862fc625 JM |
363 | uint32_t Panel::on_select(uint32_t dummy) |
364 | { | |
58d6d841 JM |
365 | // TODO make configurable, including turning off |
366 | // buzz is ignored on panels that do not support buzz | |
58d6d841 | 367 | this->click_changed = true; |
862fc625 JM |
368 | this->idle_time = 0; |
369 | lcd->buzz(60, 300); // 50ms 300Hz | |
58d6d841 | 370 | return 0; |
35089dc7 JM |
371 | } |
372 | ||
862fc625 JM |
373 | uint32_t Panel::on_pause(uint32_t dummy) |
374 | { | |
375 | if (!paused) { | |
52500e58 | 376 | THEKERNEL->pauser->take(); |
862fc625 JM |
377 | paused = true; |
378 | } else { | |
52500e58 | 379 | THEKERNEL->pauser->release(); |
862fc625 | 380 | paused = false; |
52500e58 L |
381 | } |
382 | return 0; | |
383 | } | |
384 | ||
862fc625 JM |
385 | bool Panel::counter_change() |
386 | { | |
387 | if ( this->counter_changed ) { | |
388 | this->counter_changed = false; | |
389 | return true; | |
390 | } else { | |
391 | return false; | |
392 | } | |
393 | } | |
394 | bool Panel::click() | |
395 | { | |
396 | if ( this->click_changed ) { | |
397 | this->click_changed = false; | |
398 | return true; | |
399 | } else { | |
400 | return false; | |
401 | } | |
402 | } | |
35089dc7 JM |
403 | |
404 | ||
405 | // Enter menu mode | |
862fc625 JM |
406 | void Panel::enter_menu_mode() |
407 | { | |
35089dc7 JM |
408 | this->mode = MENU_MODE; |
409 | this->counter = &this->menu_selected_line; | |
410 | this->menu_changed = false; | |
411 | } | |
412 | ||
862fc625 JM |
413 | void Panel::setup_menu(uint16_t rows) |
414 | { | |
f65ce58f JM |
415 | this->setup_menu(rows, min(rows, this->max_screen_lines())); |
416 | } | |
417 | ||
862fc625 JM |
418 | void Panel::setup_menu(uint16_t rows, uint16_t lines) |
419 | { | |
35089dc7 | 420 | this->menu_selected_line = 0; |
862fc625 | 421 | this->menu_current_line = 0; |
399cb110 | 422 | this->menu_start_line = 0; |
35089dc7 | 423 | this->menu_rows = rows; |
206167cf | 424 | this->panel_lines = lines; |
35089dc7 JM |
425 | } |
426 | ||
862fc625 JM |
427 | void Panel::menu_update() |
428 | { | |
399cb110 | 429 | // Limits, up and down |
446deda2 | 430 | // NOTE menu_selected_line is changed in an interrupt and can change at any time |
862fc625 | 431 | int msl = this->menu_selected_line; // hopefully this is atomic |
206167cf JM |
432 | |
433 | #if 0 | |
434 | // this allows it to wrap but with new method we dont; want to wrap | |
862fc625 JM |
435 | msl = msl % ( this->menu_rows << this->menu_offset ); |
436 | while ( msl < 0 ) { | |
437 | msl += this->menu_rows << this->menu_offset; | |
438 | } | |
206167cf JM |
439 | #else |
440 | // limit selected line to screen lines available | |
441 | if(msl >= this->menu_rows<<this->menu_offset){ | |
442 | msl= (this->menu_rows-1)<<this->menu_offset; | |
443 | }else if(msl < 0) msl= 0; | |
444 | #endif | |
446deda2 | 445 | |
206167cf | 446 | this->menu_selected_line = msl; // update atomically we hope |
b6e81799 JM |
447 | // figure out which actual line to select, if we have a menu offset it means we want to move one line per two clicks |
448 | if(msl % (this->menu_offset+1) == 0) { // only if divisible by offset | |
449 | this->menu_current_line = msl >> this->menu_offset; | |
450 | } | |
35089dc7 JM |
451 | |
452 | // What to display | |
206167cf JM |
453 | if ( this->menu_rows > this->panel_lines ) { |
454 | #if 0 | |
455 | // old way of scrolling not nice.... | |
862fc625 | 456 | if ( this->menu_current_line >= 2 ) { |
446deda2 | 457 | this->menu_start_line = this->menu_current_line - 1; |
35089dc7 | 458 | } |
206167cf JM |
459 | if ( this->menu_current_line > this->menu_rows - this->panel_lines ) { |
460 | this->menu_start_line = this->menu_rows - this->panel_lines; | |
461 | } | |
462 | #else | |
463 | // new way we only scroll the lines when the cursor hits the bottom of the screen or the top of the screen | |
464 | // do we want to scroll up? | |
465 | int sl= this->menu_current_line - this->menu_start_line; // screen line we are on | |
466 | if(sl >= this->panel_lines) { | |
467 | this->menu_start_line += ((sl+1)-this->panel_lines); // scroll up to keep it on the screen | |
468 | ||
469 | }else if(sl < 0 ) { // do we want to scroll down? | |
470 | this->menu_start_line += sl; // scroll down | |
35089dc7 | 471 | } |
206167cf JM |
472 | #endif |
473 | ||
474 | }else{ | |
475 | this->menu_start_line = 0; | |
35089dc7 JM |
476 | } |
477 | ||
478 | this->menu_changed = true; | |
479 | } | |
480 | ||
862fc625 JM |
481 | bool Panel::menu_change() |
482 | { | |
483 | if ( this->menu_changed ) { | |
484 | this->menu_changed = false; | |
485 | return true; | |
486 | } else { | |
487 | return false; | |
488 | } | |
35089dc7 JM |
489 | } |
490 | ||
862fc625 JM |
491 | bool Panel::control_value_change() |
492 | { | |
493 | if ( this->control_value_changed ) { | |
494 | this->control_value_changed = false; | |
495 | return true; | |
496 | } else { | |
497 | return false; | |
498 | } | |
35089dc7 JM |
499 | } |
500 | ||
1ad23cd3 | 501 | bool Panel::enter_control_mode(float passed_normal_increment, float passed_pressed_increment) |
862fc625 | 502 | { |
35089dc7 JM |
503 | this->mode = CONTROL_MODE; |
504 | this->normal_increment = passed_normal_increment; | |
35089dc7 JM |
505 | this->counter = &this->control_normal_counter; |
506 | this->control_normal_counter = 0; | |
58d6d841 JM |
507 | this->control_base_value = 0; |
508 | return true; | |
35089dc7 JM |
509 | } |
510 | ||
862fc625 JM |
511 | void Panel::control_value_update() |
512 | { | |
58d6d841 JM |
513 | // TODO what do we do here? |
514 | this->control_value_changed = true; | |
35089dc7 JM |
515 | } |
516 | ||
1ad23cd3 | 517 | void Panel::set_control_value(float value) |
862fc625 | 518 | { |
58d6d841 | 519 | this->control_base_value = value; |
35089dc7 JM |
520 | } |
521 | ||
1ad23cd3 | 522 | float Panel::get_control_value() |
862fc625 JM |
523 | { |
524 | return this->control_base_value + (this->control_normal_counter * this->normal_increment); | |
35089dc7 JM |
525 | } |
526 | ||
862fc625 JM |
527 | bool Panel::is_playing() const |
528 | { | |
58d6d841 JM |
529 | void *returned_data; |
530 | ||
862fc625 JM |
531 | bool ok = THEKERNEL->public_data->get_value( player_checksum, is_playing_checksum, &returned_data ); |
532 | if (ok) { | |
533 | bool b = *static_cast<bool *>(returned_data); | |
58d6d841 JM |
534 | return b; |
535 | } | |
536 | return false; | |
7aa9fef3 JM |
537 | } |
538 | ||
862fc625 JM |
539 | void Panel::set_playing_file(string f) |
540 | { | |
7aa9fef3 | 541 | // just copy the first 20 characters after the first / if there |
862fc625 JM |
542 | size_t n = f.find_last_of('/'); |
543 | if (n == string::npos) n = 0; | |
544 | strncpy(playing_file, f.substr(n + 1, 19).c_str(), sizeof(playing_file)); | |
545 | playing_file[sizeof(playing_file) - 1] = 0; | |
7aa9fef3 | 546 | } |