= btLongerClick ) { prevState = state; return -2; }
163 | if( state == LOW && millis()-btTime >= btLongClick ) { prevState = state; return -1; }
164 | return 0;
165 | }
166 |
167 | int prevButtonState=0;
168 |
169 | int handleButton()
170 | {
171 | prevButtonState = buttonState;
172 | buttonState = checkButton();
173 | return buttonState;
174 | }
175 |
176 | // --------------------------------------------------------------------------
177 | void error(char *err, uint16_t col, int8 halt=1)
178 | {
179 | lcdSPI(); lcd.fillScreen(col);
180 | font.setColor(YELLOW);
181 | font.setFillRectFun(customRect);
182 | font.setBold(1);
183 | font.setSpacingY(4);
184 | font.printStr(4,4,err);
185 | font.setBold(0);
186 | if(halt) sd.errorHalt(err); else Serial.println(err);
187 | }
188 | // --------------------------------------------------------------------------
189 | int statMode=1; // 0-0ff, 1-progress, 2-full, 3-fps
190 |
191 | void darken(uint16_t *p)
192 | {
193 | uint16_t c = *p;
194 | int r,g,b;
195 | r = (c>>8)&0xf8;
196 | g = (c>>3)&0xfc;
197 | b = (c<<3)&0xf8;
198 | //*p = RGBto565(r/2,g/2,b/2); // blend to black
199 | *p = RGBto565(r+(60-r)/2,g+(60-g)/2,b+(60-b)/2); // blend to dark grey
200 | }
201 |
202 | void drawProgress(int y, int p)
203 | {
204 | int i;
205 | for(i=0; i=8 && statMode>0) {
244 | if(statMode==1) drawProgress(NLINES-8,140*(file.curPosition()/1000)/(file.fileSize()/1000)); else
245 | if(statMode>1) {
246 | font.setFillRectFun(customRectBuf);
247 | font.setColor(BLACK);
248 | font.printStr(1,NLINES-6,txt);
249 | font.printStr(2,NLINES-6,txt);
250 | font.printStr(3,NLINES-6,txt);
251 | font.printStr(1,NLINES-8,txt);
252 | font.printStr(2,NLINES-8,txt);
253 | font.printStr(3,NLINES-8,txt);
254 | font.printStr(1,NLINES-7,txt);
255 | font.printStr(3,NLINES-7,txt);
256 | font.setColor(YELLOW);
257 | font.printStr(2,NLINES-7,txt);
258 | }
259 | }
260 | statTime += millis()-statStartTime;
261 |
262 | lcdSPI();
263 | lcd.drawImage(0,i*nl,lcd.width(),nl,buf);
264 | }
265 | frTime = millis()-frTime-statTime;
266 | lcdTime = frTime-sdTime;
267 | if(buttonState>0) {
268 | if(++statMode>3) statMode=0;
269 | }
270 | if(statMode==2) snprintf(txt,30,"Fr/SD/LCD: %2ld/%2ld/%2ld FPS:%2d",frTime,sdTime,lcdTime,1000/frTime);
271 | if(statMode==3) snprintf(txt,30,"%2ld fps",1000/frTime);
272 |
273 | if(skipFr>0) file.seekCur(wd*ht*2*skipFr);
274 | if(handleButton()<0 && prevButtonState==0) break;
275 | }
276 | file.close();
277 | while(handleButton()==0);
278 | return 1;
279 | }
280 | // --------------------------------------------------------------------------
281 | // Limited to LCD resolution
282 | int showBMP(char *filename)
283 | {
284 | int bmpWd, bmpHt, bmpBits, bmpNumCols, y=0;
285 | uint16_t pal[256];
286 |
287 | sdSPI();
288 | if(!file.open(filename, O_CREAT | O_RDONLY)) {
289 | lcdSPI(); lcd.fillScreen(YELLOW);
290 | Serial.print(F("Cannot open "));
291 | Serial.println(filename);
292 | delay(1000);
293 | //return -1;
294 | }
295 | file.seekSet(0);
296 | file.read(buf,54);
297 | uint8_t *buf8 = (uint8_t *)buf;
298 | bmpWd = buf8[18]+buf8[19]*256;
299 | bmpHt = buf8[22]+buf8[23]*256;
300 | bmpBits = buf8[28];
301 | bmpNumCols = buf8[46]+buf8[47]*256;
302 | //Serial.print(bmpWd); Serial.print(" x "); Serial.print(bmpHt); Serial.print(" x "); Serial.print(bmpBits); Serial.print(" bpp");
303 | //if(bmpBits<=8) { Serial.print(" / "); Serial.print(bmpNumCols); Serial.print(" colors"); }
304 | //Serial.println();
305 | if(bmpBits<=8) {
306 | file.read(buf,bmpNumCols*4);
307 | for(int i=0;i>4];
315 | buf[i*2+1] = pal[buf8[i]&0xf];
316 | }
317 | } else
318 | if(bmpBits==8) {
319 | file.read(buf8,bmpWd);
320 | for(int i=0;i=15) sc=3; else // 15/2
339 | if(2*jht/lcd.height()>=7) sc=2; else // 7/2
340 | if(2*jht/lcd.height()>=3) sc=1; // 3/2
341 | int x=(lcd.width()-(jwd>>sc))/2;
342 | int y=(lcd.height()-(jht>>sc))/2;
343 | if(x<0) x=0;
344 | if(y<0) y=0;
345 | snprintf(txt,50,"%4d x %4d sc=%d [%s]",jwd,jht,sc,filename);
346 | Serial.println(txt);
347 | jpeg.show(x,y,filename,sc);
348 | while(handleButton()==0 || prevButtonState!=0);
349 | return 1;
350 | }
351 | // --------------------------------------------------------------------------
352 | int showTxt(char *filename)
353 | {
354 | sdSPI();
355 | if(!file.open(filename, O_CREAT | O_RDONLY)) {
356 | lcdSPI(); lcd.fillScreen(YELLOW);
357 | Serial.print(F("Cannot open "));
358 | Serial.println(filename);
359 | delay(1000);
360 | //return -1;
361 | }
362 | file.seekSet(0);
363 | while(file.available()) {
364 | int rd = file.read(buf,NLINES*BUF_WD*2);
365 | char *txt = (char*)buf;
366 | txt[rd-1]=0;
367 | lcdSPI();
368 | lcd.fillScreen(RGBto565(0,0,100));
369 | font.setColor(YELLOW);
370 | font.setSpacingY(1);
371 | font.setCR(1);
372 | font.printStr(0,0,txt);
373 | }
374 | file.close();
375 | while(handleButton()==0 || prevButtonState!=0);
376 | return 1;
377 | }
378 |
379 | // --------------------------------------------------------------------------
380 | char *getExt(char *filename)
381 | {
382 | int len = strlen(filename);
383 | return len>3 ? filename+len-3 : filename;
384 | }
385 |
386 | int checkExt(char *filename, char *ext)
387 | {
388 | return strcmp(getExt(filename),ext)==0;
389 | }
390 | // --------------------------------------------------------------------------
391 | // SD file browser data
392 | #define MAX_NAME_LEN 23
393 | #define MAX_DIR_LEN 50
394 | #define MAX_SIZE_LEN 5
395 | const int charWd = 6;
396 | const int lineHt = 12;
397 | const int numScreenFilesMax = 9;
398 | int numScreenFiles = 0;
399 | char filesList[numScreenFilesMax][MAX_NAME_LEN+1]; // last char filesList[MAX_NAME_LEN] is used as file/dir mode
400 | char filesSize[numScreenFilesMax][MAX_SIZE_LEN];
401 | int selFile = 0;
402 | int fileAvailable = 1;
403 | uint32_t dirPos;
404 | bool rootDir = true;
405 | char curDir[MAX_DIR_LEN];
406 | int ys = 14, xs = 2;
407 |
408 | void fileList(int rewind=0)
409 | {
410 | numScreenFiles = 0;
411 | selFile = 0;
412 | sdSPI();
413 | if(rewind || !fileAvailable) {
414 | dirPos = 0;
415 | if(!rootDir) {
416 | strcpy(filesList[numScreenFiles],"..");
417 | filesList[numScreenFiles][MAX_NAME_LEN] = 2;
418 | numScreenFiles++;
419 | }
420 | }
421 | sd.vwd()->seekSet(dirPos);
422 | while(numScreenFiles1000*1000*1000) snprintf(filesSize[numScreenFiles],MAX_SIZE_LEN,"%dG",fsize>>30);
439 | else if(fsize>1000*1000) snprintf(filesSize[numScreenFiles],MAX_SIZE_LEN,"%dM",fsize>>20);
440 | else if(fsize>1999) snprintf(filesSize[numScreenFiles],MAX_SIZE_LEN,"%dK",fsize>>10);
441 | else snprintf(filesSize[numScreenFiles],MAX_SIZE_LEN,"%dB",fsize);
442 | filesSize[numScreenFiles][MAX_SIZE_LEN-1] = 0;
443 | //Serial.print("\t\t"); Serial.println(filesSize[numScreenFiles]);
444 | }
445 | numScreenFiles++;
446 | file.close();
447 | }
448 | dirPos = sd.vwd()->curPosition();
449 | if(numScreenFiles>0) fileListShow();
450 | }
451 |
452 | // --------------------------------------------------------------------------
453 | uint16_t bgCol(int i)
454 | {
455 | return i&1 ? RGBto565(30,30,30) : RGBto565(50,50,50);
456 | }
457 |
458 | void selFrame(int i, uint16_t c)
459 | {
460 | lcdSPI();
461 | lcd.drawRect(0,ys+i*lineHt,(MAX_NAME_LEN-1)*charWd+4,lineHt,c);
462 | }
463 |
464 | void selFrameActive()
465 | {
466 | selFrame(selFile,filesList[selFile][MAX_NAME_LEN] ? GREEN : RED);
467 | }
468 |
469 | void fileListShow()
470 | {
471 | lcdSPI(); lcd.fillScreen(BLACK);
472 | font.setFillRectFun(customRect);
473 | lcd.drawFastHLine(0,0,lcd.width(),RGBto565(0,0,180));
474 | lcd.drawFastHLine(0,ys-1,lcd.width(),RGBto565(0,0,180));
475 | lcd.drawFastHLine(0,1,lcd.width(),RGBto565(0,0,200));
476 | lcd.drawFastHLine(0,ys-2,lcd.width(),RGBto565(0,0,200));
477 | lcd.drawFastHLine(0,2,lcd.width(),RGBto565(0,0,220));
478 | lcd.drawFastHLine(0,ys-3,lcd.width(),RGBto565(0,0,220));
479 | lcd.fillRect(0,3,lcd.width(),ys-6,BLUE);
480 | font.setColor(YELLOW);
481 | font.printStr(xs,3,curDir);
482 | for(int i=0;i0) {
540 | selFrame(selFile,bgCol(selFile));
541 | if(++selFile>=numScreenFiles) {
542 | fileList();
543 | if(numScreenFiles==0) fileList(1);
544 | }
545 | selFrameActive();
546 | }
547 | if(buttonState<0 && prevButtonState==0) {
548 | if(filesList[selFile][MAX_NAME_LEN]==2) {
549 | if(filesList[selFile][0]=='.') {
550 | char *last = strrchr(curDir,'/');
551 | if(last && last!=curDir) *last=0;
552 | last = strrchr(curDir,'/');
553 | if(last==curDir) rootDir = true;
554 | } else {
555 | rootDir = false;
556 | if(strlen(curDir)+strlen(filesList[selFile])+2