#include #include #include #ifndef _WIN32_WCE #include #include #include #include #include #endif #include #include // For gettimeofday(); #include // for wait3() on sparc_solaris #include #include #include enum STATE { state_initial, state_number, state_numletter }; /* * This routine will run the program specified with the arguments * specified. It is NOT POSSIBLE to send parameters with embedded spaces. * This routine closes all open file descriptors except for stdout and * stderr on the child so that it can't hang and cause them to remain open. * It returns the PID of the child process on success and -1 on failure. */ int run_program(const char * program, const char * args) { int loop; int pid; /* Child's process ID */ if ( (pid = fork()) == -1) { fprintf(stderr,"run_program: cannot fork().\n"); return -1; } if (pid == 0) { /* CHILD */ int ret; int num_descriptors;/* Number of available file descr */ char command[600]; /* Command passed to system() call */ /* Close all files except stdout and stderr. */ /* This prevents a hung child from keeping devices open */ num_descriptors = getdtablesize(); for (loop = 0; loop < num_descriptors; loop++) { if ( (loop != 1) && (loop != 2) ) { close(loop); } } sprintf(command,"%s %s",program, args); ret = system(command); if ( (ret == 127) || (ret == -1) ) { fprintf(stderr, "run_program: system() failed !!!!!\n"); perror("Error"); fprintf(stderr, "Attempted command was: '%s'\n", command); exit(-1); /* This should never occur */ } exit(0); } else { /* PARENT */ return pid; } } int noblock_read_character(void) { int c; // See if there is one or more characters available // on stdin. If so, then get the next one. If not, // return zero. Wait up to 1ms for a keypress (1000 // microseconds). struct timeval millisec = { 0 , 1000 }; fd_set read_descriptors; FD_ZERO(&read_descriptors); FD_SET(0, &read_descriptors); if (select( 16, &read_descriptors, NULL, NULL, &millisec) > 0) { return getchar(); } else { return 0; } } int main(unsigned argc, const char *argv[]) { bool done = false; int c; // Turn off line-buffered input so that we can get characters as // soon as the user types them and will time out if none are pressed. //system("stty -icanon time 1"); system("/bin/stty raw opost onlcr -echo"); // Keep getting characters and acting on them until someone // kills us or stdin runs out of data for us. while ( true ) { static STATE current_state = state_initial; static int number_picked = 0; static int letter_picked = 0; // Get the next character (zero if there was no character). c = noblock_read_character(); // When a character is pressed, reset the "since_pressed" // timer to zero. While no character is pressed, count up // the time so that we can have timeouts. static struct timeval last_char_time = { 0 , 0 }; struct timeval now; gettimeofday(&now, NULL); if ( (c != 0) || ((last_char_time.tv_sec == 0) && (last_char_time.tv_usec == 0)) ) { last_char_time = now; } long secdiff = now.tv_sec - last_char_time.tv_sec; long usecdiff = now.tv_usec - last_char_time.tv_usec; double since_pressed = secdiff + 1e-6*usecdiff; if (c != 0) { printf("XXX got %d\n", c); } //------------------------------------------------------------------- // Process commands that have the same meaning independent of the // state of the program. // '/' means quit the program (remove for safety if desired) /* if (c == '/') { char command[512]; sprintf(command, "musicbox_kill_playing_jobs.txt"); system(command); break; } */ // Space bar (' ') means toggle pause/resume if (c == ' ') { char command[512]; sprintf(command, "musicbox_pause_or_resume.txt"); system(command); continue; } // '-' means play all categories at random if (c == '-') { char command[512]; sprintf(command, "musicbox_play.txt all &"); system(command); continue; } // '=' means play an uncataloged CD if there are any if (c == '=') { char command[512]; sprintf(command, "musicbox_find_uncataloged_cd.txt"); system(command); continue; } // Tab means stop playing all playing songs if (c == 9) { char command[512]; sprintf(command, "musicbox_kill_playing_jobs.txt"); system(command); continue; } // Enter key means change play mode if (c == 13) { char command[512]; sprintf(command, "musicbox_switch_play_mode.txt"); system(command); continue; } // Backspace key means skip current song if (c == 127) { char command[512]; sprintf(command, "musicbox_skip_this_song.txt"); system(command); continue; } //------------------------------------------------------------------- // Finite state machine running to keep track of what we should // do. Switch selects which state we are in, then the state // logic within each state determines what to do next based on // key presses and timeouts. switch (current_state) { // Initial state, no partial input of any command. case state_initial: // They pressed a digit, so go into the number state where // we have gotten a number at the start of a command. if (isdigit(c)) { number_picked = c; current_state = state_number; break; } else { } break; case state_number: // Check for timeout. If we time out, reset the last_char_time // as if a key had been pressed to stave off the next timeout. // Then start playing all songs under this number and go back // to the initial state. Play them in the background, so we can // keep executing commands. if (since_pressed > 2) { last_char_time = now; char command[512]; sprintf(command, "musicbox_kill_playing_jobs.txt"); system(command); sprintf(command, "musicbox_play.txt %c &",number_picked); system(command); current_state = state_initial; break; } // Check for no key pressed (skip the rest of the check) if (c == 0) { break; } // If they pressed a lower-case letter key, go to the next state // where we have both a number and a letter. if (islower(c)) { letter_picked = c; current_state = state_numletter; break; } else { fprintf(stderr,"Don't understand number command\n"); current_state = state_initial; break; } break; case state_numletter: // Check for timeout. If we time out, reset the last_char_time // as if a key had been pressed to stave off the next timeout. // Then start playing all songs under this number/letter and go back // to the initial state. Play them in the background, so we can // keep executing commands. if (since_pressed > 2) { last_char_time = now; char command[512]; sprintf(command, "musicbox_kill_playing_jobs.txt"); system(command); sprintf(command, "musicbox_play.txt %c/%c &", number_picked, letter_picked); system(command); current_state = state_initial; break; } // Check for no key pressed (skip the rest of the check) if (c == 0) { break; } // If they pressed a lower-case letter key, we play the whole // three-character subtree. // If we're playing a newly-inserted CD or else an // uncataloged CD and there is no link for the specified directory, // then this should catalog it rather than switching to play // the one entered. if (islower(c)) { last_char_time = now; char command[512]; sprintf(command, "musicbox_catalog_or_play.txt %c/%c/%c &", number_picked, letter_picked, c); system(command); current_state = state_initial; break; } else { fprintf(stderr,"Don't understand numletter command\n"); current_state = state_initial; break; } break; default: fprintf(stderr,"Unexpected internal state\n"); current_state = state_initial; break; } } // Set the keyboard input back to normal and quit with no error // condition. system("/bin/stty sane"); return 0; }