ft2_gui.c (30440B)
1 // for finding memory leaks in debug mode with Visual Studio 2 #if defined _DEBUG && defined _MSC_VER 3 #include <crtdbg.h> 4 #endif 5 6 #include <stdint.h> 7 #include "ft2_header.h" 8 #include "ft2_config.h" 9 #include "ft2_about.h" 10 #include "ft2_mouse.h" 11 #include "ft2_nibbles.h" 12 #include "ft2_gui.h" 13 #include "ft2_pattern_ed.h" 14 #include "scopes/ft2_scopes.h" 15 #include "ft2_help.h" 16 #include "ft2_sample_ed.h" 17 #include "ft2_inst_ed.h" 18 #include "ft2_diskop.h" 19 #include "ft2_wav_renderer.h" 20 #include "ft2_trim.h" 21 #include "ft2_video.h" 22 #include "ft2_tables.h" 23 #include "ft2_bmp.h" 24 #include "ft2_structs.h" 25 26 static void releaseMouseStates(void) 27 { 28 mouse.lastUsedObjectID = OBJECT_ID_NONE; 29 mouse.lastUsedObjectType = OBJECT_NONE; 30 mouse.leftButtonPressed = false; 31 mouse.leftButtonReleased = false; 32 mouse.rightButtonPressed = false; 33 mouse.rightButtonReleased = false; 34 mouse.firstTimePressingButton = false; 35 mouse.buttonCounter = 0; 36 mouse.lastX = 0; 37 mouse.lastY = 0; 38 ui.sampleDataOrLoopDrag = -1; 39 ui.leftLoopPinMoving = false; 40 ui.rightLoopPinMoving = false; 41 } 42 43 void unstuckLastUsedGUIElement(void) 44 { 45 /* If last object ID is OBJECT_ID_NONE, check if we moved 46 ** the sample data loop pins, and unstuck them if so. 47 */ 48 if (mouse.lastUsedObjectID == OBJECT_ID_NONE) 49 { 50 if (ui.leftLoopPinMoving) 51 { 52 setLeftLoopPinState(false); 53 ui.leftLoopPinMoving = false; 54 } 55 56 if (ui.rightLoopPinMoving) 57 { 58 setRightLoopPinState(false); 59 ui.rightLoopPinMoving = false; 60 } 61 62 releaseMouseStates(); 63 return; 64 } 65 66 switch (mouse.lastUsedObjectType) 67 { 68 default: break; 69 70 case OBJECT_PUSHBUTTON: 71 { 72 assert(mouse.lastUsedObjectID >= 0 && mouse.lastUsedObjectID < NUM_PUSHBUTTONS); 73 pushButton_t *p = &pushButtons[mouse.lastUsedObjectID]; 74 if (p->state == PUSHBUTTON_PRESSED) 75 { 76 p->state = PUSHBUTTON_UNPRESSED; 77 if (p->visible) 78 drawPushButton(mouse.lastUsedObjectID); 79 } 80 } 81 break; 82 83 case OBJECT_RADIOBUTTON: 84 { 85 assert(mouse.lastUsedObjectID >= 0 && mouse.lastUsedObjectID < NUM_RADIOBUTTONS); 86 radioButton_t *r = &radioButtons[mouse.lastUsedObjectID]; 87 if (r->state == RADIOBUTTON_PRESSED) 88 { 89 r->state = RADIOBUTTON_UNCHECKED; 90 if (r->visible) 91 drawRadioButton(mouse.lastUsedObjectID); 92 } 93 } 94 break; 95 96 case OBJECT_CHECKBOX: 97 { 98 assert(mouse.lastUsedObjectID >= 0 && mouse.lastUsedObjectID < NUM_CHECKBOXES); 99 checkBox_t *c = &checkBoxes[mouse.lastUsedObjectID]; 100 if (c->state == CHECKBOX_PRESSED) 101 { 102 c->state = CHECKBOX_UNPRESSED; 103 if (c->visible) 104 drawCheckBox(mouse.lastUsedObjectID); 105 } 106 } 107 break; 108 109 case OBJECT_SCROLLBAR: 110 { 111 assert(mouse.lastUsedObjectID >= 0 && mouse.lastUsedObjectID < NUM_SCROLLBARS); 112 scrollBar_t *s = &scrollBars[mouse.lastUsedObjectID]; 113 if (s->state == SCROLLBAR_PRESSED) 114 { 115 s->state = SCROLLBAR_UNPRESSED; 116 if (s->visible) 117 drawScrollBar(mouse.lastUsedObjectID); 118 } 119 } 120 break; 121 } 122 123 releaseMouseStates(); 124 } 125 126 bool setupGUI(void) 127 { 128 // all memory will be NULL-tested and free'd if we return false somewhere in this function 129 130 editor.tmpFilenameU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR)); 131 editor.tmpInstrFilenameU = (UNICHAR *)malloc((PATH_MAX + 1) * sizeof (UNICHAR)); 132 133 if (editor.tmpFilenameU == NULL || editor.tmpInstrFilenameU == NULL) 134 goto setupGUI_OOM; 135 136 editor.tmpFilenameU[0] = 0; 137 editor.tmpInstrFilenameU[0] = 0; 138 139 // set uninitialized GUI struct entries 140 141 textBox_t *t = &textBoxes[1]; // skip first entry, it's reserved for inputBox() 142 for (int32_t i = 1; i < NUM_TEXTBOXES; i++, t++) 143 { 144 t->visible = false; 145 t->bufOffset = 0; 146 t->cursorPos = 0; 147 t->textPtr = NULL; 148 t->renderBufW = (9 + 1) * t->maxChars; // 9 = max text glyph width (+1 for kerning) 149 t->renderBufH = 10; // 10 = max text glyph height 150 t->renderW = t->w - (t->tx * 2); 151 152 t->renderBuf = (uint8_t *)malloc(t->renderBufW * t->renderBufH * sizeof (int8_t)); 153 if (t->renderBuf == NULL) 154 goto setupGUI_OOM; 155 } 156 157 pushButton_t *p = pushButtons; 158 for (int32_t i = 0; i < NUM_PUSHBUTTONS; i++, p++) 159 { 160 p->state = 0; 161 p->visible = false; 162 163 if (i == PB_LOGO || i == PB_BADGE) 164 { 165 p->bitmapFlag = true; 166 } 167 else 168 { 169 p->bitmapFlag = false; 170 p->bitmapUnpressed = NULL; 171 p->bitmapPressed = NULL; 172 } 173 } 174 175 checkBox_t *c = checkBoxes; 176 for (int32_t i = 0; i < NUM_CHECKBOXES; i++, c++) 177 { 178 c->state = 0; 179 c->checked = false; 180 c->visible = false; 181 } 182 183 radioButton_t *r = radioButtons; 184 for (int32_t i = 0; i < NUM_RADIOBUTTONS; i++, r++) 185 { 186 r->state = 0; 187 r->visible = false; 188 } 189 190 scrollBar_t *s = scrollBars; 191 for (int32_t i = 0; i < NUM_SCROLLBARS; i++, s++) 192 { 193 s->visible = false; 194 s->state = 0; 195 s->pos = 0; 196 s->page = 0; 197 s->end = 0; 198 s->thumbX = 0; 199 s->thumbY = 0; 200 s->thumbW = 0; 201 s->thumbH = 0; 202 } 203 204 setPal16(palTable[config.cfg_StdPalNum], false); 205 initAboutScreen(); 206 setupInitialTextBoxPointers(); 207 setInitialTrimFlags(); 208 initializeScrollBars(); 209 setMouseMode(MOUSE_MODE_NORMAL); 210 updateTextBoxPointers(); 211 drawGUIOnRunTime(); 212 updateSampleEditorSample(); 213 updatePatternWidth(); 214 initFTHelp(); 215 216 return true; 217 218 setupGUI_OOM: 219 showErrorMsgBox("Not enough memory!"); 220 return false; 221 } 222 223 // TEXT ROUTINES 224 225 // returns full pixel width of a char/glyph 226 uint8_t charWidth(char ch) 227 { 228 return font1Widths[ch & 0x7F]; 229 } 230 231 // returns full pixel width of a char/glyph (big font) 232 uint8_t charWidth16(char ch) 233 { 234 return font2Widths[ch & 0x7F]; 235 } 236 237 // return full pixel width of a text string 238 uint16_t textWidth(const char *textPtr) 239 { 240 assert(textPtr != NULL); 241 242 uint16_t textWidth = 0; 243 while (*textPtr != '\0') 244 textWidth += charWidth(*textPtr++); 245 246 // there will be a pixel spacer at the end of the last char/glyph, remove it 247 if (textWidth > 0) 248 textWidth--; 249 250 return textWidth; 251 } 252 253 uint16_t textNWidth(const char *textPtr, int32_t length) 254 { 255 assert(textPtr != NULL); 256 257 uint16_t textWidth = 0; 258 for (int32_t i = 0; i < length; i++) 259 { 260 const char ch = textPtr[i]; 261 if (ch == '\0') 262 break; 263 264 textWidth += charWidth(ch); 265 } 266 267 // there will be a pixel spacer at the end of the last char/glyph, remove it 268 if (textWidth > 0) 269 textWidth--; 270 271 return textWidth; 272 } 273 274 // return full pixel width of a text string (big font) 275 uint16_t textWidth16(const char *textPtr) 276 { 277 assert(textPtr != NULL); 278 279 uint16_t textWidth = 0; 280 while (*textPtr != '\0') 281 textWidth += charWidth(*textPtr++); 282 283 // there will be a pixel spacer at the end of the last char/glyph, remove it 284 if (textWidth > 0) 285 textWidth--; 286 287 return textWidth; 288 } 289 290 void textOutTiny(int32_t xPos, int32_t yPos, char *str, uint32_t color) // A..Z/a..z and 0..9 291 { 292 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 293 while (*str != '\0') 294 { 295 char chr = *str++; 296 297 if (chr >= '0' && chr <= '9') 298 { 299 chr -= '0'; 300 } 301 else if (chr >= 'a' && chr <= 'z') 302 { 303 chr -= 'a'; 304 chr += 10; 305 } 306 else if (chr >= 'A' && chr <= 'Z') 307 { 308 chr -= 'A'; 309 chr += 10; 310 } 311 else 312 { 313 dstPtr += FONT3_CHAR_W; 314 continue; 315 } 316 317 const uint8_t *srcPtr = &bmp.font3[chr * FONT3_CHAR_W]; 318 for (int32_t y = 0; y < FONT3_CHAR_H; y++) 319 { 320 for (int32_t x = 0; x < FONT3_CHAR_W; x++) 321 { 322 if (srcPtr[x] != 0) 323 dstPtr[x] = color; 324 } 325 326 srcPtr += FONT3_WIDTH; 327 dstPtr += SCREEN_W; 328 } 329 330 dstPtr -= (SCREEN_W * FONT3_CHAR_H) - FONT3_CHAR_W; 331 } 332 } 333 334 void textOutTinyOutline(int32_t xPos, int32_t yPos, char *str) // A..Z/a..z and 0..9 335 { 336 const uint32_t bgColor = video.palette[PAL_BCKGRND]; 337 const uint32_t fgColor = video.palette[PAL_FORGRND]; 338 339 textOutTiny(xPos-1, yPos, str, bgColor); 340 textOutTiny(xPos, yPos-1, str, bgColor); 341 textOutTiny(xPos+1, yPos, str, bgColor); 342 textOutTiny(xPos, yPos+1, str, bgColor); 343 344 textOutTiny(xPos, yPos, str, fgColor); 345 } 346 347 void charOut(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, char chr) 348 { 349 assert(xPos < SCREEN_W && yPos < SCREEN_H); 350 351 if ((uint8_t)chr > 127+31) 352 chr = ' '; 353 354 chr &= 0x7F; // this is important to get the nordic glyphs in the font 355 if (chr == ' ') 356 return; 357 358 const uint32_t pixVal = video.palette[paletteIndex]; 359 const uint8_t *srcPtr = &bmp.font1[chr * FONT1_CHAR_W]; 360 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 361 362 for (uint32_t y = 0; y < FONT1_CHAR_H; y++) 363 { 364 for (uint32_t x = 0; x < FONT1_CHAR_W; x++) 365 { 366 if (srcPtr[x] != 0) 367 dstPtr[x] = pixVal; 368 } 369 370 srcPtr += FONT1_WIDTH; 371 dstPtr += SCREEN_W; 372 } 373 } 374 375 void charOutBg(uint16_t xPos, uint16_t yPos, uint8_t fgPalette, uint8_t bgPalette, char chr) 376 { 377 assert(xPos < SCREEN_W && yPos < SCREEN_H); 378 379 if ((uint8_t)chr > 127+31) 380 chr = ' '; 381 382 chr &= 0x7F; // this is important to get the nordic glyphs in the font 383 if (chr == ' ') 384 return; 385 386 const uint32_t fg = video.palette[fgPalette]; 387 const uint32_t bg = video.palette[bgPalette]; 388 389 const uint8_t *srcPtr = &bmp.font1[chr * FONT1_CHAR_W]; 390 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 391 392 for (int32_t y = 0; y < FONT1_CHAR_H; y++) 393 { 394 for (int32_t x = 0; x < FONT1_CHAR_W-1; x++) 395 dstPtr[x] = srcPtr[x] ? fg : bg; 396 397 srcPtr += FONT1_WIDTH; 398 dstPtr += SCREEN_W; 399 } 400 } 401 402 void charOutOutlined(uint16_t x, uint16_t y, uint8_t paletteIndex, char chr) 403 { 404 charOut(x - 1, y, PAL_BCKGRND, chr); 405 charOut(x + 1, y, PAL_BCKGRND, chr); 406 charOut(x, y - 1, PAL_BCKGRND, chr); 407 charOut(x, y + 1, PAL_BCKGRND, chr); 408 409 charOut(x, y, paletteIndex, chr); 410 } 411 412 void charOutShadow(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, uint8_t shadowPaletteIndex, char chr) 413 { 414 assert(xPos < SCREEN_W && yPos < SCREEN_H); 415 416 if ((uint8_t)chr > 127+31) 417 chr = ' '; 418 419 chr &= 0x7F; // this is important to get the nordic glyphs in the font 420 if (chr == ' ') 421 return; 422 423 const uint32_t pixVal1 = video.palette[paletteIndex]; 424 const uint32_t pixVal2 = video.palette[shadowPaletteIndex]; 425 const uint8_t *srcPtr = &bmp.font1[chr * FONT1_CHAR_W]; 426 uint32_t *dstPtr1 = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 427 uint32_t *dstPtr2 = dstPtr1 + (SCREEN_W+1); 428 429 for (int32_t y = 0; y < FONT1_CHAR_H; y++) 430 { 431 for (int32_t x = 0; x < FONT1_CHAR_W; x++) 432 { 433 if (srcPtr[x] != 0) 434 { 435 dstPtr2[x] = pixVal2; 436 dstPtr1[x] = pixVal1; 437 } 438 } 439 440 srcPtr += FONT1_WIDTH; 441 dstPtr1 += SCREEN_W; 442 dstPtr2 += SCREEN_W; 443 } 444 } 445 446 void charOutClipX(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, char chr, uint16_t clipX) 447 { 448 assert(xPos < SCREEN_W && yPos < SCREEN_H); 449 450 if (xPos > clipX) 451 return; 452 453 if ((uint8_t)chr > 127+31) 454 chr = ' '; 455 456 chr &= 0x7F; // this is important to get the nordic glyphs in the font 457 if (chr == ' ') 458 return; 459 460 const uint32_t pixVal = video.palette[paletteIndex]; 461 const uint8_t *srcPtr = &bmp.font1[chr * FONT1_CHAR_W]; 462 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 463 464 int32_t width = FONT1_CHAR_W; 465 if (xPos+width > clipX) 466 width = FONT1_CHAR_W - ((xPos + width) - clipX); 467 468 for (int32_t y = 0; y < FONT1_CHAR_H; y++) 469 { 470 for (int32_t x = 0; x < width; x++) 471 { 472 if (srcPtr[x] != 0) 473 dstPtr[x] = pixVal; 474 } 475 476 srcPtr += FONT1_WIDTH; 477 dstPtr += SCREEN_W; 478 } 479 } 480 481 void bigCharOut(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, char chr) 482 { 483 assert(xPos < SCREEN_W && yPos < SCREEN_H); 484 485 if ((uint8_t)chr > 127+31) 486 chr = ' '; 487 488 chr &= 0x7F; // this is important to get the nordic glyphs in the font 489 if (chr == ' ') 490 return; 491 492 const uint8_t *srcPtr = &bmp.font2[chr * FONT2_CHAR_W]; 493 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 494 const uint32_t pixVal = video.palette[paletteIndex]; 495 496 for (int32_t y = 0; y < FONT2_CHAR_H; y++) 497 { 498 for (int32_t x = 0; x < FONT2_CHAR_W; x++) 499 { 500 if (srcPtr[x] != 0) 501 dstPtr[x] = pixVal; 502 } 503 504 srcPtr += FONT2_WIDTH; 505 dstPtr += SCREEN_W; 506 } 507 } 508 509 static void bigCharOutShadow(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, uint8_t shadowPaletteIndex, char chr) 510 { 511 assert(xPos < SCREEN_W && yPos < SCREEN_H); 512 513 if ((uint8_t)chr > 127+31) 514 chr = ' '; 515 516 chr &= 0x7F; // this is important to get the nordic glyphs in the font 517 if (chr == ' ') 518 return; 519 520 const uint32_t pixVal1 = video.palette[paletteIndex]; 521 const uint32_t pixVal2 = video.palette[shadowPaletteIndex]; 522 const uint8_t *srcPtr = &bmp.font2[chr * FONT2_CHAR_W]; 523 uint32_t *dstPtr1 = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 524 uint32_t *dstPtr2 = dstPtr1 + (SCREEN_W+1); 525 526 for (int32_t y = 0; y < FONT2_CHAR_H; y++) 527 { 528 for (int32_t x = 0; x < FONT2_CHAR_W; x++) 529 { 530 if (srcPtr[x] != 0) 531 { 532 dstPtr2[x] = pixVal2; 533 dstPtr1[x] = pixVal1; 534 } 535 } 536 537 srcPtr += FONT2_WIDTH; 538 dstPtr1 += SCREEN_W; 539 dstPtr2 += SCREEN_W; 540 } 541 } 542 543 void textOut(uint16_t x, uint16_t y, uint8_t paletteIndex, const char *textPtr) 544 { 545 assert(textPtr != NULL); 546 547 uint16_t currX = x; 548 while (true) 549 { 550 const char chr = *textPtr++; 551 if (chr == '\0') 552 break; 553 554 charOut(currX, y, paletteIndex, chr); 555 currX += charWidth(chr); 556 } 557 } 558 559 void textOutBorder(uint16_t x, uint16_t y, uint8_t paletteIndex, uint8_t borderPaletteIndex, const char *textPtr) 560 { 561 textOut(x, y-1, borderPaletteIndex, textPtr); // top 562 textOut(x+1, y, borderPaletteIndex, textPtr); // right 563 textOut(x, y+1, borderPaletteIndex, textPtr); // bottom 564 textOut(x-1, y, borderPaletteIndex, textPtr); // left 565 566 textOut(x, y, paletteIndex, textPtr); 567 } 568 569 // fixed width 570 void textOutFixed(uint16_t x, uint16_t y, uint8_t fgPaltete, uint8_t bgPalette, const char *textPtr) 571 { 572 assert(textPtr != NULL); 573 574 uint16_t currX = x; 575 while (true) 576 { 577 const char chr = *textPtr++; 578 if (chr == '\0') 579 break; 580 581 charOutBg(currX, y, fgPaltete, bgPalette, chr); 582 currX += FONT1_CHAR_W-1; 583 } 584 } 585 586 void textOutShadow(uint16_t x, uint16_t y, uint8_t paletteIndex, uint8_t shadowPaletteIndex, const char *textPtr) 587 { 588 assert(textPtr != NULL); 589 590 uint16_t currX = x; 591 while (true) 592 { 593 const char chr = *textPtr++; 594 if (chr == '\0') 595 break; 596 597 charOutShadow(currX, y, paletteIndex, shadowPaletteIndex, chr); 598 currX += charWidth(chr); 599 } 600 } 601 602 void bigTextOut(uint16_t x, uint16_t y, uint8_t paletteIndex, const char *textPtr) 603 { 604 assert(textPtr != NULL); 605 606 uint16_t currX = x; 607 while (true) 608 { 609 const char chr = *textPtr++; 610 if (chr == '\0') 611 break; 612 613 bigCharOut(currX, y, paletteIndex, chr); 614 currX += charWidth16(chr); 615 } 616 } 617 618 void bigTextOutShadow(uint16_t x, uint16_t y, uint8_t paletteIndex, uint8_t shadowPaletteIndex, const char *textPtr) 619 { 620 assert(textPtr != NULL); 621 622 uint16_t currX = x; 623 while (true) 624 { 625 const char chr = *textPtr++; 626 if (chr == '\0') 627 break; 628 629 bigCharOutShadow(currX, y, paletteIndex, shadowPaletteIndex, chr); 630 currX += charWidth16(chr); 631 } 632 } 633 634 void textOutClipX(uint16_t x, uint16_t y, uint8_t paletteIndex, const char *textPtr, uint16_t clipX) 635 { 636 assert(textPtr != NULL); 637 638 uint16_t currX = x; 639 while (true) 640 { 641 const char chr = *textPtr++; 642 if (chr == '\0') 643 break; 644 645 charOutClipX(currX, y, paletteIndex, chr, clipX); 646 647 currX += charWidth(chr); 648 if (currX >= clipX) 649 break; 650 } 651 } 652 653 void hexOut(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, uint32_t val, uint8_t numDigits) 654 { 655 assert(xPos < SCREEN_W && yPos < SCREEN_H); 656 657 const uint32_t pixVal = video.palette[paletteIndex]; 658 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 659 660 for (int32_t i = numDigits-1; i >= 0; i--) 661 { 662 const uint8_t *srcPtr = &bmp.font6[((val >> (i * 4)) & 15) * FONT6_CHAR_W]; 663 664 // render glyph 665 for (int32_t y = 0; y < FONT6_CHAR_H; y++) 666 { 667 for (int32_t x = 0; x < FONT6_CHAR_W; x++) 668 { 669 if (srcPtr[x] != 0) 670 dstPtr[x] = pixVal; 671 } 672 673 srcPtr += FONT6_WIDTH; 674 dstPtr += SCREEN_W; 675 } 676 677 dstPtr -= (SCREEN_W * FONT6_CHAR_H) - FONT6_CHAR_W; // xpos += FONT6_CHAR_W 678 } 679 } 680 681 void hexOutBg(uint16_t xPos, uint16_t yPos, uint8_t fgPalette, uint8_t bgPalette, uint32_t val, uint8_t numDigits) 682 { 683 assert(xPos < SCREEN_W && yPos < SCREEN_H); 684 685 const uint32_t fg = video.palette[fgPalette]; 686 const uint32_t bg = video.palette[bgPalette]; 687 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 688 689 for (int32_t i = numDigits-1; i >= 0; i--) 690 { 691 // extract current nybble and set pointer to glyph 692 const uint8_t *srcPtr = &bmp.font6[((val >> (i * 4)) & 15) * FONT6_CHAR_W]; 693 694 // render glyph 695 for (int32_t y = 0; y < FONT6_CHAR_H; y++) 696 { 697 for (int32_t x = 0; x < FONT6_CHAR_W; x++) 698 dstPtr[x] = srcPtr[x] ? fg : bg; 699 700 srcPtr += FONT6_WIDTH; 701 dstPtr += SCREEN_W; 702 } 703 704 dstPtr -= (SCREEN_W * FONT6_CHAR_H) - FONT6_CHAR_W; // xpos += FONT6_CHAR_W 705 } 706 } 707 708 void hexOutShadow(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, uint8_t shadowPaletteIndex, uint32_t val, uint8_t numDigits) 709 { 710 hexOut(xPos + 1, yPos + 1, shadowPaletteIndex, val, numDigits); 711 hexOut(xPos + 0, yPos + 0, paletteIndex, val, numDigits); 712 } 713 714 // FILL ROUTINES 715 716 void clearRect(uint16_t xPos, uint16_t yPos, uint16_t w, uint16_t h) 717 { 718 assert(xPos < SCREEN_W && yPos < SCREEN_H && (xPos + w) <= SCREEN_W && (yPos + h) <= SCREEN_H); 719 720 const uint32_t pitch = w * sizeof (int32_t); 721 722 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 723 for (int32_t y = 0; y < h; y++, dstPtr += SCREEN_W) 724 memset(dstPtr, 0, pitch); 725 } 726 727 void fillRect(uint16_t xPos, uint16_t yPos, uint16_t w, uint16_t h, uint8_t paletteIndex) 728 { 729 assert(xPos < SCREEN_W && yPos < SCREEN_H && (xPos + w) <= SCREEN_W && (yPos + h) <= SCREEN_H); 730 731 const uint32_t pixVal = video.palette[paletteIndex]; 732 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 733 734 for (int32_t y = 0; y < h; y++) 735 { 736 for (int32_t x = 0; x < w; x++) 737 dstPtr[x] = pixVal; 738 739 dstPtr += SCREEN_W; 740 } 741 } 742 743 void blit32(uint16_t xPos, uint16_t yPos, const uint32_t *srcPtr, uint16_t w, uint16_t h) 744 { 745 assert(srcPtr != NULL && xPos < SCREEN_W && yPos < SCREEN_H && (xPos + w) <= SCREEN_W && (yPos + h) <= SCREEN_H); 746 747 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 748 for (int32_t y = 0; y < h; y++) 749 { 750 for (int32_t x = 0; x < w; x++) 751 { 752 if (srcPtr[x] != 0x00FF00) 753 dstPtr[x] = srcPtr[x] | 0xFF000000; // most significant 8 bits = palette number. 0xFF because no true palette 754 } 755 756 srcPtr += w; 757 dstPtr += SCREEN_W; 758 } 759 } 760 761 void blit(uint16_t xPos, uint16_t yPos, const uint8_t *srcPtr, uint16_t w, uint16_t h) 762 { 763 assert(srcPtr != NULL && xPos < SCREEN_W && yPos < SCREEN_H && (xPos + w) <= SCREEN_W && (yPos + h) <= SCREEN_H); 764 765 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 766 for (int32_t y = 0; y < h; y++) 767 { 768 for (int32_t x = 0; x < w; x++) 769 { 770 const uint32_t pixel = srcPtr[x]; 771 if (pixel != PAL_TRANSPR) 772 dstPtr[x] = video.palette[pixel]; 773 } 774 775 srcPtr += w; 776 dstPtr += SCREEN_W; 777 } 778 } 779 780 void blitClipX(uint16_t xPos, uint16_t yPos, const uint8_t *srcPtr, uint16_t w, uint16_t h, uint16_t clipX) 781 { 782 if (clipX > w) 783 clipX = w; 784 785 assert(srcPtr != NULL && xPos < SCREEN_W && yPos < SCREEN_H && (xPos + clipX) <= SCREEN_W && (yPos + h) <= SCREEN_H); 786 787 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 788 for (int32_t y = 0; y < h; y++) 789 { 790 for (int32_t x = 0; x < clipX; x++) 791 { 792 const uint32_t pixel = srcPtr[x]; 793 if (pixel != PAL_TRANSPR) 794 dstPtr[x] = video.palette[pixel]; 795 } 796 797 srcPtr += w; 798 dstPtr += SCREEN_W; 799 } 800 } 801 802 void blitFast(uint16_t xPos, uint16_t yPos, const uint8_t *srcPtr, uint16_t w, uint16_t h) // no transparency/colorkey 803 { 804 assert(srcPtr != NULL && xPos < SCREEN_W && yPos < SCREEN_H && (xPos + w) <= SCREEN_W && (yPos + h) <= SCREEN_H); 805 806 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 807 for (int32_t y = 0; y < h; y++) 808 { 809 for (int32_t x = 0; x < w; x++) 810 dstPtr[x] = video.palette[srcPtr[x]]; 811 812 srcPtr += w; 813 dstPtr += SCREEN_W; 814 } 815 } 816 817 void blitFastClipX(uint16_t xPos, uint16_t yPos, const uint8_t *srcPtr, uint16_t w, uint16_t h, uint16_t clipX) // no transparency/colorkey 818 { 819 if (clipX > w) 820 clipX = w; 821 822 assert(srcPtr != NULL && xPos < SCREEN_W && yPos < SCREEN_H && (xPos + clipX) <= SCREEN_W && (yPos + h) <= SCREEN_H); 823 824 uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos]; 825 for (int32_t y = 0; y < h; y++) 826 { 827 for (int32_t x = 0; x < clipX; x++) 828 dstPtr[x] = video.palette[srcPtr[x]]; 829 830 srcPtr += w; 831 dstPtr += SCREEN_W; 832 } 833 } 834 835 // LINE ROUTINES 836 837 void hLine(uint16_t x, uint16_t y, uint16_t w, uint8_t paletteIndex) 838 { 839 assert(x < SCREEN_W && y < SCREEN_H && (x + w) <= SCREEN_W); 840 841 const uint32_t pixVal = video.palette[paletteIndex]; 842 843 uint32_t *dstPtr = &video.frameBuffer[(y * SCREEN_W) + x]; 844 for (int32_t i = 0; i < w; i++) 845 dstPtr[i] = pixVal; 846 } 847 848 void vLine(uint16_t x, uint16_t y, uint16_t h, uint8_t paletteIndex) 849 { 850 assert(x < SCREEN_W && y < SCREEN_H && (y + h) <= SCREEN_W); 851 852 const uint32_t pixVal = video.palette[paletteIndex]; 853 854 uint32_t *dstPtr = &video.frameBuffer[(y * SCREEN_W) + x]; 855 for (int32_t i = 0; i < h; i++) 856 { 857 *dstPtr = pixVal; 858 dstPtr += SCREEN_W; 859 } 860 } 861 862 void hLineDouble(uint16_t x, uint16_t y, uint16_t w, uint8_t paletteIndex) 863 { 864 hLine(x, y, w, paletteIndex); 865 hLine(x, y+1, w, paletteIndex); 866 } 867 868 void vLineDouble(uint16_t x, uint16_t y, uint16_t h, uint8_t paletteIndex) 869 { 870 vLine(x, y, h, paletteIndex); 871 vLine(x+1, y, h, paletteIndex); 872 } 873 874 void line(int16_t x1, int16_t x2, int16_t y1, int16_t y2, uint8_t paletteIndex) 875 { 876 const int16_t dx = x2 - x1; 877 const uint16_t ax = ABS(dx) * 2; 878 const int16_t sx = SGN(dx); 879 const int16_t dy = y2 - y1; 880 const uint16_t ay = ABS(dy) * 2; 881 const int16_t sy = SGN(dy); 882 int16_t x = x1; 883 int16_t y = y1; 884 885 uint32_t pixVal = video.palette[paletteIndex]; 886 const int32_t pitch = sy * SCREEN_W; 887 uint32_t *dst32 = &video.frameBuffer[(y * SCREEN_W) + x]; 888 889 // draw line 890 if (ax > ay) 891 { 892 int16_t d = ay - (ax >> 1); 893 while (true) 894 { 895 *dst32 = pixVal; 896 if (x == x2) 897 break; 898 899 if (d >= 0) 900 { 901 d -= ax; 902 dst32 += pitch; 903 } 904 905 x += sx; 906 d += ay; 907 dst32 += sx; 908 } 909 } 910 else 911 { 912 int16_t d = ax - (ay >> 1); 913 while (true) 914 { 915 *dst32 = pixVal; 916 if (y == y2) 917 break; 918 919 if (d >= 0) 920 { 921 d -= ay; 922 dst32 += sx; 923 } 924 925 y += sy; 926 d += ax; 927 dst32 += pitch; 928 } 929 } 930 } 931 932 void drawFramework(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t type) 933 { 934 assert(x < SCREEN_W && y < SCREEN_H && w >= 2 && h >= h); 935 936 h--; 937 w--; 938 939 if (type == FRAMEWORK_TYPE1) 940 { 941 // top left corner 942 hLine(x, y, w, PAL_DSKTOP1); 943 vLine(x, y + 1, h - 1, PAL_DSKTOP1); 944 945 // bottom right corner 946 hLine(x, y + h, w, PAL_DSKTOP2); 947 vLine(x + w, y, h + 1, PAL_DSKTOP2); 948 949 // fill background 950 fillRect(x + 1, y + 1, w - 1, h - 1, PAL_DESKTOP); 951 } 952 else 953 { 954 // top left corner 955 hLine(x, y, w + 1, PAL_DSKTOP2); 956 vLine(x, y + 1, h, PAL_DSKTOP2); 957 958 // bottom right corner 959 hLine(x + 1, y + h, w, PAL_DSKTOP1); 960 vLine(x + w, y + 1, h - 1, PAL_DSKTOP1); 961 962 // clear background 963 clearRect(x + 1, y + 1, w - 1, h - 1); 964 } 965 } 966 967 // GUI FUNCTIONS 968 969 void showTopLeftMainScreen(bool restoreScreens) 970 { 971 ui.diskOpShown = false; 972 ui.sampleEditorExtShown = false; 973 ui.instEditorExtShown = false; 974 ui.transposeShown = false; 975 ui.advEditShown = false; 976 ui.wavRendererShown = false; 977 ui.trimScreenShown = false; 978 979 ui.scopesShown = true; 980 if (restoreScreens) 981 { 982 switch (ui.oldTopLeftScreen) 983 { 984 default: break; 985 case 1: ui.diskOpShown = true; break; 986 case 2: ui.sampleEditorExtShown = true; break; 987 case 3: ui.instEditorExtShown = true; break; 988 case 4: ui.transposeShown = true; break; 989 case 5: ui.advEditShown = true; break; 990 case 6: ui.wavRendererShown = true; break; 991 case 7: ui.trimScreenShown = true; break; 992 } 993 994 if (ui.oldTopLeftScreen > 0) 995 ui.scopesShown = false; 996 } 997 998 ui.oldTopLeftScreen = 0; 999 1000 if (ui.diskOpShown) 1001 { 1002 showDiskOpScreen(); 1003 } 1004 else 1005 { 1006 // pos ed. 1007 drawFramework(0, 0, 112, 77, FRAMEWORK_TYPE1); 1008 drawFramework(2, 2, 51, 19, FRAMEWORK_TYPE2); 1009 drawFramework(2,30, 51, 19, FRAMEWORK_TYPE2); 1010 showScrollBar(SB_POS_ED); 1011 showPushButton(PB_POSED_POS_UP); 1012 showPushButton(PB_POSED_POS_DOWN); 1013 showPushButton(PB_POSED_INS); 1014 showPushButton(PB_POSED_PATT_UP); 1015 showPushButton(PB_POSED_PATT_DOWN); 1016 showPushButton(PB_POSED_DEL); 1017 showPushButton(PB_POSED_LEN_UP); 1018 showPushButton(PB_POSED_LEN_DOWN); 1019 showPushButton(PB_POSED_REP_UP); 1020 showPushButton(PB_POSED_REP_DOWN); 1021 textOutShadow(4, 52, PAL_FORGRND, PAL_DSKTOP2, "Songlen."); 1022 textOutShadow(4, 64, PAL_FORGRND, PAL_DSKTOP2, "Repstart"); 1023 drawPosEdNums(song.songPos); 1024 drawSongLength(); 1025 drawSongLoopStart(); 1026 1027 // logo button 1028 showPushButton(PB_LOGO); 1029 showPushButton(PB_BADGE); 1030 1031 // left menu 1032 drawFramework(291, 0, 65, 173, FRAMEWORK_TYPE1); 1033 showPushButton(PB_ABOUT); 1034 showPushButton(PB_NIBBLES); 1035 showPushButton(PB_KILL); 1036 showPushButton(PB_TRIM); 1037 showPushButton(PB_EXTEND_VIEW); 1038 showPushButton(PB_TRANSPOSE); 1039 showPushButton(PB_INST_ED_EXT); 1040 showPushButton(PB_SMP_ED_EXT); 1041 showPushButton(PB_ADV_EDIT); 1042 showPushButton(PB_ADD_CHANNELS); 1043 showPushButton(PB_SUB_CHANNELS); 1044 1045 // song/pattern 1046 drawFramework(112, 32, 94, 45, FRAMEWORK_TYPE1); 1047 drawFramework(206, 32, 85, 45, FRAMEWORK_TYPE1); 1048 showPushButton(PB_BPM_UP); 1049 showPushButton(PB_BPM_DOWN); 1050 showPushButton(PB_SPEED_UP); 1051 showPushButton(PB_SPEED_DOWN); 1052 showPushButton(PB_EDITADD_UP); 1053 showPushButton(PB_EDITADD_DOWN); 1054 showPushButton(PB_PATT_UP); 1055 showPushButton(PB_PATT_DOWN); 1056 showPushButton(PB_PATTLEN_UP); 1057 showPushButton(PB_PATTLEN_DOWN); 1058 showPushButton(PB_PATT_EXPAND); 1059 showPushButton(PB_PATT_SHRINK); 1060 textOutShadow(116, 36, PAL_FORGRND, PAL_DSKTOP2, "BPM"); 1061 textOutShadow(116, 50, PAL_FORGRND, PAL_DSKTOP2, "Spd."); 1062 textOutShadow(116, 64, PAL_FORGRND, PAL_DSKTOP2, "Add."); 1063 textOutShadow(210, 36, PAL_FORGRND, PAL_DSKTOP2, "Ptn."); 1064 textOutShadow(210, 50, PAL_FORGRND, PAL_DSKTOP2, "Ln."); 1065 drawSongBPM(song.BPM); 1066 drawSongSpeed(song.speed); 1067 drawEditPattern(editor.editPattern); 1068 drawPatternLength(editor.editPattern); 1069 drawIDAdd(); 1070 1071 // status bar 1072 drawFramework(0, 77, 291, 15, FRAMEWORK_TYPE1); 1073 textOutShadow(4, 80, PAL_FORGRND, PAL_DSKTOP2, "Global volume"); 1074 drawGlobalVol(song.globalVolume); 1075 1076 ui.updatePosSections = true; 1077 1078 textOutShadow(204, 80, PAL_FORGRND, PAL_DSKTOP2, "Time"); 1079 charOutShadow(250, 80, PAL_FORGRND, PAL_DSKTOP2, ':'); 1080 charOutShadow(270, 80, PAL_FORGRND, PAL_DSKTOP2, ':'); 1081 1082 drawPlaybackTime(); 1083 1084 if (ui.sampleEditorExtShown) drawSampleEditorExt(); 1085 else if (ui.instEditorExtShown) drawInstEditorExt(); 1086 else if (ui.transposeShown) drawTranspose(); 1087 else if (ui.advEditShown) drawAdvEdit(); 1088 else if (ui.wavRendererShown) drawWavRenderer(); 1089 else if (ui.trimScreenShown) drawTrimScreen(); 1090 1091 if (ui.scopesShown) 1092 drawScopeFramework(); 1093 } 1094 } 1095 1096 void hideTopLeftMainScreen(void) 1097 { 1098 hideDiskOpScreen(); 1099 hideInstEditorExt(); 1100 hideSampleEditorExt(); 1101 hideTranspose(); 1102 hideAdvEdit(); 1103 hideWavRenderer(); 1104 hideTrimScreen(); 1105 1106 ui.scopesShown = false; 1107 1108 // position editor 1109 hideScrollBar(SB_POS_ED); 1110 1111 hidePushButton(PB_POSED_POS_UP); 1112 hidePushButton(PB_POSED_POS_DOWN); 1113 hidePushButton(PB_POSED_INS); 1114 hidePushButton(PB_POSED_PATT_UP); 1115 hidePushButton(PB_POSED_PATT_DOWN); 1116 hidePushButton(PB_POSED_DEL); 1117 hidePushButton(PB_POSED_LEN_UP); 1118 hidePushButton(PB_POSED_LEN_DOWN); 1119 hidePushButton(PB_POSED_REP_UP); 1120 hidePushButton(PB_POSED_REP_DOWN); 1121 1122 // logo button 1123 hidePushButton(PB_LOGO); 1124 hidePushButton(PB_BADGE); 1125 1126 // left menu 1127 hidePushButton(PB_ABOUT); 1128 hidePushButton(PB_NIBBLES); 1129 hidePushButton(PB_KILL); 1130 hidePushButton(PB_TRIM); 1131 hidePushButton(PB_EXTEND_VIEW); 1132 hidePushButton(PB_TRANSPOSE); 1133 hidePushButton(PB_INST_ED_EXT); 1134 hidePushButton(PB_SMP_ED_EXT); 1135 hidePushButton(PB_ADV_EDIT); 1136 hidePushButton(PB_ADD_CHANNELS); 1137 hidePushButton(PB_SUB_CHANNELS); 1138 1139 // song/pattern 1140 hidePushButton(PB_BPM_UP); 1141 hidePushButton(PB_BPM_DOWN); 1142 hidePushButton(PB_SPEED_UP); 1143 hidePushButton(PB_SPEED_DOWN); 1144 hidePushButton(PB_EDITADD_UP); 1145 hidePushButton(PB_EDITADD_DOWN); 1146 hidePushButton(PB_PATT_UP); 1147 hidePushButton(PB_PATT_DOWN); 1148 hidePushButton(PB_PATTLEN_UP); 1149 hidePushButton(PB_PATTLEN_DOWN); 1150 hidePushButton(PB_PATT_EXPAND); 1151 hidePushButton(PB_PATT_SHRINK); 1152 } 1153 1154 void showTopRightMainScreen(void) 1155 { 1156 // right menu 1157 drawFramework(356, 0, 65, 173, FRAMEWORK_TYPE1); 1158 showPushButton(PB_PLAY_SONG); 1159 showPushButton(PB_PLAY_PATT); 1160 showPushButton(PB_STOP); 1161 showPushButton(PB_RECORD_SONG); 1162 showPushButton(PB_RECORD_PATT); 1163 showPushButton(PB_DISK_OP); 1164 showPushButton(PB_INST_ED); 1165 showPushButton(PB_SMP_ED); 1166 showPushButton(PB_CONFIG); 1167 showPushButton(PB_HELP); 1168 1169 // instrument switcher 1170 ui.instrSwitcherShown = true; 1171 showInstrumentSwitcher(); 1172 1173 // song name 1174 showTextBox(TB_SONG_NAME); 1175 drawSongName(); 1176 } 1177 1178 void hideTopRightMainScreen(void) 1179 { 1180 // right menu 1181 hidePushButton(PB_PLAY_SONG); 1182 hidePushButton(PB_PLAY_PATT); 1183 hidePushButton(PB_STOP); 1184 hidePushButton(PB_RECORD_SONG); 1185 hidePushButton(PB_RECORD_PATT); 1186 hidePushButton(PB_DISK_OP); 1187 hidePushButton(PB_INST_ED); 1188 hidePushButton(PB_SMP_ED); 1189 hidePushButton(PB_CONFIG); 1190 hidePushButton(PB_HELP); 1191 1192 // instrument switcher 1193 hideInstrumentSwitcher(); 1194 ui.instrSwitcherShown = false; 1195 1196 hideTextBox(TB_SONG_NAME); 1197 } 1198 1199 // BOTTOM STUFF 1200 1201 void setOldTopLeftScreenFlag(void) 1202 { 1203 if (ui.diskOpShown) ui.oldTopLeftScreen = 1; 1204 else if (ui.sampleEditorExtShown) ui.oldTopLeftScreen = 2; 1205 else if (ui.instEditorExtShown) ui.oldTopLeftScreen = 3; 1206 else if (ui.transposeShown) ui.oldTopLeftScreen = 4; 1207 else if (ui.advEditShown) ui.oldTopLeftScreen = 5; 1208 else if (ui.wavRendererShown) ui.oldTopLeftScreen = 6; 1209 else if (ui.trimScreenShown) ui.oldTopLeftScreen = 7; 1210 } 1211 1212 void hideTopLeftScreen(void) 1213 { 1214 setOldTopLeftScreenFlag(); 1215 1216 hideTopLeftMainScreen(); 1217 hideNibblesScreen(); 1218 hideConfigScreen(); 1219 hideAboutScreen(); 1220 hideHelpScreen(); 1221 } 1222 1223 void hideTopScreen(void) 1224 { 1225 setOldTopLeftScreenFlag(); 1226 1227 hideTopLeftMainScreen(); 1228 hideTopRightMainScreen(); 1229 hideNibblesScreen(); 1230 hideConfigScreen(); 1231 hideAboutScreen(); 1232 hideHelpScreen(); 1233 1234 ui.instrSwitcherShown = false; 1235 ui.scopesShown = false; 1236 } 1237 1238 void showTopScreen(bool restoreScreens) 1239 { 1240 ui.scopesShown = false; 1241 1242 if (ui.aboutScreenShown) 1243 { 1244 showAboutScreen(); 1245 } 1246 else if (ui.configScreenShown) 1247 { 1248 showConfigScreen(); 1249 } 1250 else if (ui.helpScreenShown) 1251 { 1252 showHelpScreen(); 1253 } 1254 else if (ui.nibblesShown) 1255 { 1256 showNibblesScreen(); 1257 } 1258 else 1259 { 1260 showTopLeftMainScreen(restoreScreens); // updates ui.scopesShown 1261 showTopRightMainScreen(); 1262 } 1263 } 1264 1265 void showBottomScreen(void) 1266 { 1267 if (ui.extendedPatternEditor || ui.patternEditorShown) 1268 showPatternEditor(); 1269 else if (ui.instEditorShown) 1270 showInstEditor(); 1271 else if (ui.sampleEditorShown) 1272 showSampleEditor(); 1273 } 1274 1275 void drawGUIOnRunTime(void) 1276 { 1277 setScrollBarPos(SB_POS_ED, 0, false); 1278 1279 showTopScreen(false); // false = don't restore screens 1280 showPatternEditor(); 1281 1282 ui.updatePosSections = true; 1283 }