ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SIDPlayer/src/main.cpp
Revision: 1.1
Committed: 2000-07-28T12:42:30Z (23 years, 8 months ago) by cebix
Branch: MAIN
Branch point for: cebix
Log Message:
Initial revision

File Contents

# Content
1 /*
2 * main.cpp - SIDPlayer main program
3 *
4 * SIDPlayer (C) Copyright 1996-2000 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "sys.h"
22
23 #include <AppKit.h>
24 #include <InterfaceKit.h>
25 #include <support/UTF8.h>
26 #include <storage/Path.h>
27 #include <media/SoundPlayer.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include "main.h"
33 #include "prefs.h"
34 #include "prefs_window.h"
35 #include "mem.h"
36 #include "cpu.h"
37 #include "sid.h"
38 #include "psid.h"
39
40
41 // Global variables
42 uint32 f_rand_seed = 1;
43
44 // Flag: PSID file loaded and ready
45 static bool psid_loaded = false;
46
47 // Data from PSID header
48 static int number_of_songs; // Total number of songs in module
49 static uint16 init_adr; // C64 init routine address
50 uint16 play_adr; // C64 replay routine address
51 static uint32 speed_flags; // Speed flags (1 bit/song)
52 char module_name[64]; // Module name
53 char author_name[64]; // Author name
54 char copyright_info[64]; // Copyright info
55
56 // Currently played song number (0..n)
57 static int current_song;
58
59
60 // Message codes
61 const uint32 MSG_NEW_MODULE = 'load';
62 const uint32 MSG_NEW_SONG = 'song';
63 const uint32 MSG_SHOW_PREFS = 'pref';
64 const uint32 MSG_PLAY_PAUSE = 'plpa';
65 const uint32 MSG_STOP = 'stop';
66 const uint32 MSG_NEXT = 'next';
67 const uint32 MSG_PREV = 'prev';
68
69
70 class MainWindow;
71 class SpeedSlider;
72
73 // Application object
74 class SIDPlayer : public BApplication {
75 public:
76 SIDPlayer();
77 virtual ~SIDPlayer();
78 virtual void ArgvReceived(int32 argc, char **argv);
79 virtual void RefsReceived(BMessage *msg);
80 virtual void MessageReceived(BMessage *msg);
81 virtual void ReadyToRun(void);
82 virtual void AboutRequested(void);
83
84 private:
85 static void buffer_proc(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format);
86
87 MainWindow *main_window;
88 PrefsWindow *prefs_window;
89 BSoundPlayer player;
90 bool player_stopped;
91 };
92
93
94 // Main window object
95 class MainWindow : public BWindow {
96 public:
97 MainWindow();
98 virtual bool QuitRequested(void);
99 virtual void MessageReceived(BMessage *msg);
100
101 private:
102 BStringView *make_name_display(BRect frame, char *label_text, BView *parent);
103
104 rgb_color fill_color;
105
106 BStringView *name_view;
107 BStringView *author_view;
108 BStringView *copyright_view;
109 BStringView *position_view;
110 SpeedSlider *speed_slider;
111 };
112
113
114 // Top view object (handles drag&drop)
115 class TopView : public BView {
116 public:
117 TopView(BRect frame, const char *name, uint32 resizingMode, uint32 flags);
118 virtual void MessageReceived(BMessage *msg);
119 virtual void KeyDown(const char *bytes, int32 numBytes);
120 };
121
122
123 // Buttons
124 class PlayPauseButton : public BButton {
125 public:
126 PlayPauseButton(BRect frame, BMessage *msg);
127 virtual void Draw(BRect update);
128 };
129
130 class StopButton : public BButton {
131 public:
132 StopButton(BRect frame, BMessage *msg);
133 virtual void Draw(BRect update);
134 };
135
136 class NextButton : public BButton {
137 public:
138 NextButton(BRect frame, BMessage *msg);
139 virtual void Draw(BRect update);
140 };
141
142 class PrevButton : public BButton {
143 public:
144 PrevButton(BRect frame, BMessage *msg);
145 virtual void Draw(BRect update);
146 };
147
148
149 // Speed slider
150 class SpeedSlider : public BSlider {
151 public:
152 SpeedSlider(BRect frame, const char *name) : BSlider(frame, name, "Speed", NULL, -100, 100, B_TRIANGLE_THUMB)
153 {
154 SetHashMarks(B_HASH_MARKS_TOP);
155 SetHashMarkCount(3);
156 const rgb_color bar_color = {128, 128, 216, 0};
157 SetBarColor(bar_color);
158 SetValue(0);
159 }
160 virtual ~SpeedSlider() {}
161 virtual void SetValue(int32 value)
162 {
163 int percent = value < 0 ? 100 + value / 2 : 100 + value;
164 sprintf(status, "%d%%", percent);
165 SIDAdjustSpeed(percent);
166 BSlider::SetValue(value);
167 }
168 virtual char *UpdateText(void) const
169 {
170 return status;
171 }
172
173 private:
174 mutable char status[32];
175 };
176
177
178 /*
179 * Init everything
180 */
181
182 void InitAll(void)
183 {
184 PrefsInit(0, NULL);
185 MemoryInit();
186 SIDInit();
187 CPUInit();
188 }
189
190
191 /*
192 * Exit everything
193 */
194
195 void ExitAll(void)
196 {
197 CPUExit();
198 SIDExit();
199 MemoryExit();
200 PrefsExit();
201 }
202
203
204 /*
205 * Read PSID file header to buffer
206 */
207
208 bool LoadPSIDHeader(const char *file, uint8 *p)
209 {
210 // Read header
211 memset(p, 0, PSID_MAX_HEADER_LENGTH);
212 int fd = open(file, O_RDONLY);
213 if (fd < 0)
214 return false;
215 ssize_t actual = read(fd, p, PSID_MAX_HEADER_LENGTH);
216 close(fd);
217 return actual >= PSID_MIN_HEADER_LENGTH;
218 }
219
220
221 /*
222 * Check for PSID header
223 */
224
225 bool IsPSIDHeader(const uint8 *p)
226 {
227 // Check signature and version
228 uint32 id = read_psid_32(p, PSID_ID);
229 uint16 version = read_psid_16(p, PSID_VERSION);
230 return id == 'PSID' && (version == 1 || version == 2);
231 }
232
233
234 /*
235 * Check whether file is a PSID file
236 */
237
238 bool IsPSIDFile(const char *file)
239 {
240 // Load header
241 uint8 header[PSID_MAX_HEADER_LENGTH];
242 if (!LoadPSIDHeader(file, header))
243 return false;
244
245 // Check header
246 return IsPSIDHeader(header);
247 }
248
249
250 /*
251 * Load PSID file for playing
252 */
253
254 bool LoadPSIDFile(const char *file)
255 {
256 // Open file
257 int fd = open(file, O_RDONLY);
258 if (fd < 0)
259 return false;
260
261 // Clear C64 RAM
262 MemoryClear();
263 psid_loaded = false;
264
265 // Load and check header
266 uint8 header[PSID_MAX_HEADER_LENGTH];
267 ssize_t actual = read(fd, header, PSID_MAX_HEADER_LENGTH);
268 if (actual < 0 || !IsPSIDHeader(header)) {
269 close(fd);
270 return false;
271 }
272
273 // Extract data from header
274 number_of_songs = read_psid_16(header, PSID_NUMBER);
275 if (number_of_songs == 0)
276 number_of_songs = 1;
277 current_song = read_psid_16(header, PSID_DEFSONG);
278 if (current_song)
279 current_song--;
280 if (current_song >= number_of_songs)
281 current_song = 0;
282
283 init_adr = read_psid_16(header, PSID_INIT);
284 play_adr = read_psid_16(header, PSID_MAIN);
285
286 speed_flags = read_psid_32(header, PSID_SPEED);
287
288 int32 sl = 32, dl = 64, state = 0;
289 convert_to_utf8(B_ISO1_CONVERSION, (char *)(header + PSID_NAME), &sl, module_name, &dl, &state);
290 sl = 32, dl = 64, state = 0;
291 convert_to_utf8(B_ISO1_CONVERSION, (char *)(header + PSID_AUTHOR), &sl, author_name, &dl, &state);
292 sl = 32, dl = 64, state = 0;
293 convert_to_utf8(B_ISO1_CONVERSION, (char *)(header + PSID_COPYRIGHT), &sl, copyright_info, &dl, &state);
294 module_name[63] = 0;
295 author_name[63] = 0;
296 copyright_info[63] = 0;
297
298 // Seek to start of module data
299 lseek(fd, read_psid_16(header, PSID_LENGTH), SEEK_SET);
300
301 // Find load address
302 uint16 load_adr = read_psid_16(header, PSID_START);
303 if (load_adr == 0) { // Load address is at start of module data
304 uint8 hi, lo;
305 read(fd, &lo, 1);
306 read(fd, &hi, 1);
307 load_adr = (hi << 8) | lo;
308 }
309 if (init_adr == 0) // Init routine address is equal to load address
310 init_adr = load_adr;
311
312 // Load module data to C64 RAM
313 read(fd, ram + load_adr, RAM_SIZE - load_adr);
314 close(fd);
315
316 // Select default song
317 SelectSong(current_song);
318
319 // Set replay routine address if not given in header
320 if (play_adr == 0) { // Replay routine address is given by interrupt vector
321 if (ram[1] & 2) // Kernal ROM switched in
322 play_adr = (ram[0x0315] << 8) | ram[0x0314];
323 else // Kernal ROM switched out
324 play_adr = (ram[0xffff] << 8) | ram[0xfffe];
325 }
326
327 // Everything OK
328 psid_loaded = true;
329 return true;
330 }
331
332
333 /*
334 * Select song for playing
335 */
336
337 void SelectSong(int num)
338 {
339 if (num >= number_of_songs)
340 num = 0;
341 current_song = num;
342
343 // Reset SID
344 SIDReset(0);
345
346 // Set replay frequency
347 int freq = 50;
348 if (num < 32)
349 freq = speed_flags & (1 << num) ? 60 : 50;
350 SIDSetReplayFreq(freq);
351 SIDAdjustSpeed(100);
352
353 // Execute init routine
354 CPUExecute(init_adr, current_song, 0, 0, 1000000);
355 }
356
357
358 /*
359 * Application constructor
360 */
361
362 #if B_HOST_IS_LENDIAN
363 const media_raw_audio_format audio_format = {44100.0, 2, media_raw_audio_format::B_AUDIO_SHORT, B_MEDIA_LITTLE_ENDIAN, 4096};
364 #else
365 const media_raw_audio_format audio_format = {44100.0, 2, media_raw_audio_format::B_AUDIO_SHORT, B_MEDIA_BIG_ENDIAN, 4096};
366 #endif
367
368 SIDPlayer::SIDPlayer() : BApplication("application/x-vnd.cebix-SIDPlayer"), player(&audio_format, "SIDPlayer", buffer_proc)
369 {
370 main_window = NULL;
371 player.SetHasData(true);
372 player_stopped = true;
373 }
374
375
376 /*
377 * Application destructor
378 */
379
380 SIDPlayer::~SIDPlayer()
381 {
382 main_window = NULL;
383 prefs_window = NULL;
384 player.Stop();
385 player_stopped = true;
386 }
387
388
389 /*
390 * Shell arguments received
391 */
392
393 void SIDPlayer::ArgvReceived(int32 argc, char **argv)
394 {
395 if (argc >= 2) {
396 player.Stop();
397 LoadPSIDFile(argv[1]);
398 player.Start();
399 player_stopped = false;
400 if (main_window)
401 main_window->PostMessage(MSG_NEW_MODULE);
402 }
403 }
404
405
406 /*
407 * Tracker arguments received
408 */
409
410 void SIDPlayer::RefsReceived(BMessage *msg)
411 {
412 entry_ref the_ref;
413 if (msg->FindRef("refs", &the_ref) == B_NO_ERROR) {
414 BEntry the_entry;
415 if (the_entry.SetTo(&the_ref) == B_NO_ERROR) {
416 if (the_entry.IsFile()) {
417 BPath the_path;
418 the_entry.GetPath(&the_path);
419 player.Stop();
420 LoadPSIDFile(the_path.Path());
421 player.Start();
422 player_stopped = false;
423 if (main_window)
424 main_window->PostMessage(MSG_NEW_MODULE);
425 }
426 }
427 }
428 }
429
430
431 /*
432 * Message received
433 */
434
435 void SIDPlayer::MessageReceived(BMessage *msg)
436 {
437 switch (msg->what) {
438
439 case B_SIMPLE_DATA: // Dropped message
440 RefsReceived(msg);
441 break;
442
443 case MSG_SHOW_PREFS:
444 if (!prefs_window_open)
445 prefs_window = new PrefsWindow();
446 else if (prefs_window)
447 prefs_window->Activate(true);
448 break;
449
450 case MSG_PLAY_PAUSE:
451 if (player_stopped) {
452 player.Start();
453 player_stopped = false;
454 } else {
455 player.Stop();
456 player_stopped = true;
457 }
458 break;
459
460 case MSG_STOP:
461 player.Stop();
462 player_stopped = true;
463 SelectSong(current_song);
464 main_window->PostMessage(MSG_NEW_SONG);
465 break;
466
467 case MSG_NEXT:
468 if (current_song < number_of_songs-1) {
469 player.Stop();
470 SelectSong(current_song + 1);
471 main_window->PostMessage(MSG_NEW_SONG);
472 if (!player_stopped)
473 player.Start();
474 }
475 break;
476
477 case MSG_PREV:
478 if (current_song > 0) {
479 player.Stop();
480 SelectSong(current_song - 1);
481 main_window->PostMessage(MSG_NEW_SONG);
482 if (!player_stopped)
483 player.Start();
484 }
485 break;
486
487 default:
488 BApplication::MessageReceived(msg);
489 break;
490 }
491 }
492
493
494 /*
495 * Arguments processed, open player window
496 */
497
498 void SIDPlayer::ReadyToRun(void)
499 {
500 main_window = new MainWindow();
501 if (psid_loaded)
502 main_window->PostMessage(MSG_NEW_MODULE);
503 }
504
505
506 /*
507 * Show About window
508 */
509
510 void AboutWindow(void)
511 {
512 BAlert *theAlert = new BAlert("",
513 "SIDPlayer\nVersion 4.0\n\n"
514 "Copyright " B_UTF8_COPYRIGHT " 1996-2000 Christian Bauer\n"
515 "E-mail: Christian.Bauer@uni-mainz.de\n"
516 "http://www.uni-mainz.de/~bauec002/\n\n"
517 "SIDPlayer comes with ABSOLUTELY NO\n"
518 "WARRANTY. This is free software, and\n"
519 "you are welcome to redistribute it\n"
520 "under the terms of the GNU General\n"
521 "Public License.\n",
522 "OK", NULL, NULL, B_WIDTH_FROM_LABEL);
523
524 BTextView *theText = theAlert->TextView();
525 if (theText) {
526 theText->SetStylable(true);
527 theText->Select(0, 9);
528 BFont ourFont;
529 theText->SetFontAndColor(be_bold_font);
530 theText->GetFontAndColor(2, &ourFont, NULL);
531 ourFont.SetSize(24);
532 theText->SetFontAndColor(&ourFont);
533 }
534 theAlert->Go();
535 }
536
537 void SIDPlayer::AboutRequested(void)
538 {
539 AboutWindow();
540 }
541
542
543 /*
544 * SoundPlayer buffer procedure
545 */
546
547 void SIDPlayer::buffer_proc(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format)
548 {
549 SIDCalcBuffer((uint8 *)buffer, size);
550 }
551
552
553 /*
554 * Main window constructor
555 */
556
557 MainWindow::MainWindow() : BWindow(BRect(0, 0, 284, 96), "SIDPlayer", B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS)
558 {
559 fill_color = ui_color(B_PANEL_BACKGROUND_COLOR);
560 BRect b = Bounds();
561
562 // Move window to right position
563 Lock();
564 MoveTo(80, 80);
565
566 // Create menu bar
567 BMenuBar *bar = new BMenuBar(BRect(0, 0, b.right, b.bottom), "menubar");
568 BMenu *menu = new BMenu("File");
569 menu->AddItem(new BMenuItem("About SIDPlayer" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED)));
570 menu->AddItem(new BSeparatorItem());
571 menu->AddItem(new BMenuItem("Sound Control" B_UTF8_ELLIPSIS, new BMessage(MSG_SHOW_PREFS), 'P'));
572 menu->AddItem(new BSeparatorItem());
573 menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q'));
574 menu->SetTargetForItems(be_app);
575 bar->AddItem(menu);
576 AddChild(bar);
577 SetKeyMenuBar(bar);
578 float menu_height = bar->Bounds().Height();
579
580 // Resize window to fit menu bar
581 ResizeBy(0, menu_height);
582
583 // Light gray background
584 TopView *top = new TopView(BRect(0, menu_height, b.right, b.bottom + menu_height), "main", B_FOLLOW_NONE, B_WILL_DRAW);
585 AddChild(top);
586 top->SetViewColor(fill_color);
587
588 // Name/author/copyright display
589 name_view = make_name_display(BRect(0, 5, 279, 21), "Name", top);
590 author_view = make_name_display(BRect(0, 25, 279, 41), "Author", top);
591 copyright_view = make_name_display(BRect(0, 45, 279, 61), "Copyright", top);
592
593 // Buttons
594 top->AddChild(new PlayPauseButton(BRect(6, 67, 36, 91), new BMessage(MSG_PLAY_PAUSE)));
595 top->AddChild(new StopButton(BRect(37, 67, 67, 91), new BMessage(MSG_STOP)));
596 top->AddChild(new PrevButton(BRect(68, 67, 98, 91), new BMessage(MSG_PREV)));
597 top->AddChild(new NextButton(BRect(99, 67, 129, 91), new BMessage(MSG_NEXT)));
598
599 // Position indicator
600 top->AddChild(position_view = new BStringView(BRect(134, 72, 193, 85), "position", ""));
601 position_view->SetViewColor(fill_color);
602
603 // Speed slider
604 top->AddChild(speed_slider = new SpeedSlider(BRect(194, 62, 279, 63), "speed"));
605
606 // Show window
607 top->MakeFocus();
608 Unlock();
609 Show();
610 }
611
612
613 /*
614 * Create name display field
615 */
616
617 BStringView *MainWindow::make_name_display(BRect frame, char *label_text, BView *parent)
618 {
619 // Label to the left of the display field
620 BRect label_rect = frame;
621 label_rect.right = label_rect.left + 65;
622
623 BStringView *label = new BStringView(label_rect, "", label_text);
624 parent->AddChild(label);
625 label->SetViewColor(fill_color);
626 label->SetLowColor(fill_color);
627 label->SetAlignment(B_ALIGN_RIGHT);
628 label->SetFont(be_bold_font);
629
630 // Box around display field
631 BRect frame_rect = frame;
632 frame_rect.left += 70;
633
634 BBox *box = new BBox(frame_rect);
635 parent->AddChild(box);
636 box->SetViewColor(fill_color);
637 box->SetLowColor(fill_color);
638
639 // The display field
640 BRect textview_rect = frame_rect;
641 textview_rect.OffsetTo(0, 0);
642 textview_rect.InsetBy(4, 2);
643
644 BStringView *text = new BStringView(textview_rect, "", "");
645 box->AddChild(text);
646 text->SetViewColor(fill_color);
647 text->SetLowColor(fill_color);
648 text->SetFont(be_plain_font);
649 return text;
650 }
651
652
653 /*
654 * Main window closed, quit program
655 */
656
657 bool MainWindow::QuitRequested(void)
658 {
659 be_app->PostMessage(B_QUIT_REQUESTED);
660 return true;
661 }
662
663
664 /*
665 * Message received
666 */
667
668 void MainWindow::MessageReceived(BMessage *msg)
669 {
670 switch (msg->what) {
671 case MSG_NEW_MODULE:
672 // Update text views
673 Lock();
674 name_view->SetText(module_name);
675 author_view->SetText(author_name);
676 copyright_view->SetText(copyright_info);
677 Unlock();
678 // falls through
679
680 case MSG_NEW_SONG:
681 // Update position indicator and speed slider
682 if (number_of_songs > 0) {
683 char str[16];
684 sprintf(str, "Song %d/%d", current_song + 1, number_of_songs);
685 position_view->SetText(str);
686 }
687 speed_slider->SetValue(0);
688 break;
689
690 case MSG_SHOW_PREFS:
691 case MSG_PLAY_PAUSE:
692 case MSG_STOP:
693 case MSG_NEXT:
694 case MSG_PREV:
695 be_app->PostMessage(msg);
696 break;
697
698 default:
699 BWindow::MessageReceived(msg);
700 break;
701 }
702 }
703
704
705 /*
706 * TopView handles dropped messages (load new PSID module) and keypresses
707 */
708
709 TopView::TopView(BRect frame, const char *name, uint32 resizingMode, uint32 flags)
710 : BView(frame, name, resizingMode, flags) {}
711
712 void TopView::MessageReceived(BMessage *msg)
713 {
714 if (msg->what == B_SIMPLE_DATA)
715 be_app->PostMessage(msg);
716 else
717 BView::MessageReceived(msg);
718 }
719
720 void TopView::KeyDown(const char *bytes, int32 numBytes)
721 {
722 BMessage *msg = Window()->CurrentMessage();
723 uint32 modifiers = 0;
724 msg->FindInt32("modifiers", (int32 *)&modifiers);
725
726 switch (bytes[0]) {
727 case 'p':
728 case 'P':
729 Window()->PostMessage(MSG_PLAY_PAUSE);
730 break;
731
732 case B_ESCAPE:
733 case B_SPACE:
734 case 's':
735 case 'S':
736 Window()->PostMessage(MSG_STOP);
737 break;
738
739 case B_LEFT_ARROW:
740 Window()->PostMessage(MSG_PREV);
741 break;
742
743 case B_RIGHT_ARROW:
744 case 'n':
745 case 'N':
746 Window()->PostMessage(MSG_NEXT);
747 break;
748
749 case 'q':
750 case 'Q':
751 be_app->PostMessage(B_QUIT_REQUESTED);
752 break;
753 }
754 }
755
756
757 /*
758 * Play/Pause button
759 */
760
761 PlayPauseButton::PlayPauseButton(BRect frame, BMessage *msg) : BButton(frame, "play", "", msg) {};
762
763 void PlayPauseButton::Draw(BRect update)
764 {
765 // First draw normal button
766 BButton::Draw(update);
767
768 // Then draw play/pause image on top of it
769 if (Value())
770 SetHighColor(255, 255, 255);
771 else
772 SetHighColor(0, 0, 0);
773
774 FillRect(BRect(11, 8, 13, 16));
775 FillTriangle(BPoint(16, 8), BPoint(16, 16), BPoint(20, 12));
776 }
777
778
779 /*
780 * Stop button
781 */
782
783 StopButton::StopButton(BRect frame, BMessage *msg) : BButton(frame, "stop", "", msg) {};
784
785 void StopButton::Draw(BRect update)
786 {
787 // First draw normal button
788 BButton::Draw(update);
789
790 // Then draw stop image on top of it
791 if (Value())
792 SetHighColor(255, 255, 255);
793 else
794 SetHighColor(0, 0, 0);
795
796 FillRect(BRect(11, 8, 20, 16));
797 }
798
799
800 /*
801 * "Next" button
802 */
803
804 NextButton::NextButton(BRect frame, BMessage *msg) : BButton(frame, "next", "", msg) {};
805
806 void NextButton::Draw(BRect update)
807 {
808 // First draw normal button
809 BButton::Draw(update);
810
811 // Then draw "next" image on top of it
812 if (Value())
813 SetHighColor(255, 255, 255);
814 else
815 SetHighColor(0, 0, 0);
816
817 FillTriangle(BPoint(12, 8), BPoint(12, 16), BPoint(16, 12));
818 FillRect(BRect(17, 8, 19, 16));
819 }
820
821
822 /*
823 * "Prev" button
824 */
825
826 PrevButton::PrevButton(BRect frame, BMessage *msg) : BButton(frame, "prev", "", msg) {};
827
828 void PrevButton::Draw(BRect update)
829 {
830 // First draw normal button
831 BButton::Draw(update);
832
833 // Then draw "prev" image on top of it
834 if (Value())
835 SetHighColor(255, 255, 255);
836 else
837 SetHighColor(0, 0, 0);
838
839 FillRect(BRect(12, 8, 14, 16));
840 FillTriangle(BPoint(19, 8), BPoint(19, 16), BPoint(15, 12));
841 }
842
843
844 /*
845 * Main program
846 */
847
848 int main(int argc, char **argv)
849 {
850 InitAll();
851 SIDPlayer *the_app = new SIDPlayer();
852 the_app->Run();
853 delete the_app;
854 ExitAll();
855 return 0;
856 }