/* * main.c * * Description: * Main program for the cd audio tool * * History: * rogerc 10/29/90 Created */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cdaudio.h" #include "program.h" #include "client.h" #include "display.h" #include "skip.h" #include "cue.h" #include "seldbox.h" #include "progdbox.h" #include "cddata.h" #include "database.h" #include "icon" #define UPDATEINTERVAL 250 #define TIMEINTERVAL 2000 static struct mainwin_tag { Widget play, stop, open, quit, skipf, skipb, ff, rew, time; Widget data, repeat, shuffle, program, clear; } mainwin; typedef struct _resource_struct { XmFontList font_list; XmString label_string; GC gc; } RESOURCESTRUCT; static void shuffle( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ); static void normal( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ); static void program( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ); static void repeat ( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ); void set_button_bitmap( Widget button, char *bits, Dimension width, Dimension height ); void play( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ); void stop( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ); void open_drawer( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ); void time_display( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ); void timer_revert( CLIENTDATA *clientp, XtIntervalId *id ); void quit_arm( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ); void quit_activate( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ); void quit_disarm( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ); void update( CLIENTDATA *clientp, XtIntervalId *id ); void enable_buttons( int state ); Widget create_button_label( Widget parent, char *name, Arg *create_args, int ncreate_args ); void draw_button_label( Widget w, RESOURCESTRUCT *r, XmDrawingAreaCallbackStruct *cb ); void usage( void ); static void terminate( void ); static void PostError(Widget parent, char *errmsg, int fatal); static char * GetResourceString(Widget w, char *name, char *class, char *def); static CDPLAYER *cdplayer; /* * main( int argc, char *argv[] ) * * Description: * Contains the main loop for the CD Audio tool * * Parameters: * argc Number of command line arguments * argv Command line argument vector * * Returns: * never */ main( int argc, char *argv[] ) { Widget toplevel, mainForm, form, disprc, buttonform; Widget separator; Widget play_label, stop_label, skip_label, cue_label; Widget children[30]; CLIENTDATA client; CDSTATUS status; Arg wargs[10]; int n, num_children, i; char *dev = NULL; Display *dpy; unsigned long white, black; Dimension height; Pixmap icon; static XtResource icon_resource = { "icon", "Icon", XmRPrimForegroundPixmap, sizeof (Pixmap), 0, XtRImmediate, (caddr_t)XmUNSPECIFIED_PIXMAP }; static const char *databasePath = NULL, *databaseCDir = NULL; static XtResource dbaseResources[] = { { "databasePath", "DatabasePath", XmRString, sizeof(String), (unsigned int)&databasePath, 0, NULL }, { "databaseCDir", "DatabaseCDir", XmRString, sizeof(String), (unsigned int)&databaseCDir, 0, NULL } }; static XrmOptionDescRec cmdLineOptions[] = { {"-dbpath", "*databasePath", XrmoptionSepArg, NULL}, {"-dbcdir", "*databaseCDir", XrmoptionSepArg, NULL}, }; int rv; Pixel fg, bg; int error; /* mallopt(M_DEBUG,0xffff);*/ memset( &client, 0, sizeof (client) ); for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-dev") == 0) { if (i == argc - 1) { usage(); } dev = argv[i + 1]; break; } } error = 0; if (!(client.cdplayer = CDopen( dev, "r" ))) { /* Save oserror() for later use */ error = oserror(); if (error == 0) { /* Make sure it's non-zero! */ error = ENOENT; } } /* * Plug a security hole; this program is setuid, and it writes * to the file ~/.cdplayerrc, so all kinds of awful things * could happen with symbolic links if we didn't do this. * cdplayer has to be setuid so that it can open /dev/scsi files. */ setuid(getuid()); /* * Don't do this i18n stuff until after setuid. */ setlocale(LC_ALL, ""); XtSetLanguageProc(NULL, NULL, NULL); /* * Now report error if there was one. */ if (error) { setoserror(error); perror(dev ? dev : "CDROM"); exit(1); } toplevel = XtInitialize( argv[0], "Cdplayer", cmdLineOptions, XtNumber(cmdLineOptions), &argc, argv ); client.toplevel = toplevel; XtGetApplicationResources(toplevel, NULL, dbaseResources, XtNumber(dbaseResources), NULL, 0); rv = db_init_pkg(databasePath, databaseCDir); if (rv & DB_NEED_DIR) { if (!databaseCDir) { databaseCDir = db_get_dflt_dir(); } if (mkdir(databaseCDir, 0777) < 0) { #ifdef DEBUG perror(databaseCDir); #endif } } cdplayer = client.cdplayer; atexit( terminate ); CDgetstatus( client.cdplayer, &status ); client.status = &status; client.data = db_init_from_cd( client.cdplayer, client.status ); n = 0; XtSetArg( wargs[n], XtNiconPixmap, &icon ); n++; if (icon == None) { n = 0; XtSetArg( wargs[n], XtNiconPixmap, XCreateBitmapFromData( XtDisplay( toplevel ), XtScreen( toplevel )->root, icon_bits, icon_width, icon_height ) ); n++; XtSetValues( toplevel, wargs, n ); } n = 0; mainForm = XmCreateForm( toplevel, "mainForm", wargs, n ); XtManageChild( mainForm ); n = 0; XtSetArg( wargs[n], XmNorientation, XmVERTICAL ); n++; XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(wargs[n], XmNbottomAttachment, XmATTACH_FORM); n++; disprc = XmCreateRowColumn( mainForm, "disprc", wargs, n ); XtManageChild( disprc ); create_display( disprc, &client ); form = XmCreateForm( disprc, "displayButtons", NULL, 0 ); XtManageChild( form ); num_children = 0; children[num_children++] = mainwin.program = XmCreatePushButton( form, "program", NULL, 0 ); children[num_children++] = mainwin.shuffle = XmCreatePushButton( form, "shuffle", NULL, 0 ); children[num_children++] = mainwin.clear = XmCreatePushButton( form, "clear", NULL, 0 ); n = 0; XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg(wargs[n], XmNtopWidget, mainwin.program); n++; children[num_children++] = mainwin.time = XmCreatePushButton( form, "time", wargs, n ); n = 0; XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg(wargs[n], XmNtopWidget, mainwin.shuffle); n++; children[num_children++] = mainwin.repeat = XmCreatePushButton( form, "repeat", wargs, n ); n = 0; XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_WIDGET); n++; XtSetArg(wargs[n], XmNtopWidget, mainwin.clear); n++; children[num_children++] = mainwin.data = XmCreatePushButton( form, "data", wargs, n ); XtManageChildren( children, num_children ); num_children = 0; n = 0; XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(wargs[n], XmNbottomAttachment, XmATTACH_FORM); n++; buttonform = XmCreateForm( mainForm, "controlButtons", wargs, n ); XtManageChild( buttonform ); n = 0; XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg(wargs[n], XmNrightWidget, buttonform); n++; XtSetValues(disprc, wargs, n); n = 0; XtSetArg( wargs[n], XmNtopAttachment, XmATTACH_FORM ); n++; XtSetArg( wargs[n], XmNleftAttachment, XmATTACH_FORM ); n++; XtSetArg( wargs[n], XmNrecomputeSize, False ); n++; children[num_children++] = play_label = XmCreateLabel( buttonform, "playLabel", wargs, n ); n = 0; XtSetArg( wargs[n], XmNheight, &height ); n++; XtGetValues( play_label, wargs, n ); n = 0; XtSetArg( wargs[n], XmNtopAttachment, XmATTACH_FORM ); n++; XtSetArg( wargs[n], XmNleftAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNleftWidget, play_label ); n++; XtSetArg( wargs[n], XmNrightAttachment, XmATTACH_FORM ); n++; XtSetArg( wargs[n], XmNrecomputeSize, False ); n++; children[num_children++] = stop_label = XmCreateLabel( buttonform, "stopLabel", wargs, n ); n = 0; XtSetArg(wargs[n], XmNforeground, &fg); n++; XtSetArg(wargs[n], XmNbackground, &bg); n++; XtGetValues(stop_label, wargs, n); n = 0; XtSetArg( wargs[n], XmNtopAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNtopWidget, play_label ); n++; XtSetArg( wargs[n], XmNleftAttachment, XmATTACH_FORM ); n++; children[num_children++] = mainwin.play = XmCreatePushButton( buttonform, "play", wargs, n ); n = 0; XtSetArg( wargs[n], XmNtopAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNtopWidget, stop_label ); n++; XtSetArg( wargs[n], XmNrightAttachment, XmATTACH_FORM ); n++; children[num_children++] = mainwin.stop = XmCreatePushButton( buttonform, "stop", wargs, n ); n = 0; XtSetArg( wargs[n], XmNtopAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNtopWidget, mainwin.play ); n++; XtSetArg( wargs[n], XmNrightAttachment, XmATTACH_FORM ); n++; XtSetArg( wargs[n], XmNleftAttachment, XmATTACH_FORM ); n++; XtSetArg( wargs[n], XmNheight, height ); n++; XtSetArg(wargs[n], XmNforeground, fg); n++; XtSetArg(wargs[n], XmNbackground, bg); n++; children[num_children++] = skip_label = create_button_label(buttonform, "skipLabel", wargs, n ); n = 0; XtSetArg( wargs[n], XmNtopAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNtopWidget, skip_label ); n++; XtSetArg( wargs[n], XmNleftAttachment, XmATTACH_FORM ); n++; children[num_children++] = mainwin.skipb = XmCreatePushButton( buttonform, "skipb", wargs, n ); n = 0; XtSetArg( wargs[n], XmNtopAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNtopWidget, skip_label ); n++; XtSetArg( wargs[n], XmNleftAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNleftWidget, mainwin.skipb ); n++; XtSetArg( wargs[n], XmNrightAttachment, XmATTACH_FORM ); n++; children[num_children++] = mainwin.skipf = XmCreatePushButton( buttonform, "skipf", wargs, n ); n = 0; XtSetArg( wargs[n], XmNtopAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNtopWidget, mainwin.skipb ); n++; XtSetArg( wargs[n], XmNrightAttachment, XmATTACH_FORM ); n++; XtSetArg( wargs[n], XmNleftAttachment, XmATTACH_FORM ); n++; XtSetArg( wargs[n], XmNheight, height ); n++; XtSetArg(wargs[n], XmNforeground, fg); n++; XtSetArg(wargs[n], XmNbackground, bg); n++; children[num_children++] = cue_label = create_button_label( buttonform, "cueLabel", wargs, n ); n = 0; XtSetArg( wargs[n], XmNtopAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNtopWidget, cue_label ); n++; XtSetArg( wargs[n], XmNleftAttachment, XmATTACH_FORM ); n++; children[num_children++] = mainwin.rew = XmCreatePushButton( buttonform, "rew", wargs, n ); n = 0; XtSetArg( wargs[n], XmNtopAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNtopWidget, cue_label ); n++; XtSetArg( wargs[n], XmNleftAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNleftWidget, mainwin.rew ); n++; XtSetArg( wargs[n], XmNrightAttachment, XmATTACH_FORM ); n++; children[num_children++] = mainwin.ff = XmCreatePushButton( buttonform, "ff", wargs, n ); n = 0; XtSetArg( wargs[n], XmNtopAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNtopWidget, mainwin.rew ); n++; XtSetArg( wargs[n], XmNleftAttachment, XmATTACH_FORM ); n++; XtSetArg( wargs[n], XmNrightAttachment, XmATTACH_FORM ); n++; children[num_children++] = separator = XmCreateSeparator( buttonform, "separator", wargs, n ); n = 0; XtSetArg( wargs[n], XmNtopAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNtopWidget, separator ); n++; XtSetArg( wargs[n], XmNleftAttachment, XmATTACH_FORM ); n++; children[num_children++] = mainwin.quit = XmCreatePushButton( buttonform, "quit", wargs, n ); n = 0; XtSetArg( wargs[n], XmNtopAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNtopWidget, separator ); n++; XtSetArg( wargs[n], XmNleftAttachment, XmATTACH_WIDGET ); n++; XtSetArg( wargs[n], XmNleftWidget, mainwin.quit ); n++; XtSetArg( wargs[n], XmNrightAttachment, XmATTACH_FORM ); n++; children[num_children++] = mainwin.open = XmCreatePushButton( buttonform, "open", wargs, n ); XtManageChildren( children, num_children ); create_data_dbox( mainwin.data, &client ); create_program_select_dialog( mainwin.program, &client ); client.normalButton = mainwin.clear; client.shuffleButton = mainwin.shuffle; client.programButton = mainwin.program; client.time = mainwin.time; client.update_func = update; XtAddCallback(mainwin.play, XmNactivateCallback, play, &client); XtAddCallback(mainwin.stop, XmNactivateCallback, stop, &client); XtAddCallback(mainwin.open, XmNactivateCallback, open_drawer, &client); XtAddCallback(mainwin.skipf, XmNactivateCallback, skipf, &client); XtAddCallback(mainwin.skipb, XmNactivateCallback, skipb, &client); XtAddCallback(mainwin.quit, XmNarmCallback, quit_arm, &client); XtAddCallback(mainwin.quit, XmNactivateCallback, quit_activate, &client); XtAddCallback(mainwin.quit, XmNdisarmCallback, quit_disarm, &client); XtAddCallback(mainwin.ff, XmNarmCallback, ff_arm, &client); XtAddCallback(mainwin.ff, XmNdisarmCallback, ff_disarm, &client); XtAddCallback(mainwin.rew, XmNarmCallback, rew_arm, &client); XtAddCallback(mainwin.rew, XmNdisarmCallback, rew_disarm, &client); XtAddCallback(mainwin.time, XmNactivateCallback, time_display, &client); XtAddCallback(mainwin.data, XmNactivateCallback, popup_data_dbox, &client); XtAddCallback(mainwin.program, XmNactivateCallback, program, &client); XtAddCallback(mainwin.shuffle, XmNactivateCallback, shuffle, &client); XtAddCallback(mainwin.clear, XmNactivateCallback, normal, &client); XtAddCallback(mainwin.repeat, XmNactivateCallback, repeat, &client); XtRealizeWidget(toplevel); update(&client, 0); XtMainLoop(); } /* * static void shuffle( Widget w, CLIENTDATA *clientp, * XmAnyCallbackStruct *call_data ) * * Description: * callback function for the shuffle widget. Create a random program * and set things up for playing it. * * Parameters: * w The shuffle widget * clientp client data * call_data ignored */ static void shuffle( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ) { CDstop( clientp->cdplayer ); clientp->play_mode = PLAYMODE_SHUFFLE; if (clientp->program) free_program( clientp->program ); clientp->program = program_shuffle( clientp->cdplayer ); set_playmode_text( PLAYMODE_SHUFFLE ); } /* * static void normal( Widget w, CLIENTDATA *clientp, * XmAnyCallbackStruct *call_data ) * * Description: * callback function for the normal widget. Takes us out of shuffle * or program mode, stopping cd if we weren't already in normal mode * * Parameters: * w the normal widget * clientp client data * call_data ignored */ static void normal( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ) { if (clientp->play_mode != PLAYMODE_NORMAL) { program_stop( clientp->cdplayer, clientp->program ); if (clientp->program) { free_program( clientp->program ); clientp->program = 0; } } set_playmode_text( PLAYMODE_NORMAL ); clientp->play_mode = PLAYMODE_NORMAL; } /* * static void program ( Widget w, CLIENTDATA *clientp, * XmAnyCallbackStruct *call_data ) * * Description: * callback function for the program widget. Pops up the program * selection dialog box to allow the user to select a program to play. * * Parameters: * w * clientp client data * call_data ignored */ static void program ( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ) { select_program( clientp ); } /* * static void repeat ( Widget w, CLIENTDATA *clientp, * XmAnyCallbackStruct *call_data ) * * Description: * Toggle repeat status * * Parameters: * w The repeat button * clientp client data * call_data ignored */ static void repeat ( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ) { CDSTATUS status; clientp->repeat_button = ~clientp->repeat_button; set_repeat( clientp->repeat_button ); /* * This makes repeat work when cdplayer is started when the CD * is already playing. Since the CD was already playing, * play() might not have been called, and thus clientp->repeat * might not have been set. So we set it here if appropriate. */ if (CDgetstatus(clientp->cdplayer, &status) && (status.state == CD_PLAYING || status.state == CD_STILL || status.state == CD_PAUSED)) { clientp->repeat = 1; } } /* * void play( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ) * * Description: * Start play of CD if CD-ROM drive is ready * Continue play of CD if CD is paused * Pause CD if CD is playing * * Parameters: * w The play button * clientp Client data * call_data ignored */ void play( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ) { CDSTATUS status; CDgetstatus( clientp->cdplayer, &status ); clientp->repeat = 1; switch (status.state) { case CD_PLAYING: case CD_STILL: case CD_PAUSED: clientp->user_paused = status.state == CD_PLAYING ? 1 : 0; clientp->skipped_back = 0; CDtogglepause( clientp->cdplayer ); break; case CD_READY: program_playwarn( clientp ); select_playwarn( clientp ); switch (clientp->play_mode) { default: case PLAYMODE_NORMAL: if (clientp->start_track < status.first) clientp->start_track = status.first; if (clientp->start_track > status.last) clientp->start_track = status.last; CDplay( clientp->cdplayer, clientp->start_track, 1 ); clientp->start_track = 0; break; case PLAYMODE_SHUFFLE: case PLAYMODE_PROGRAM: program_play( clientp->cdplayer, clientp->program, 1 ); break; } break; default: break; } } /* * void stop( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ) * * Description: * Stop the CD * * Parameters: * w The stop button * clientp Client data * call_data ignored */ void stop( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ) { clientp->repeat = 0; if (clientp->play_mode == PLAYMODE_PROGRAM || clientp->play_mode == PLAYMODE_SHUFFLE) program_stop( clientp->cdplayer, clientp->program ); CDstop( clientp->cdplayer ); } /* * void time_display( Widget w, CLIENTDATA *clientp, * XmAnyCallbackStruct *call_data ) * * Description: * Cycle through the various modes we have for displaying the time. * * Parameters: * w The time button * clientp Client data * call_data ignored */ void time_display( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ) { if (clientp->status->state == CD_READY) { clientp->ready_time_display = (clientp->ready_time_display + 1) % (READY_LAST + 1); if (clientp->timeid) XtRemoveTimeOut( clientp->timeid ); clientp->timeid = XtAddTimeOut( TIMEINTERVAL, timer_revert, clientp ); } else clientp->time_display = (clientp->time_display + 1) % (TIME_LAST + 1); draw_display( clientp ); } /* * void timer_revert( CLIENTDATA *clientp, XtIntervalId *id ) * * Description: * This is used when CD is stopped and user presses the time button. * The display mode for the time changes until this time out gets called, * at which point it reverts to the default. * * Parameters: * clientp client data * id time out id */ void timer_revert( CLIENTDATA *clientp, XtIntervalId *id ) { clientp->timeid = 0; clientp->ready_time_display = READY_NORMAL; draw_display( clientp ); } /* * void open_drawer( Widget w, CLIENTDATA *clientp, * XmAnyCallbackStruct *call_data ) * * Description: * Ejects the CD caddy from the CD-ROM drive * * Parameters: * w the open button * clientp Client data * call_data ignored */ void open_drawer( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ) { if (!CDeject(clientp->cdplayer) && oserror() == EBUSY) { /* * In 5.1, it is possible that someone could mount an * efs CD while we are running. CDeject checks for * this case, and returns failure and sets errno to * EBUSY to tell us that the CD was mounted. We post * an error message box and exit when the user presses * OK. */ PostError(clientp->toplevel, GetResourceString(clientp->toplevel, "cdMountedErrorMsg", "CdMountedErrorMsg", "CD has been mounted"), 1); } clientp->start_track = 0; } /* * void quit_arm( Widget w, CLIENTDATA *clientp, * XmAnyCallbackStruct *call_data ) * * Description: * Clear the quit flag, so that if user disarms the quit button without * activating, we won't exit * * Parameters: * w The quit button * clientp Client data * call_data ignored */ void quit_arm( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ) { clientp->quit_flag = 0; } /* * void quit_activate( Widget w, CLIENTDATA *clientp, * XmAnyCallbackStruct *call_data ) * * Description: * Set the quit flag, so that as soon as the quit button is disarmed, * we'll exit. * * Parameters: * w the quit button * clientp client data * call_data ignored */ void quit_activate( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ) { clientp->quit_flag = 1; } /* * void quit_disarm( Widget w, CLIENTDATA *clientp, * XmAnyCallbackStruct *call_data ) * * Description: * If the button was activated, we exit. * * Parameters: * w the quit button * clientp client data * call_data ignored */ void quit_disarm( Widget w, CLIENTDATA *clientp, XmAnyCallbackStruct *call_data ) { if (clientp->quit_flag) { XtCloseDisplay( XtDisplay(w) ); exit( 0 ); } } /* * void update( CLIENTDATA *clientp, XtIntervalId *id ) * * Description: * This gets called every UPDATEINTERVAL milliseconds, and it checks * out the state of the CD-ROM drive, updating the display if necessary * * Parameters: * clientp Client data * id time out id */ void update( CLIENTDATA *clientp, XtIntervalId *id ) { static CDSTATUS status; static CDSTATUS oldstatus; static int first_time = 1; /* * If we're rewinding or fast-forwarding, rew and ff set the * status stuff, and we don't want to take it from the CD-ROM drive. */ if (!clientp->cueing) CDgetstatus( clientp->cdplayer, &status ); clientp->status = &status; if (oldstatus.state == CD_NODISC && status.state == CD_READY) { clientp->data_changed = 1; time_display( clientp->time, clientp, NULL ); } if (status.state != oldstatus.state && (status.state == CD_NODISC || status.state == CD_ERROR)) { /* * Give each of the dialog boxes a chance to clean up */ program_nodisc( clientp ); select_nodisc( clientp ); data_nodisc( clientp ); if (clientp->program) { free_program( clientp->program ); clientp->program = 0; } clientp->play_mode = PLAYMODE_NORMAL; set_playmode_text( PLAYMODE_NORMAL ); } if (status.state == CD_READY && (clientp->play_mode == PLAYMODE_PROGRAM || clientp->play_mode == PLAYMODE_SHUFFLE) && program_active( clientp->program )) { program_next( clientp->cdplayer, clientp->program ); CDgetstatus( clientp->cdplayer, &status ); } else if (status.state == CD_READY && clientp->repeat && clientp->repeat_button && !clientp->cueing) { if (clientp->play_mode == PLAYMODE_NORMAL) clientp->start_track = status.first; play( NULL, clientp, NULL ); CDgetstatus( clientp->cdplayer, &status ); } if ((status.state != oldstatus.state && !clientp->skipping) || first_time) { set_status_bitmap( status.state ); enable_buttons( status.state ); first_time = 0; } /* * In program mode, we often end up in the first second of the * track after the one we've just finished playing; we never * hear it, and this keeps the user from seeing it displayed. */ if (clientp->play_mode == PLAYMODE_NORMAL || (status.state != CD_PLAYING && status.state != CD_PAUSED && status.state != CD_STILL) || status.track == clientp->program->tracks[clientp->program->current]) { draw_display( clientp ); } oldstatus = status; clientp->updateid = XtAddTimeOut( UPDATEINTERVAL, update, clientp ); } /* * void enable_buttons( int state ) * * Description: * Enable/disable buttons as appropriate for the current state * * Parameters: * state state of cdplayer */ void enable_buttons( int state ) { Arg wargs[10]; int n; static int oldstate = -1; if (oldstate == state) return; oldstate = state; switch (state) { default: case CD_NODISC: case CD_ERROR: case CD_CDROM: n = 0; XtSetArg( wargs[n], XmNsensitive, FALSE ); n++; XtSetValues( mainwin.play, wargs, n ); XtSetValues( mainwin.stop, wargs, n ); XtSetValues( mainwin.skipf, wargs, n ); XtSetValues( mainwin.skipb, wargs, n ); XtSetValues( mainwin.ff, wargs, n ); XtSetValues( mainwin.rew, wargs, n ); XtSetValues( mainwin.time, wargs, n ); XtSetValues( mainwin.data, wargs, n ); XtSetValues( mainwin.repeat, wargs, n ); XtSetValues( mainwin.program, wargs, n ); XtSetValues( mainwin.shuffle, wargs, n ); XtSetValues( mainwin.clear, wargs, n ); XtSetSensitive(mainwin.open, state == CD_CDROM); break; case CD_PLAYING: n = 0; XtSetArg( wargs[n], XmNsensitive, TRUE ); n++; XtSetValues( mainwin.play, wargs, n ); XtSetValues( mainwin.stop, wargs, n ); XtSetValues( mainwin.open, wargs, n ); XtSetValues( mainwin.skipf, wargs, n ); XtSetValues( mainwin.skipb, wargs, n ); XtSetValues( mainwin.ff, wargs, n ); XtSetValues( mainwin.rew, wargs, n ); XtSetValues( mainwin.time, wargs, n ); XtSetValues( mainwin.data, wargs, n ); XtSetValues( mainwin.repeat, wargs, n ); XtSetValues( mainwin.program, wargs, n ); XtSetValues( mainwin.shuffle, wargs, n ); XtSetValues( mainwin.clear, wargs, n ); n = 0; XtSetArg( wargs[n], XmNsensitive, FALSE ); n++; XtSetValues( mainwin.program, wargs, n ); XtSetValues( mainwin.shuffle, wargs, n ); XtSetValues( mainwin.clear, wargs, n ); break; case CD_READY: n = 0; XtSetArg( wargs[n], XmNsensitive, TRUE ); n++; XtSetValues( mainwin.play, wargs, n ); XtSetValues( mainwin.skipf, wargs, n ); XtSetValues( mainwin.skipb, wargs, n ); XtSetValues( mainwin.time, wargs, n ); XtSetValues( mainwin.data, wargs, n ); XtSetValues( mainwin.open, wargs, n ); XtSetValues( mainwin.repeat, wargs, n ); XtSetValues( mainwin.program, wargs, n ); XtSetValues( mainwin.shuffle, wargs, n ); XtSetValues( mainwin.clear, wargs, n ); n = 0; XtSetArg( wargs[n], XmNsensitive, FALSE ); n++; XtSetValues( mainwin.stop, wargs, n ); XtSetValues( mainwin.ff, wargs, n ); XtSetValues( mainwin.rew, wargs, n ); break; case CD_PAUSED: case CD_STILL: n = 0; XtSetArg( wargs[n], XmNsensitive, TRUE ); n++; XtSetValues( mainwin.play, wargs, n ); XtSetValues( mainwin.skipf, wargs, n ); XtSetValues( mainwin.skipb, wargs, n ); XtSetValues( mainwin.time, wargs, n ); XtSetValues( mainwin.data, wargs, n ); XtSetValues( mainwin.open, wargs, n ); XtSetValues( mainwin.stop, wargs, n ); XtSetValues( mainwin.repeat, wargs, n ); XtSetValues( mainwin.program, wargs, n ); XtSetValues( mainwin.shuffle, wargs, n ); XtSetValues( mainwin.clear, wargs, n ); n = 0; XtSetArg( wargs[n], XmNsensitive, FALSE ); n++; XtSetValues( mainwin.ff, wargs, n ); XtSetValues( mainwin.rew, wargs, n ); XtSetValues( mainwin.program, wargs, n ); XtSetValues( mainwin.shuffle, wargs, n ); XtSetValues( mainwin.clear, wargs, n ); break; } } Widget create_button_label( Widget parent, char *name, Arg *create_args, int ncreate_args ) { Widget w; int n; Arg wargs[10]; RESOURCESTRUCT *resource_struct; static XtResource resources[] = { { XmNfontList, XmCFontList, XmRFontList, sizeof (XmFontList), XtOffset( struct _resource_struct *, font_list ), XtRString, "-*-helvetica-medium-r-*-*-9-*" }, { XmNlabelString, XmCLabelString, XmRXmString, sizeof (XmString), XtOffset( struct _resource_struct *, label_string ), XtRString, "" } }; XGCValues gcv; XmFontContext context; XFontStruct *font; XmStringCharSet charset = XmSTRING_DEFAULT_CHARSET; Boolean success; resource_struct = malloc(sizeof (*resource_struct)); memset(resource_struct, 0, sizeof (*resource_struct)); w = XmCreateDrawingArea(parent, name, create_args, ncreate_args); XtGetApplicationResources(w, resource_struct, resources, XtNumber(resources), NULL, 0); success = XmFontListInitFontContext(&context, resource_struct->font_list); XmFontListGetNextFont(context, &charset, &font); XmFontListFreeFontContext(context); n = 0; XtSetArg(wargs[n], XtNforeground, &gcv.foreground); n++; XtSetArg(wargs[n], XtNbackground, &gcv.background); n++; XtGetValues(w, wargs, n); gcv.font = font->fid; resource_struct->gc = XtGetGC(w, GCFont | GCForeground | GCBackground, &gcv); XtAddCallback(w, XmNexposeCallback, draw_button_label, resource_struct); return (w); } #define XMARGIN 4 #define YMARGIN 2 void draw_button_label( Widget w, RESOURCESTRUCT *r, XmDrawingAreaCallbackStruct *cb ) { Arg wargs[10]; int n; Dimension width, height, x, y, text_width, text_height; Dimension x1, y1, x2, y2; n = 0; XtSetArg( wargs[n], XmNwidth, &width ); n++; XtSetArg( wargs[n], XmNheight, &height ); n++; XtGetValues( w, wargs, n ); XmStringExtent( r->font_list, r->label_string, &text_width, &text_height ); x = (width - text_width) >> 1; y = (height - text_height) >> 1; XmStringDrawImage( XtDisplay( w ), cb->window, r->font_list, r->label_string, r->gc, x, y, width - y, XmALIGNMENT_BEGINNING, XmSTRING_DIRECTION_L_TO_R, NULL ); x1 = x - XMARGIN; y1 = height >> 1; x2 = width >> 2; y2 = y1; XDrawLine( XtDisplay( w ), cb->window, r->gc, x1, y1, x2, y2 ); x1 = x2; y2 = height - YMARGIN; XDrawLine( XtDisplay( w ), cb->window, r->gc, x1, y1, x2, y2 ); x1 = x + text_width + XMARGIN; y1 = height >> 1; x2 = (width >> 1) + (width >> 2); y2 = y1; XDrawLine( XtDisplay( w ), cb->window, r->gc, x1, y1, x2, y2 ); x1 = x2; y2 = height - YMARGIN; XDrawLine( XtDisplay( w ), cb->window, r->gc, x1, y1, x2, y2 ); } void usage( void ) { fprintf( stderr, "cdplayer: usage: cdplayer [-dev ]\n" ); exit( 1 ); } static void terminate( void ) { if (cdplayer) { CDclose( cdplayer ); } } /* * void * okcb(Widget w, int fatal, XmAnyCallbackStruct *cb) * * Description: * OK callback function for error message boxes. If fatal is * non-zero, we exit, otherwise we just unmanage ourselves. * * Parameters: * w Error message box * fatal Is this a fatal error message? * cb callback info */ /*ARGSUSED*/ static void okcb(Widget w, int fatal, XmAnyCallbackStruct *cb) { if (fatal) { exit(1); } else { XtUnmanageChild(w); } } /* * static void * ManageErrorBox(Widget w, void *idontcare, XEvent *event) * * Description: * Manage the error dialog box. This is an event handler for * the VisibilityNotify event of the parent of the error dialog * box. * * Parameters: * w parent of error dialog box * errorBox client data; the widget to manage * event VisibilityNotify event * cont continuation flag */ /*ARGSUSED*/ static void ManageErrorBox(Widget w, XtPointer errorBox, XEvent *event, Boolean *cont) { XtManageChild((Widget)errorBox); XtRemoveEventHandler(w, VisibilityChangeMask, False, ManageErrorBox, errorBox); } /* * XmString * CreateMotifString(char *str) * * Description: A utility function to create a * big ugly bloatif compound string * from a (possibly multi-line) string. * * Shamelessly stolen from printstatus. Thanks Dave! * * Returns: A compound string. * * WARNINGS: I don't free or reuse the returned string, so don't forget to * XmStringFree the returned string when you're done with it. * * CAVEATS: Empty lines are ignored. Put a space in (" \n") for a * blank line. * */ static XmString CreateMotifString(char *str) { XmString mstr, mnewline, mtempstr, mnextstr; register char *strptr, *tokptr, *dupstr; register int strnum; mnewline = XmStringSeparatorCreate(); /* * Parse the input string */ /* * Don't pass str itself to strtok, which writes NULLs into. Make * a copy of the input string, which is freed below. */ strptr = tokptr = dupstr = strdup(str); strnum = 0; mstr = NULL; while((strptr = strtok(tokptr, "\n")) != NULL) { tokptr = NULL; /* tell strtok to continue where we left off */ /* next time */ /* Two cases: first line, and subsequent lines. * Don't append mnewline after every line: * for cleanliness, append it only at begin of subsequent lines. */ if (!strnum++) { mstr = XmStringCreateSimple(strptr); } else { /* Append new line, being careful not to lose storage */ mtempstr = mstr; mstr = XmStringConcat(mstr, mnewline); XmStringFree(mtempstr); /* Now append the new string, being careful not to lose storage */ mtempstr = mstr; mnextstr = XmStringCreateSimple(strptr); mstr = XmStringConcat(mstr, mnextstr); XmStringFree(mtempstr); XmStringFree(mnextstr); } } XmStringFree(mnewline); free(dupstr); if (!mstr) { mstr = XmStringCreateSimple(""); } return mstr; } /* * void * PostError(Widget parent, char *errmsg, int fatal) * * Description: * Put up an error message box, containing the text from errmsg * * Parameters: * parent widget to be parent of message box if it needs to be * created. * errmsg the error message to display * fatal whether to exit when OK button is pressed */ static void PostError(Widget parent, char *errmsg, int fatal) { XmString xstr; int n; Arg wargs[10]; static Widget errorBox; if (!errorBox) { n = 0; XtSetArg(wargs[n], XmNdialogStyle, XmDIALOG_APPLICATION_MODAL); n++; errorBox = XmCreateErrorDialog(parent, "errorBox", wargs, n); XtUnmanageChild(XmMessageBoxGetChild(errorBox, XmDIALOG_CANCEL_BUTTON)); XtUnmanageChild(XmMessageBoxGetChild(errorBox, XmDIALOG_HELP_BUTTON)); } XtRemoveAllCallbacks(errorBox, XmNokCallback); XtAddCallback(errorBox, XmNokCallback, (XtCallbackProc)okcb, (XtPointer)fatal); xstr = CreateMotifString(errmsg); n = 0; XtSetArg(wargs[n], XmNmessageString, xstr); n++; XtSetValues(errorBox, wargs, n); XmStringFree(xstr); /* * Defer managing the message box until later if necessary so that * it will come up in a better place */ if (XtIsRealized(parent)) { /* * toplevel widget is already up, we can put up the dialog no * problem. */ XtManageChild(errorBox); } else { /* * toplevel widget isn't managed. When the toplevel widget * gets managed, its VisibilityNotify event list will get * called, and the error dialog will come up then. */ XtAddEventHandler(parent, VisibilityChangeMask, False, ManageErrorBox, (XtPointer)errorBox); } } /* * char * * GetResourceString(Widget w, char *name, char *class, char *def) * * Description: * Get a resource string for a widget. * * Parameters: * w Widget to get resource for * name resource name * class resource class * def default value * * Returns: * String value of the resource. Note that if def is non-NULL, * this will always be valid (in other words, if what you pass in * as def is guaranteed to be valid, you don't have to check the * return value of this function) */ static char * GetResourceString(Widget w, char *name, char *class, char *def) { XtResource res; char *str; res.resource_name = name; res.resource_class = class; res.resource_type = XtRString; res.resource_size = sizeof(char *); res.resource_offset = (unsigned int)&str; res.default_type = XtRString; res.default_addr = def; XtGetApplicationResources(w, NULL, &res, 1, NULL, 0); return str ? str : def; }