1 /* $Header: /home/cvs/wavplay/xltwavplay.c,v 1.2 1999/12/04 00:01:20 wwg Exp $
2  * Warren W. Gay VE3WWG		Thu Feb 13 21:11:22 1997
3  *
4  * 	X LessTif WAV Play :
5  *
6  * 	Copyright (C) 1997  Warren W. Gay VE3WWG
7  *
8  * This  program is free software; you can redistribute it and/or modify it
9  * under the  terms  of  the GNU General Public License as published by the
10  * Free Software Foundation version 2 of the License.
11  *
12  * This  program  is  distributed  in  the hope that it will be useful, but
13  * WITHOUT   ANY   WARRANTY;   without   even  the   implied   warranty  of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15  * Public License for more details (see enclosed file COPYING).
16  *
17  * You  should have received a copy of the GNU General Public License along
18  * with this  program; if not, write to the Free Software Foundation, Inc.,
19  * 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  * Send correspondance to:
22  *
23  * 	Warren W. Gay VE3WWG
24  *
25  * Email:
26  *	ve3wwg@yahoo.com
27  *	wgay@mackenziefinancial.com
28  *
29  * $Log: xltwavplay.c,v $
30  * Revision 1.2  1999/12/04 00:01:20  wwg
31  * Implement wavplay-1.4 release changes
32  *
33  * Revision 1.1.1.1  1999/11/21 19:50:56  wwg
34  * Import wavplay-1.3 into CVS
35  *
36  * Revision 1.4  1997/04/17 23:43:08  wwg
37  * Added #include <errno.h> for 1.0pl2 fix.
38  *
39  * Revision 1.3  1997/04/17 00:41:56  wwg
40  * Fixed to use member name .Errno vs original dangerous
41  * .errno reference. This presents a problem on platforms
42  * that make extern errno thread safe, but defining errno
43  * as a macro.
44  *
45  * Revision 1.2  1997/04/15 02:13:35  wwg
46  * A number of small pre-release fixes.
47  *
48  * Revision 1.1  1997/04/13 23:59:08  wwg
49  * Initial revision
50  */
51 #define TIMER1_MS	150		/* TimerProc1() */
52 
53 #define LessTif_Bug_radioBehavior 1	/* XmNradioBehavior busted in RowColumn widget (T/F) */
54 
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <unistd.h>
58 #include <stdarg.h>
59 #include <time.h>
60 #include <signal.h>
61 #include <string.h>
62 #ifndef FREEBSD
63 #include <getopt.h>
64 #endif
65 #include <errno.h>
66 #include <sys/types.h>
67 #include <sys/ipc.h>
68 #include <sys/sem.h>
69 #include <sys/wait.h>
70 #include <sys/stat.h>
71 #include <assert.h>
72 
73 #ifndef NO_EDITRES
74 #include <X11/Xmu/Editres.h>
75 #endif
76 
77 #include <Xm/Xm.h>
78 #include <Xm/MainW.h>
79 #include <Xm/Form.h>
80 #include <Xm/PushB.h>
81 #include <Xm/ArrowB.h>
82 #include <Xm/CascadeB.h>
83 #include <Xm/Text.h>
84 #include <Xm/Label.h>
85 #include <Xm/ToggleB.h>
86 #include <Xm/Separator.h>
87 #include <Xm/MessageB.h>
88 #include <Xm/ScrollBar.h>
89 #include <Xm/RowColumn.h>
90 #include <Xm/FileSB.h>
91 #include <Xm/List.h>
92 #include <Xm/Frame.h>
93 
94 #include "xmsprint.h"
95 #include "xltwavplay.h"
96 #include "wavplay.h"
97 #include "client.h"
98 
99 static char * const rcsid[] = {
100 	"@(#)xltwavplay.c $Revision: 1.2 $",
101 	_wavplay_h_,
102 	"@(#)xltwavplay version " WAVPLAY_VERSION
103 };
104 
105 #ifndef NO_EDITRES
106 extern void _XEditResCheckmessages(void);		/* EditRes event handler */
107 #endif
108 
109 extern pid_t svrPID;					/* Process ID of the wavplay server */
110 extern int svrIPC;					/* Message Queue ID for the wavplay server */
111 int cmdopt_x = 0;					/* This is controlled by bOptionsDebug */
112 int bRecorded = 0;					/* True after first record event */
113 
114 char *env_WAVPLAYPATH = WAVPLAYPATH;			/* Default pathname of executable /usr/local/bin/wavplay */
115 char *env_AUDIODEV = AUDIODEV;				/* Default compiled in audio device */
116 unsigned long env_AUDIOLCK = AUDIOLCK;			/* Default compiled in locking semaphore */
117 
118 /* FALLBACK RESOURCES : */
119 
120 static String fallback_resources[] = {
121 #ifndef INSTALLED_RESOURCE_FILE				/* if not installed as file xltwavplay use.. */
122 	"xltwavplay.geometry: +350+250",
123 	"xltwavplay*background: grey70",
124 	"xltwavplay*foreground: black",
125 
126 	"xltwavplay.*fontList: *helvetica-bold-r-normal--14*",
127 
128 	"xltwavplay.main.menubar.spacing: 10",
129 	"xltwavplay.main.menubar.filemenucascade.labelString: File",
130 	"xltwavplay*filemenu.select_files.labelString: Select Files..",
131 	"xltwavplay*filemenu.exit.labelString: Exit",
132 
133 	"xltwavplay*filemenu.save_as.labelString: Save " RECORD_PATH " As..",
134 
135 	"xltwavplay.main.menubar.optsmenucascade.labelString: Options",
136 	"xltwavplay*optsmenu.debug.labelString: Debug Mode",
137 	"xltwavplay*optsmenu.sem_reset.labelString: Reset locking semaphores",
138 
139 	"xltwavplay.main.menubar.helpmenucascade.labelString: Help",
140 	"xltwavplay*helpmenu.about.labelString: About",
141 
142 	/* Copyright notice: adjust font to suit */
143 	"xltwavplay*AboutDlg.fontList: -adobe-helvetica-medium-r-normal-*-10-100-75-75-p-*-iso8859-1",
144 
145 	"xltwavplay*FilesDlg.dialogTitle: Select file(s) to play",
146 	"xltwavplay*FilesDlg.pattern: *.wav",
147 	"xltwavplay*FilesDlg.fileTypeMask: FILE_REGULAR",
148 
149 	"xltwavplay*SaveAsDlg.dialogTitle: Select a file name to write",
150 	"xltwavplay*SaveAsDlg.pattern: *.wav",
151 	"xltwavplay*SaveAsDlg.fileTypeMask: FILE_REGULAR",
152 
153 	"xltwavplay*listbox_label.labelString: Selection:",
154 
155 	"xltwavplay*FileLbl0.labelString: File:",
156 	"xltwavplay*TypeLbl0.labelString: Type:",
157 	"xltwavplay*DateLbl0.labelString: Date:",
158 	"xltwavplay*SizeLbl0.labelString: Size:",
159 	"xltwavplay*SamplingRateLbl0.labelString: Sampling Rate:",
160 	"xltwavplay*SamplesLbl0.labelString: Samples:",
161 	"xltwavplay*TimeLbl0.labelString: Duration:",
162         "xltwavplay*TimeDisplayLbl0.labelString: SMPTE Time:",
163 
164 	"xltwavplay*FileLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
165 	"xltwavplay*FileLbl1.foreground: black",
166 	"xltwavplay*TypeLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
167 	"xltwavplay*TypeLbl1.foreground: black",
168 	"xltwavplay*DateLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
169 	"xltwavplay*DateLbl1.foreground: black",
170 	"xltwavplay*SizeLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
171 	"xltwavplay*SizeLbl1.foreground: black",
172 	"xltwavplay*SamplingRateLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
173 	"xltwavplay*SamplingRateLbl1.foreground: black",
174 	"xltwavplay*SamplesLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
175 	"xltwavplay*SamplesLbl1.foreground: black",
176 	"xltwavplay*TimeLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
177 	"xltwavplay*TimeLbl1.foreground: black",
178         "xltwavplay*TimeDisplayLbl1.labelString: 00:00:00.00",
179 
180 	"xltwavplay*Play.labelString: Play",
181 	"xltwavplay*StopPlay.labelString: Stop",
182 	"xltwavplay*Pause.labelString: Pause",
183 	"xltwavplay*Restore.labelString: Restore",
184 
185 	"xltwavplay*Record.labelString: Record",
186 	"xltwavplay*Stop.labelString: Stop",
187 
188 	"xltwavplay*8Bit.labelString: 8 bit",
189 	"xltwavplay*16Bit.labelString: 16 bit",
190 
191 	"xltwavplay*rate_lbl.labelString: Sampling Rate:",
192 
193 	"xltwavplay*rec_rate_tgl.labelString: Record Sampling Rate:",
194 #endif /* ndef INSTALLED_RESOURCE_FILE */
195 	NULL
196 };
197 
198 #define NoTMR	0UL					/* Value for "no timer" */
199 
200 static void SampleRateChg_Show(int newValue);
201 
202 /*
203  * WIDGETS:
204  */
205 Widget wParent;						/* Parent shell widget */
206 XtAppContext aContext;					/* Application context */
207 
208 Widget wMain;						/* Main widget */
209 Widget wMenuBar;					/* Main menu bar */
210 Widget wFileMenuCascade;				/* Cascade button for File Menu */
211 Widget wFileMenu;					/* File menu itself */
212 Widget wSelect;						/* File->Select */
213 Widget wSaveAs;						/* File->SaveAs.. */
214 Widget wExit;						/* File->Exit */
215 
216 Widget wOptionsMenuCascade;
217 Widget wOptionsMenu;
218 Widget wOptions_DebugTgl;
219 Boolean bOptionsDebug = False;				/* Debug mode when True */
220 Widget wOptions_SemReset;				/* Options->Semaphore Reset */
221 
222 Widget wHelpMenuCascade;				/* Cascade button for Help Menu */
223 Widget wHelpMenu;					/* Help menu itself */
224 Widget wAbout;						/* Help->About */
225 
226 Widget wErrDlg;						/* Error Dialog Box */
227 XmString sErrDlgTitle;					/* Title for the Error Dialog Box */
228 
229 Widget wAboutDlg;					/* About Dialog Box */
230 XmString sAboutDlgTitle;				/* About Dialog Title String */
231 XmString sAboutDlgMessage;				/* About Dialog message and copyright */
232 
233 Widget wFilesDlg;					/* File selection dialog */
234 Widget wSaveAsDlg;					/* File "Save As..." dialog */
235 
236 Widget wMainForm;					/* Main form widget */
237 
238 Widget wLeftRC;						/* Left main Row/Column */
239 Widget wListFrame;					/* Frame for Selections List Box */
240 Widget wListForm;					/* Form within Selections List Box */
241 Widget wSelectionsLbl;					/* Label for Selections List Box */
242 Widget wSelectionsListBox;				/* The selections List Box */
243 Widget wTimeFrame;                                     /* Frame for Time control */
244 Widget wTimeRC;                                                /* Row Col widget for Time control */
245 Widget wTimeDisplayRC;                                 /* Keeps display next to label */
246 Widget wTimeDisplayLbl0;                               /* Label for the time */
247 Widget wTimeDisplayLbl1;                               /* Actual time display */
248 Widget wTimeScrollBar;                                 /* Time scrollbar */
249 Widget wRateSBFrame;					/* Frame for the scroll bar */
250 Widget wRateRC;						/* Row Col widget for Rate scroll bar */
251 Widget wRateLbl;					/* Label for the scroll bar */
252 Widget wRateScrollBar;					/* The Sampling Rate Scroll Bar */
253 int SampRate_value = 0;					/* Current scroll bar value */
254 
255 Widget wRecRateFrame;					/* The record rate frame */
256 Widget wRecRateRC;
257 Widget wRecRateTgl;
258 Widget wRecRate;
259 int RecRateTgl_value = 0;
260 
261 Widget wDetailsFrame;					/* Frame for File Info Details */
262 Widget wDetailsRC;					/* Horizontal RC for wBotLeftRC* */
263 Widget wBotLeftRC0;					/* Holds labels File:, Type: etc. */
264 Widget wFileLbl0;
265 Widget wTypeLbl0;
266 Widget wDateLbl0;
267 Widget wSizeLbl0;
268 Widget wSamplingRateLbl0;
269 Widget wSamplesLbl0;
270 Widget wTimeLbl0;
271 
272 Widget wBotLeftRC1;					/* Holds labels to right of wBotLeftRC0 */
273 Widget wFileLbl1;
274 Widget wTypeLbl1;
275 Widget wDateLbl1;
276 Widget wSizeLbl1;
277 Widget wSamplingRateLbl1;
278 Widget wSamplesLbl1;
279 Widget wTimeLbl1;
280 
281 Widget wRightRC;					/* Right side Row/Column */
282 Widget wBitsFrame;					/* Frame holding BitsRC */
283 
284 Widget wBitsRC;						/* RowColumn that holds 8, 12, & 16 bits */
285 
286 Widget w8BitCB;						/* 8-bit check box */
287 Widget w16BitCB;					/* 16-bit check box */
288 int curBits = 0;					/* The current setting */
289 
290 Widget wPlayFrame;					/* Frame for play buttons */
291 Widget wPlayRC;						/* RC for play buttons */
292 Widget wPlayPB;						/* Play push button */
293 Widget wPausePB;					/* Pause push button */
294 Widget wStopPlayPB;					/* StopPlay push button */
295 Widget wRestorePB;					/* Restore push button */
296 
297 Widget wStereoFrame;					/* Frame for Stereo Toggle */
298 Widget wStereoCB;					/* Stereo Check Box */
299 
300 Widget wRecordFrame;
301 Widget wRecordRC;
302 Widget wRecordPB;					/* Record push button */
303 Widget wStopPB;						/* Stop push button */
304 
305 XmString sRecorded_wav;					/* XmString() of RECORD_PATH */
306 
307 /* Info required by the time display */
308 #define SMPTE_FPS 25
309 
310 typedef struct TimeDisplayInfo {
311        UInt32 sampling_rate;
312        UInt32 sample_num;
313        char sample_info_str[16];
314 } TimeDisplayInfo;
315 
316 TimeDisplayInfo time_display;
317 
318 /* Flag to show when the help text is displayed */
319 int help_text_showing = 1;
320 
321 /*
322  * This is an internal conveniance routine:
323  * In order for EditRes to function, we must register all shell widgets.
324  */
325 #ifndef NO_EDITRES
326 
327 static void
RegisterShellWidget(Widget w)328 RegisterShellWidget(Widget w) {
329 	XtAddEventHandler(w,(EventMask)0,True,_XEditResCheckMessages,NULL);
330 }
331 
332 #else
333 
334 #define RegisterShellWidget(w)				/* No EDITRES facility */
335 
336 #endif	/* ndef NO_EDITRES */
337 
338 /*
339  * This function initiates the creation of an error dialog with
340  * a meaningful error text and an OK button. Note that the dialog
341  * box is not operationaly until we return back to the main loop.
342  */
343 static void
ReportError(const char * ErrMsg)344 ReportError(const char *ErrMsg) {
345 	XmString s;							/* XmString form of message */
346 
347 	if ( XtIsManaged(wErrDlg) )
348 		return;							/* The dialog is already in use */
349 
350 	/*
351 	 * Set the Error Dialog message text, and manage it so that it pops up:
352 	 */
353 	s = XmStringCreateLtoR((char *)ErrMsg,XmSTRING_DEFAULT_CHARSET);/* We need an XmString */
354 	XtVaSetValues(wErrDlg,XmNmessageString,s,NULL);			/* Put msg in there */
355 	XtManageChild(wErrDlg);						/* Make it pop up */
356 	XmStringFree(s);						/* Release the string */
357 }
358 
359 /*
360  * Error function to format a message and report it:
361  */
362 static void
v_erf(const char * format,va_list ap)363 v_erf(const char *format,va_list ap) {
364 	char buf[1024];
365 
366 	vsprintf(buf,format,ap);			/* Format the message */
367 	fputs(buf,stderr);				/* Send to stderr */
368 	fputc('\n',stderr);				/* ..with a newline */
369 	ReportError(buf);				/* Now popup error message if we can */
370 }
371 
372 /*
373  * Format an error report:
374  */
375 void
ReportErrorf(const char * format,...)376 ReportErrorf(const char *format,...) {
377 	va_list ap;
378 
379 	va_start(ap,format);				/* Access the arguments list */
380 	v_erf(format,ap);				/* Go report the error */
381 	va_end(ap);					/* A formality for stdargs */
382 }
383 
384 /*
385  * This function extracts the Record sampling rate from the text input widget:
386  */
387 static UInt32
GetRecRate(void)388 GetRecRate(void) {
389 	char *pText = XmTextGetString(wRecRate);			/* Get contents */
390 	unsigned long ul;
391 
392 	if ( sscanf(pText,"%lu",&ul) != 1 ) {				/* Convert to u_int */
393 		if ( !*pText )
394 			ReportErrorf("No sampling rate has been specified.");
395 		else	ReportErrorf("Record sampling rate '%s' must be an integer.",pText);
396 		XtFree(pText);						/* Release this now */
397 		return 0UL;						/* Return no sampling rate */
398 	}
399 	XtFree(pText);							/* Release this now */
400 	XmSprintfText(wRecRate,"%lu",ul);				/* Reformat what we think we got */
401 	return (UInt32) ul;						/* Return sampling rate */
402 }
403 
404 /*
405  * This callback is called when the Record Sampling Rate input text widget gets
406  * the focus. When this happens, we turn off the toggle that takes the sampling
407  * rate from the slider.
408  */
409 static void
RecRateFocus_CB(Widget w,XtPointer client_data,XtPointer call_data)410 RecRateFocus_CB(Widget w,XtPointer client_data,XtPointer call_data) {
411 
412 	XtVaSetValues(wRecRateTgl,XmNset,False,NULL);			/* Disable toggle */
413 	RecRateTgl_value = 0;						/* And tracking value */
414 	XmTextSetInsertionPosition(wRecRate,0);				/* Cursor at start of field */
415 }
416 
417 /*
418  * This callback is called when the Record Sampling Rate toggle gets altered.
419  */
420 static void
RecRateTgl_CB(Widget w,XtPointer client_data,XtPointer call_data)421 RecRateTgl_CB(Widget w,XtPointer client_data,XtPointer call_data) {
422 	XmToggleButtonCallbackStruct *ptr = (XmToggleButtonCallbackStruct *)call_data;
423 
424 	if ( (RecRateTgl_value = ptr->set) != 0 )			/* Toggle set? */
425 		XmSprintfText(wRecRate,"%d",SampRate_value);		/* Yes, show rate from slider */
426 }
427 
428 /*
429  * This callback is entered when the [Play] button is pressed.
430  */
431 static void
Play_CB(Widget w,XtPointer client_data,XtPointer call_data)432 Play_CB(Widget w,XtPointer client_data,XtPointer call_data) {
433 	tosvr_play(0,v_erf);						/* Tell server to play */
434 }
435 
436 /*
437  * This callback is entered when the [Pause] button is pressed.
438  */
439 static void
Pause_CB(Widget w,XtPointer client_data,XtPointer call_data)440 Pause_CB(Widget w,XtPointer client_data,XtPointer call_data) {
441 	tosvr_pause(0,v_erf);						/* Tell server to pause */
442 }
443 
444 /*
445  * This callback is entered when the [Stop] button is pressed.
446  */
447 static void
Stop_CB(Widget w,XtPointer client_data,XtPointer call_data)448 Stop_CB(Widget w,XtPointer client_data,XtPointer call_data) {
449 	tosvr_stop(0,v_erf);						/* Tell the server to stop */
450 }
451 
452 /*
453  * This is the [Restore] settings button callback:
454  */
455 static void
Restore_CB(Widget w,XtPointer client_data,XtPointer call_data)456 Restore_CB(Widget w,XtPointer client_data,XtPointer call_data) {
457 
458 	tosvr_restore(0,v_erf);						/* Tell server to "restore" */
459         XtVaSetValues(wTimeScrollBar,XmNvalue,0,NULL);                  /* Fix time slider */
460         time_display.sample_num = 0;
461         XmSprintfLabel(wTimeDisplayLbl1,"00:00:00.00");
462 	XtVaSetValues(wRateScrollBar,XmNvalue,0,NULL);			/* Fix slider */
463 	SampleRateChg_Show(0);						/* Update sampling rate etc. */
464 }
465 
466 /*
467  * The [Record] button callback:
468  */
469 static void
Record_CB(Widget w,XtPointer client_data,XtPointer call_data)470 Record_CB(Widget w,XtPointer client_data,XtPointer call_data) {
471 	Arg al[20];						/* Arg list */
472 	Cardinal ac;						/* Args count */
473 	UInt32 RecRate = GetRecRate();				/* Sampling Rate */
474 	Chan chan_mode;						/* Stereo / Mono */
475 	UInt16 data_bits = curBits < 8 ? 8 : curBits;		/* Data bits */
476 	Boolean b;						/* Current setting of Stereo toggle */
477 
478 	if ( RecRate < 1 ) {					/* We must have a sampling rate */
479 		ReportErrorf("Please set a Sampling Rate\nand try again.");
480 		return;
481 	}
482 
483 	ac = 0;
484 	XtSetArg(al[ac],XmNset,&b); ++ac;
485 	XtGetValues(wStereoCB,al,ac);				/* Get Stereo toggle setting */
486 
487 	chan_mode = b ? Stereo : Mono;				/* Channels now known */
488 
489         time_display.sampling_rate = RecRate;                   /* Store for use during record */
490         sprintf(time_display.sample_info_str,"%u-bit %s",
491                 data_bits,
492                 chan_mode == Stereo ? "stereo" : "mono");
493 
494         /* Update a few fields */
495         XmSprintfLabel(wDateLbl1,"");   /* Don't need for rec; blank if help msg still there */
496         XmSprintfLabel(wSamplingRateLbl1,"%lu Hz",
497                 (unsigned long)RecRate);
498 
499 	tosvr_record(0,v_erf,chan_mode,RecRate,data_bits);	/* Tell server to start recording */
500 }
501 
502 /*
503  * This callback is entered when a selection has been made from the list box.
504  */
505 static void
PathSelected_CB(Widget w,XtPointer client_data,XtPointer call_data)506 PathSelected_CB(Widget w,XtPointer client_data,XtPointer call_data) {
507 	XmListCallbackStruct *ptr = (XmListCallbackStruct *)call_data;
508 	char *path;							/* C string copy */
509 
510 	XmStringGetLtoR(ptr->item,XmSTRING_DEFAULT_CHARSET,&path);	/* Get C string pathname */
511 	tosvr_path(path,0,v_erf);					/* Tell server the new path */
512 	XtFree(path);							/* Release this string */
513 }
514 
515 /*
516  * Internal routine for updating the sampling rate display fields:
517  */
518 static void
SampleRateChg_Show(int newValue)519 SampleRateChg_Show(int newValue) {
520 
521 	if ( newValue < (int) DSP_MIN ) {
522 		/*
523 		 * If the value is less than the minimum, assume no overrides here:
524 		 */
525 		XmSprintfLabel(wRateLbl,"Sampling Rate Override: NONE");
526 		SampRate_value = 0;
527 	} else	{
528 		/*
529 		 * Otherwise display the overridden sampling rate:
530 		 */
531 		XmSprintfLabel(wRateLbl,"Sampling Rate Override: %d Hz",(int)newValue);
532 		SampRate_value = (int)newValue;
533 	}
534 
535 	/*
536 	 * If the record toggle is set, then text box follows slider's value:
537 	 */
538 	if ( RecRateTgl_value != 0 )
539 		XmSprintfText(wRecRate,"%d",SampRate_value);
540 }
541 
542 /*
543  * This callback is entered when the time has changed (via scroll bar):
544  */
545 static void
TimeChg_CB(Widget w,XtPointer client_data,XtPointer call_data)546 TimeChg_CB(Widget w,XtPointer client_data,XtPointer call_data) {
547        XmScrollBarCallbackStruct *ptr = (XmScrollBarCallbackStruct *)call_data;
548        time_display.sample_num = (UInt32)ptr->value;
549 
550        /* wTimeDisplayLbl1 is updated as a result of the this server msg */
551        /* (since the server refreshes the client in response */
552        tosvr_start_sample(0,v_erf,(UInt32)ptr->value);
553 }
554 
555 /*
556  * This callback is entered when the sampling rate has changed (via scroll bar):
557  */
558 static void
SampleRateChg_CB(Widget w,XtPointer client_data,XtPointer call_data)559 SampleRateChg_CB(Widget w,XtPointer client_data,XtPointer call_data) {
560 	XmScrollBarCallbackStruct *ptr = (XmScrollBarCallbackStruct *)call_data;
561 
562 	SampleRateChg_Show(ptr->value);
563 	tosvr_sampling_rate(0,v_erf,(UInt32)ptr->value);
564 }
565 
566 /*
567  * This is the Stereo/Mono toggle callback:
568  */
569 static void
StereoChanged_CB(Widget w,XtPointer client_data,XtPointer call_data)570 StereoChanged_CB(Widget w,XtPointer client_data,XtPointer call_data) {
571 	XmToggleButtonCallbackStruct *ptr = (XmToggleButtonCallbackStruct *)call_data;
572 
573 	tosvr_chan(0,v_erf,ptr->set ? Stereo : Mono);		/* Tell the server about change */
574 }
575 
576 /*
577  * This internal function is used to set the radio buttons:
578  */
579 static void
BitsShow(void)580 BitsShow(void) {
581 	Widget wSet, wNot;
582 
583 	switch ( curBits ) {
584 	default :
585 		curBits = 8;
586 	case 8 :
587 		wSet = w8BitCB;
588 		wNot = w16BitCB;
589 		break;
590 	case 16 :
591 		wSet = w16BitCB;
592 		wNot = w8BitCB;
593 	}
594 
595 	XtVaSetValues(wSet,XmNset,True,NULL);
596 #if LessTif_Bug_radioBehavior
597 	/*
598 	 * Unless I've goofed somewhere, LessTif does not properly handle radio buttons:
599 	 */
600 	XtVaSetValues(wNot,XmNset,False,NULL);
601 #endif
602 }
603 
604 /*
605  * This callback is made when the bits per sample radio buttons have changed:
606  */
607 static void
BitsChanged_CB(Widget w,XtPointer client_data,XtPointer call_data)608 BitsChanged_CB(Widget w,XtPointer client_data,XtPointer call_data) {
609 	int bits = (int) (long) client_data;
610 
611 	tosvr_bits(0,v_erf,bits);			/* Tell server the new bits setting */
612 }
613 
614 /*
615  * This callback, is for the [OK] button in the About dialog:
616  */
617 static void
AboutDlgOkCB(Widget w,XtPointer client_data,XtPointer call_data)618 AboutDlgOkCB(Widget w,XtPointer client_data,XtPointer call_data) {
619 	if ( XtIsManaged(wAboutDlg) )
620 		XtUnmanageChild(wAboutDlg);		/* Pop down the About dialog */
621 }
622 
623 /*
624  * This callback is for the toggle in the Options->Debug menu selection:
625  */
626 static void
OptionsDebugCB(Widget w,XtPointer client_data,XtPointer call_data)627 OptionsDebugCB(Widget w,XtPointer client_data,XtPointer call_data) {
628 	Arg al[4];							/* Argument list */
629 	Cardinal ac;							/* Argument count */
630 
631 	ac = 0;
632 	XtSetArg(al[ac],XmNset,&bOptionsDebug); ++ac;			/* Request Get Toggle Value */
633 	XtGetValues(wOptions_DebugTgl,al,ac);				/* Get value now */
634 
635 	cmdopt_x = bOptionsDebug != False ? 1 : 0;			/* Update cmdopt_x also */
636 	tosvr_debug(0,v_erf,cmdopt_x);					/* And tell server the same */
637 }
638 
639 /*
640  * This callback is invoked when menu item Options->Semaphore Reset is activated.
641  */
642 static void
OptionsSemResetCB(Widget w,XtPointer client_data,XtPointer call_data)643 OptionsSemResetCB(Widget w,XtPointer client_data,XtPointer call_data) {
644 	tosvr_semreset(0,v_erf);				/* Request server semaphore reset */
645 }
646 
647 /*
648  * This callback is invoked by the Help->About menu selection:
649  */
650 static void
AboutCB(Widget w,XtPointer client_data,XtPointer call_data)651 AboutCB(Widget w,XtPointer client_data,XtPointer call_data) {
652 
653 	if ( XtIsManaged(wAboutDlg) )					/* For safety */
654 		return;							/* The dialog is already in use */
655 
656 	XtVaSetValues(wAboutDlg,XmNmessageString,sAboutDlgMessage,NULL);/* Supply message */
657 	XtManageChild(wAboutDlg);					/* Pop up the dialog now */
658 }
659 
660 /*
661  * This callback is invoked by Menu File->Select, to pop up the file selections dialog box.
662  */
663 static void
PopupSelectFilesCB(Widget w,XtPointer client_data,XtPointer call_data)664 PopupSelectFilesCB(Widget w,XtPointer client_data,XtPointer call_data) {
665 
666 	if ( XtIsManaged(wFilesDlg) )					/* Already managed? */
667 		return;							/* yes, just return */
668 	XtManageChild(wFilesDlg);					/* else, make visible now */
669 }
670 
671 /*
672  * This callback is invoked by menu File->Save As.., to start a dialog to allow
673  * saving the recorded.wav file somewhere else.
674  */
675 static void
PopupSaveAsCB(Widget w,XtPointer client_data,XtPointer call_data)676 PopupSaveAsCB(Widget w,XtPointer client_data,XtPointer call_data) {
677 
678 	if ( XtIsManaged(wSaveAsDlg) )					/* Is this already managed? */
679 		return;							/* Yes, just exit */
680 	XtManageChild(wSaveAsDlg);					/* else manage it now */
681 }
682 
683 /*
684  * This callback is invoked by Menu File->Exit, to do the fatal thing..
685  */
686 static void
ExitCB(Widget w,XtPointer client_data,XtPointer call_data)687 ExitCB(Widget w,XtPointer client_data,XtPointer call_data) {
688 
689 	exit(0); /* This invokes some atexit() calls */
690 }
691 
692 /*
693  * This callback is called when the user presses [OK] in the error dialog.
694  */
695 static void
ExitDlgCB(Widget w,XtPointer client_data,XtPointer call_data)696 ExitDlgCB(Widget w,XtPointer client_data,XtPointer call_data) {
697 
698 	if ( XtIsManaged(wErrDlg) )					/* Is it still managed? */
699 		XtUnmanageChild(wErrDlg);				/* Yes, unmanage it now */
700 }
701 
702 /*
703  * This internal function is used to add a new pathname to the list box:
704  */
705 static void
PutFileSelection(XmString xms_pathname)706 PutFileSelection(XmString xms_pathname) {
707 
708 	if ( XmListItemExists(wSelectionsListBox,xms_pathname) == False )
709 		XmListAddItemUnselected(wSelectionsListBox,xms_pathname,0); /* Append to list */
710 }
711 
712 /*
713  * This callback is invoked by the file selection dialog, to add another pathname to the
714  * list box.
715  */
716 static void
FilesDlgOKCB(Widget w,XtPointer client_data,XtPointer call_data)717 FilesDlgOKCB(Widget w,XtPointer client_data,XtPointer call_data) {
718 	XmFileSelectionBoxCallbackStruct *ptr = (XmFileSelectionBoxCallbackStruct *) call_data;
719 
720 	if ( !XmStringEmpty(ptr->value) )
721 		PutFileSelection(ptr->value);	/* Put this string into the selection box */
722 }
723 
724 /*
725  * This call back pops down the dialog box (for any dialog). The widget used for the pop down
726  * is expected to be passed in the client_data argument.
727  */
728 static void
DialogCancelCB(Widget w,XtPointer client_data,XtPointer call_data)729 DialogCancelCB(Widget w,XtPointer client_data,XtPointer call_data) {
730 	Widget wDlg = (Widget) client_data;				/* Get dialog widget */
731 
732 	if ( XtIsManaged(wDlg) )					/* Is it still managed? */
733 		XtUnmanageChild(wDlg);					/* yep, unmanage it now */
734 }
735 
736 /*
737  * This function dispatches wavplay server response messages:
738  */
739 static void
DispatchMsg(SVRMSG * msg)740 DispatchMsg(SVRMSG *msg) {
741 	XmString s;
742         double seconds;
743 
744         /* Erase the help text on receipt of first server message */
745         if (help_text_showing) {
746                 help_text_showing = 0;
747                 XmSprintfLabel(wFileLbl1,"");
748                 XmSprintfLabel(wTypeLbl1,"");
749                 XmSprintfLabel(wDateLbl1,"");
750                 XmSprintfLabel(wSizeLbl1,"");
751         }
752 
753 	if ( msg->msg_type == ToClnt_Bits ) {
754 		/*
755 		 * Server has responded with a new bits per sample setting:
756 		 */
757 		if ( msg->u.toclnt_bits.DataBits != 0 ) {		/* Ignore if value is zero.. */
758 			curBits = msg->u.toclnt_bits.DataBits;		/* Server indicates new setting */
759 			BitsShow();					/* Update our client displays */
760 		}
761 	} else if ( msg->msg_type == ToClnt_Settings ) {
762 		/*
763 		 * A bunch of settings have changed, according to the server:
764 		 */
765 		if ( msg->u.toclnt_settings.DataBits != 0 ) {		/* Ignore bits if zero.. */
766 			curBits = msg->u.toclnt_settings.DataBits;	/* Current server bit setting */
767 			BitsShow();					/* Reflect in controls */
768 		}
769 		XtVaSetValues(wStereoCB,XmNset,
770 			msg->u.toclnt_settings.Channels == Stereo ? True : False,
771 			NULL);						/* Show stereo setting */
772 		/*
773 		 * Update label text:
774 		 */
775 		XmSprintfLabel(wSamplingRateLbl1,"%lu Hz%s",
776 			(unsigned long)msg->u.toclnt_settings.SamplingRate,
777 			msg->u.toclnt_settings.bOvrSampling ? "*" : "");
778 
779 		if ( msg->u.toclnt_settings.bOvrSampling != 0 ) {
780 			SampleRateChg_Show((int)msg->u.toclnt_settings.SamplingRate);
781 			XtVaSetValues(wRateScrollBar,XmNvalue,msg->u.toclnt_settings.SamplingRate,NULL);
782 		} else	{
783 			SampleRateChg_Show(0);
784 			XtVaSetValues(wRateScrollBar,XmNvalue,0,NULL);
785 		}
786 
787                 /* Store this for the time display */
788                 time_display.sampling_rate = (int)msg->u.toclnt_settings.SamplingRate;
789 
790 		/*
791 		 * Currently, this value should be "PCM" (wav file type)
792 		 */
793 		msg->u.toclnt_settings.WavType[sizeof msg->u.toclnt_settings.WavType - 1] = 0;
794 		XmSprintfLabel(wTypeLbl1,"%s",msg->u.toclnt_settings.WavType);
795 
796 		/*
797 		 * Update samples, bits and channels:
798 		 */
799 		sprintf(time_display.sample_info_str,"%u-bit%s %s%s",
800 			(unsigned)msg->u.toclnt_settings.DataBits,
801 			msg->u.toclnt_settings.bOvrBits ? "*" : "",
802 			msg->u.toclnt_settings.Channels == Stereo ? "stereo" : "mono",
803 			msg->u.toclnt_settings.bOvrMode ? "*" : "");
804 
805                 XmSprintfLabel(wSamplesLbl1,"%lu %s",
806                         (unsigned long)msg->u.toclnt_settings.Samples,
807                         time_display.sample_info_str);
808 
809 		/*
810 		 * Update duration time:
811 		 */
812 		XmSprintfLabel(wTimeLbl1,"%.2lf seconds",
813 			(double)msg->u.toclnt_settings.Samples
814 				/ (double)msg->u.toclnt_settings.SamplingRate);
815 
816                 /* Update time display if required */
817                 seconds = time_display.sample_num / (double)time_display.sampling_rate;
818                 XmSprintfLabel(wTimeDisplayLbl1,"%02d:%02d:%02d.%02d",
819                         (int)seconds/3600,((int)seconds/60)%60,
820                         (int)seconds%60,
821                         (int)((seconds-(int)seconds)*SMPTE_FPS));
822                 XtVaSetValues(wTimeScrollBar,XmNincrement,
823                         time_display.sampling_rate/SMPTE_FPS,
824                         XmNpageIncrement,time_display.sampling_rate,NULL);
825 
826 	} else if ( msg->msg_type == ToClnt_WavInfo ) {
827 
828 		if ( msg->u.toclnt_wavinfo.Errno != 0 ) {
829 			/*
830 			 * Format an error message where the pathname should go:
831 			 */
832 			XmSprintfLabel(wFileLbl1,"<%s>",sys_errlist[msg->u.toclnt_wavinfo.Errno]);
833 			XmSprintfLabel(wDateLbl1,"");
834 			XmSprintfLabel(wSizeLbl1,"");
835 			XmSprintfLabel(wSamplesLbl1,"");
836 			XmSprintfLabel(wTimeLbl1,"");
837 		} else	{
838 			if ( msg->u.toclnt_wavinfo.wavinfo.DataBits != 0 ) {
839 				curBits = msg->u.toclnt_wavinfo.wavinfo.DataBits; /* Current server bit setting */
840 				BitsShow();				/* Reflect in controls */
841 			}
842 			XtVaSetValues(wStereoCB,XmNset,
843 				msg->u.toclnt_wavinfo.wavinfo.Channels == Stereo ? True : False,
844 				NULL);					/* Show stereo setting */
845 			/*
846 			 * Update label text:
847 			 */
848 			XmSprintfLabel(wSamplingRateLbl1,"%lu Hz",
849 				(unsigned long)msg->u.toclnt_wavinfo.wavinfo.SamplingRate);
850 
851 			XmSprintfLabel(wTypeLbl1,"%s","PCM");	/* For now... */
852 
853 			XmSprintfLabel(wSamplesLbl1,"%lu %u-bit %s",
854 				(unsigned long)msg->u.toclnt_wavinfo.wavinfo.Samples,
855 				(unsigned)msg->u.toclnt_wavinfo.wavinfo.DataBits,
856 				msg->u.toclnt_wavinfo.wavinfo.Channels == Stereo ? "stereo" : "mono");
857 
858 			XmSprintfLabel(wTimeLbl1,"%.2lf seconds",
859 				(double)msg->u.toclnt_wavinfo.wavinfo.Samples
860 					/ (double)msg->u.toclnt_wavinfo.wavinfo.SamplingRate);
861                         /*
862                          * Update time slider details:
863                          */
864                         time_display.sample_num = 0;
865                         time_display.sampling_rate = msg->u.toclnt_wavinfo.wavinfo.SamplingRate;
866                         XmSprintfLabel(wTimeDisplayLbl1,"00:00:00.00");
867                         {
868                         Arg al[5];
869                         int size = msg->u.toclnt_wavinfo.wavinfo.Samples;
870 
871                         XtSetArg(al[0],XmNmaximum,size + (size/10));
872                         XtSetArg(al[1],XmNsliderSize,size/10);
873                         XtSetArg(al[2],XmNincrement,time_display.sampling_rate/SMPTE_FPS);
874                         XtSetArg(al[3],XmNpageIncrement,time_display.sampling_rate);
875                         XtSetArg(al[4],XmNvalue,0);
876                         XtSetValues(wTimeScrollBar,al,5);
877                         }
878 		}
879 	} else if ( msg->msg_type == ToClnt_Stat ) {
880 		/*
881 		 * Update stat() info:
882 		 */
883 		if ( msg->u.toclnt_stat.Errno > 0 ) {
884 			/*
885 			 * Show stat() error:
886 			 */
887 			XmSprintfLabel(wFileLbl1,"<%s>",sys_errlist[msg->u.toclnt_stat.Errno]);
888 			XmSprintfLabel(wDateLbl1,"");
889 			XmSprintfLabel(wSizeLbl1,"");
890 			XmSprintfLabel(wSamplesLbl1,"");
891 			XmSprintfLabel(wTimeLbl1,"");
892 		} else	{
893 			/*
894 			 * Show stat() info:
895 			 */
896 			XmSprintfLabel(wDateLbl1,"%s",StrDate(msg->u.toclnt_stat.sbuf.st_mtime));
897 			XmSprintfLabel(wSizeLbl1,"%lu bytes",(unsigned long)msg->u.toclnt_stat.sbuf.st_size);
898 		}
899 
900 	} else if ( msg->msg_type == ToClnt_Path ) {
901 		/*
902 		 * Update pathname in panel display
903 		 */
904 		msg->u.toclnt_path.path[msg->bytes] = 0;
905 		XmSprintfLabel(wFileLbl1,"%s",msg->u.toclnt_path.path);
906 
907 		/*
908 		 * Highlight the pathname in the list box if we can (it should already be there)
909 		 */
910 		s = XmStringCreate(msg->u.toclnt_path.path,XmSTRING_DEFAULT_CHARSET);
911 		XmListSelectItem(wSelectionsListBox,s,False);
912 		XmStringFree(s);
913 
914 		if ( !bRecorded && !strcmp(msg->u.toclnt_path.path,RECORD_PATH) ) {
915 			s = XmStringCreateLtoR(RECORD_PATH,XmSTRING_DEFAULT_CHARSET);
916 			PutFileSelection(s);
917 			XmStringFree(s);
918 			bRecorded = 1;
919 		}
920         } else if ( msg->msg_type == ToClnt_PlayState ) {
921                 /*
922                  * Update time, duration and samples left during playback.
923                  */
924                 XmSprintfLabel(wSamplesLbl1,"%lu %s",
925                         (unsigned long)msg->u.toclnt_playstate.SamplesLeft,
926                         time_display.sample_info_str);
927 
928                 XmSprintfLabel(wTimeLbl1,"%.2lf seconds",
929                         (double)msg->u.toclnt_playstate.SamplesLeft
930                                 / (double)time_display.sampling_rate);
931                 time_display.sample_num = msg->u.toclnt_playstate.CurrentSample;
932                 seconds = time_display.sample_num / (double)time_display.sampling_rate;
933                 XmSprintfLabel(wTimeDisplayLbl1,"%02d:%02d:%02d.%02d",
934                         (int)seconds/3600,((int)seconds/60)%60,
935                         (int)seconds%60,
936                         (int)((seconds-(int)seconds)*SMPTE_FPS));
937                 XtVaSetValues(wTimeScrollBar,XmNvalue,time_display.sample_num);
938         } else if ( msg->msg_type == ToClnt_RecState ) {
939                 /*
940                  * Update size, number of samples and duration during record.
941                  */
942                 XmSprintfLabel(wSizeLbl1,"%lu bytes",(unsigned long)msg->u.toclnt_recstate.bytes_written);
943                 XmSprintfLabel(wSamplesLbl1,"%lu %s",
944                         (unsigned long)msg->u.toclnt_recstate.num_samples,
945                         time_display.sample_info_str);
946                 XmSprintfLabel(wTimeLbl1,"%.2lf seconds",
947                         (double)msg->u.toclnt_recstate.num_samples
948                                 / (double)time_display.sampling_rate);
949 	} else if ( msg->msg_type == ToClnt_ErrMsg )
950 		ReportError(msg->u.toclnt_errmsg.msg);
951 }
952 
953 /*
954  * This timed procedure just looks for incoming messages from the server:
955  */
956 static void
TimerProc1(XtPointer client_data,XtIntervalId * timer_id)957 TimerProc1(XtPointer client_data,XtIntervalId *timer_id) {
958 	SVRMSG msg;
959 	int z;
960 
961 	/*
962 	 * Process messages until we run out of messages:
963 	 */
964 rpt:	while ( (z = MsgFromServer(svrIPC,&msg,IPC_NOWAIT)) == 0 )
965 		DispatchMsg(&msg);				/* Act upon the server message */
966 
967 	if ( z != 0 && errno != ENOMSG ) {
968 		ReportErrorf("%s: Internal error from MsgFromServer()",sys_errlist[errno]);
969 		goto rpt;					/* There may be more messages */
970 	}
971 
972 	XtAppAddTimeOut(aContext,TIMER1_MS,TimerProc1,NULL);
973 }
974 
975 /*
976  * The main program needs no introduction:
977  */
978 int
main(int argc,char ** argv)979 main(int argc,char **argv) {
980 	Arg al[20];						/* Arg list */
981 	Cardinal ac;						/* Args count */
982 	char *cp;
983 
984 	/*
985 	 * Set environmental values:
986 	 */
987 	if ( (cp = getenv("WAVPLAYPATH")) != NULL )
988 		env_WAVPLAYPATH = cp;		/* Environment overrides compiled in default for WAVPLAYPATH */
989 
990 	if ( (cp = getenv("AUDIODEV")) != NULL )
991 		env_AUDIODEV = cp;		/* Environment overrides compiled in default for AUDIODEV */
992 
993 	if ( (cp = getenv("AUDIOLCK")) == NULL || sscanf(cp,"%lX",&env_AUDIOLCK) != 1 )
994 		env_AUDIOLCK = AUDIOLCK;	/* Use compiled in default, if no environment, or its bad */
995 
996 	/*
997 	 * CreateApplication Context:
998 	 */
999 	ac = 0;
1000 	XtSetArg(al[ac],XmNtitle,"X LessTif WAV Play version " WAVPLAY_VERSION); ++ac;
1001 	wParent = XtAppInitialize(&aContext,
1002 		"xltwavplay",				/* app. class */
1003 		(XrmOptionDescList) NULL, 0,		/* options */
1004 		&argc, argv,				/* cmd line */
1005 		fallback_resources,			/* fallback resources */
1006 		al,ac);					/* hard coded resources */
1007 
1008 	/*
1009 	 * Create XmString() version of RECORD_PATH for list box processing:
1010 	 */
1011 	sRecorded_wav = XmStringCreate(RECORD_PATH,XmSTRING_DEFAULT_CHARSET);
1012 
1013 	/*
1014 	 * Create Main widget:
1015 	 */
1016 	ac = 0;
1017 	wMain = XmCreateMainWindow(wParent,"main",al,ac);
1018 	XtManageChild(wMain);
1019 	RegisterShellWidget(wMain);			/* This is for Editres */
1020 
1021 	/*
1022 	 * Create the menu bar at the top of the main window:
1023 	 */
1024 	ac = 0;
1025 	wMenuBar = XmCreateMenuBar(wMain,"menubar",al,ac);
1026 	XtManageChild(wMenuBar);
1027 
1028 	/*
1029 	 * Create the FILE menu in the menu bar:
1030 	 */
1031 	CreateMenu(wMenuBar,&wFileMenu,"filemenu",&wFileMenuCascade,"filemenucascade",
1032 		"select_files",'M',PopupSelectFilesCB,&wSelect,	/* File->Select */
1033 		"save_as",'M',PopupSaveAsCB,&wSaveAs,		/* File->Save As.. */
1034 		"exit",'M',ExitCB,&wExit,			/* File->Exit */
1035 		NULL);
1036 
1037 	/*
1038 	 * Create the OPTIONS menu in the menu bar:
1039 	 */
1040 	CreateMenu(wMenuBar,&wOptionsMenu,"optsmenu",&wOptionsMenuCascade,"optsmenucascade",
1041 		"debug",'T',OptionsDebugCB,&wOptions_DebugTgl,	/* Options->Debug (toggle) */
1042 		"sem_reset",'M',OptionsSemResetCB,&wOptions_SemReset, /* Options->Semaphore Reset */
1043 		NULL);
1044 
1045 	/*
1046 	 * Create the HELP menu in the menu bar:
1047 	 */
1048 	CreateMenu(wMenuBar,&wHelpMenu,"helpmenu",&wHelpMenuCascade,"helpmenucascade",
1049 		"about",'M',AboutCB,&wAbout,			/* Help->About */
1050 		NULL);
1051 
1052 	/*
1053 	 * Create the error message dialog:
1054 	 */
1055 	sErrDlgTitle = XmStringCreate("X LessTif WAV Play : Error Message",XmSTRING_DEFAULT_CHARSET);
1056 	ac = 0;
1057 	XtSetArg(al[ac],XmNdefaultPosition,FALSE); ac++;
1058 	XtSetArg(al[ac],XmNdialogStyle,XmDIALOG_APPLICATION_MODAL); ac++;
1059 	XtSetArg(al[ac],XmNtitle,"Error Report"); ac++;
1060 	XtSetArg(al[ac],XmNdefaultButtonType,XmDIALOG_OK_BUTTON); ++ac;
1061 	XtSetArg(al[ac],XmNmessageAlignment,XmALIGNMENT_CENTER); ++ac;
1062 	XtSetArg(al[ac],XmNdialogTitle,sErrDlgTitle); ++ac;
1063 	XtSetArg(al[ac],XmNallowResize,FALSE); ++ac;
1064 	wErrDlg = XmCreateErrorDialog(wParent,"ErrDlg",al,ac);
1065 	XtAddCallback(wErrDlg,XmNokCallback,ExitDlgCB,NULL);
1066 	XtUnmanageChild(XmMessageBoxGetChild(wErrDlg,XmDIALOG_CANCEL_BUTTON));
1067 	XtUnmanageChild(XmMessageBoxGetChild(wErrDlg,XmDIALOG_HELP_BUTTON));
1068 	RegisterShellWidget(wErrDlg);
1069 
1070 	/*
1071 	 * Create About Dialog Message String:
1072 	 */
1073 	sAboutDlgTitle = XmStringCreate("About X LessTif WAV Play "WAVPLAY_VERSION,XmSTRING_DEFAULT_CHARSET);
1074 	sAboutDlgMessage = XmStringCreateLtoR(
1075 		"xltwavplay version " WAVPLAY_VERSION "\n"
1076 		"was written by and is\n"
1077 		"Copyright (C) 1997 by Warren W. Gay VE3WWG\n"
1078 		"---\n"
1079 		"Special thanks to Erik de Castro Lopo (erikd@zip.com.au)\n"
1080 		"for his contributed WAV header code\n"
1081 		"(files wavfile.c and wavfile.h)\n"
1082 		"---\n"
1083 		"Thanks also go to Andre Fuechsel for his original\n"
1084 		"wavplay that inspired the current work (though\n"
1085 		"this version does not contain his code)\n"
1086 		"---\n"
1087 		"Many thanks to the LINUX folks,\n"
1088 		"the XFree86 folks,\n"
1089 		"and the LessTif group\n"
1090 		"for making this application possible.\n"
1091 		"---\n"
1092 		"This program comes with\n"
1093 		"ABSOLUTELY NO WARRANTY.",
1094 		XmSTRING_DEFAULT_CHARSET);
1095 
1096 	/*
1097 	 * Create the About Dialog box:
1098 	 */
1099 	ac = 0;
1100 	XtSetArg(al[ac],XmNdefaultPosition,FALSE); ac++;
1101 	XtSetArg(al[ac],XmNtitle,"Error Report"); ac++;
1102 	XtSetArg(al[ac],XmNdefaultButtonType,XmDIALOG_OK_BUTTON); ++ac;
1103 	XtSetArg(al[ac],XmNmessageAlignment,XmALIGNMENT_CENTER); ++ac;
1104 	XtSetArg(al[ac],XmNdialogTitle,sAboutDlgTitle); ++ac;
1105 	XtSetArg(al[ac],XmNallowResize,FALSE); ++ac;
1106 	wAboutDlg = XmCreateMessageDialog(wParent,"AboutDlg",al,ac);
1107 	XtAddCallback(wAboutDlg,XmNokCallback,AboutDlgOkCB,NULL);
1108 	XtUnmanageChild(XmMessageBoxGetChild(wAboutDlg,XmDIALOG_CANCEL_BUTTON));
1109 	XtUnmanageChild(XmMessageBoxGetChild(wAboutDlg,XmDIALOG_HELP_BUTTON));
1110 	RegisterShellWidget(wAboutDlg);
1111 
1112 	/*
1113 	 * Create Files Selection Dialog:
1114 	 */
1115 	ac = 0;
1116 	XtSetArg(al[ac],XmNfileTypeMask,XmFILE_REGULAR); ++ac;
1117 	wFilesDlg = XmCreateFileSelectionDialog(wParent,"FilesDlg",al,ac);
1118 	XtAddCallback(wFilesDlg,XmNokCallback,FilesDlgOKCB,NULL);
1119 	XtAddCallback(wFilesDlg,XmNcancelCallback,DialogCancelCB,(XtPointer)wFilesDlg);
1120 	XtUnmanageChild(XmFileSelectionBoxGetChild(wFilesDlg,XmDIALOG_HELP_BUTTON));
1121 	RegisterShellWidget(wFilesDlg);
1122 
1123 	/*
1124 	 * Create "Save As..." Dialog
1125 	 */
1126 	ac = 0;
1127 	XtSetArg(al[ac],XmNfileTypeMask,XmFILE_REGULAR); ++ac;
1128 	wSaveAsDlg = XmCreateFileSelectionDialog(wParent,"SaveAsDlg",al,ac);
1129 	XtAddCallback(wSaveAsDlg,XmNokCallback,SaveAsDlgOKCB,NULL);
1130 	XtAddCallback(wSaveAsDlg,XmNcancelCallback,DialogCancelCB,(XtPointer)wSaveAsDlg);
1131 	XtUnmanageChild(XmFileSelectionBoxGetChild(wSaveAsDlg,XmDIALOG_HELP_BUTTON));
1132 	RegisterShellWidget(wSaveAsDlg);
1133 
1134 	/*
1135 	 * Create the main form widget:
1136 	 */
1137 	ac = 0;
1138 	wMainForm = XmCreateForm(wMain,"main_form",al,ac);
1139 	XtManageChild(wMainForm);
1140 
1141 	ac = 0;
1142 	XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1143 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1144 	wLeftRC = XmCreateRowColumn(wMainForm,"LeftRC",al,ac);
1145 	XtManageChild(wLeftRC);
1146 
1147 	ac = 0;
1148 	XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1149 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1150 	wListFrame = XmCreateFrame(wLeftRC,"list_frame",al,ac);
1151 	XtManageChild(wListFrame);
1152 
1153 	ac = 0;
1154 	wListForm = XmCreateForm(wListFrame,"list_form",al,ac);
1155 	XtManageChild(wListForm);
1156 
1157 	/*
1158 	 * Create the Selections List Box Label:
1159 	 */
1160 	ac = 0;
1161 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1162 	wSelectionsLbl = XmCreateLabel(wListForm,"listbox_label",al,ac);
1163 	XtManageChild(wSelectionsLbl);
1164 
1165 	/*
1166 	 * Create the Selections List Box:
1167 	 */
1168 	ac = 0;
1169 	XtSetArg(al[ac],XmNvisibleItemCount,7); ++ac;
1170 	XtSetArg(al[ac],XmNscrollingPolicy,XmSTATIC); ++ac;
1171 	XtSetArg(al[ac],XmNvisualPolicy,XmCONSTANT); ++ac;
1172 	XtSetArg(al[ac],XmNscrolledWindowMarginHeight,6); ++ac;
1173 	XtSetArg(al[ac],XmNscrolledWindowMarginWidth,6); ++ac;
1174 	XtSetArg(al[ac],XmNselectionPolicy,XmSINGLE_SELECT); ++ac;
1175 	XtSetArg(al[ac],XmNwidth,420); ++ac;
1176 	wSelectionsListBox = XmCreateScrolledList(wListForm,"lstbox",al,ac);
1177 	XtManageChild(wSelectionsListBox);
1178 	XtAddCallback(wSelectionsListBox,XmNsingleSelectionCallback,PathSelected_CB,NULL);
1179 
1180 	XtVaSetValues(wSelectionsLbl,
1181 		XmNtopAttachment,XmATTACH_FORM,
1182 		XmNleftAttachment,XmATTACH_FORM,
1183 		NULL);
1184 
1185 	XtVaSetValues(XtParent(wSelectionsListBox),
1186                 XmNtopAttachment,XmATTACH_WIDGET,XmNtopWidget,wSelectionsLbl,
1187 		XmNleftAttachment,XmATTACH_FORM,
1188 		NULL);
1189 
1190 	ac = 0;
1191 	XtSetArg(al[ac],XmNmarginHeight,12); ++ac;
1192 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1193 	wDetailsFrame = XmCreateFrame(wLeftRC,"DetailsFrame",al,ac);
1194 	XtManageChild(wDetailsFrame);
1195 
1196 	ac = 0;
1197 	XtSetArg(al[ac],XmNorientation,XmHORIZONTAL); ++ac;
1198 	wDetailsRC = XmCreateRowColumn(wDetailsFrame,"DetailsRC",al,ac);
1199 	XtManageChild(wDetailsRC);
1200 
1201 	ac = 0;
1202 	XtSetArg(al[ac],XmNorientation,XmVERTICAL); ++ac;
1203 	XtSetArg(al[ac],XmNentryAlignment,XmALIGNMENT_END); ++ac;
1204 	wBotLeftRC0 = XmCreateRowColumn(wDetailsRC,"BotLeftRC0",al,ac);
1205 	XtManageChild(wBotLeftRC0);
1206 
1207 	ac = 0;
1208 	XtSetArg(al[ac],XmNorientation,XmVERTICAL); ++ac;
1209 	XtSetArg(al[ac],XmNentryAlignment,XmALIGNMENT_BEGINNING); ++ac;
1210 	wBotLeftRC1 = XmCreateRowColumn(wDetailsRC,"BotLeftRC1",al,ac);
1211 	XtManageChild(wBotLeftRC1);
1212 
1213 	ac = 0;
1214 	wFileLbl0 = XmCreateLabel(wBotLeftRC0,"FileLbl0",al,ac);
1215 	XtManageChild(wFileLbl0);
1216 
1217 	ac = 0;
1218 	wTypeLbl0 = XmCreateLabel(wBotLeftRC0,"TypeLbl0",al,ac);
1219 	XtManageChild(wTypeLbl0);
1220 
1221 	ac = 0;
1222 	wDateLbl0 = XmCreateLabel(wBotLeftRC0,"DateLbl0",al,ac);
1223 	XtManageChild(wDateLbl0);
1224 
1225 	ac = 0;
1226 	wSizeLbl0 = XmCreateLabel(wBotLeftRC0,"SizeLbl0",al,ac);
1227 	XtManageChild(wSizeLbl0);
1228 
1229 	ac = 0;
1230 	wSamplingRateLbl0 = XmCreateLabel(wBotLeftRC0,"SamplingRateLbl0",al,ac);
1231 	XtManageChild(wSamplingRateLbl0);
1232 
1233 	ac = 0;
1234 	wSamplesLbl0 = XmCreateLabel(wBotLeftRC0,"SamplesLbl0",al,ac);
1235 	XtManageChild(wSamplesLbl0);
1236 
1237 	ac = 0;
1238 	wTimeLbl0 = XmCreateLabel(wBotLeftRC0,"TimeLbl0",al,ac);
1239 	XtManageChild(wTimeLbl0);
1240 
1241 	ac = 0;
1242 	wFileLbl1 = XmCreateLabel(wBotLeftRC1,"FileLbl1",al,ac);
1243 	XtManageChild(wFileLbl1);
1244 
1245 	ac = 0;
1246 	wTypeLbl1 = XmCreateLabel(wBotLeftRC1,"TypeLbl1",al,ac);
1247 	XtManageChild(wTypeLbl1);
1248 
1249 	ac = 0;
1250 	wDateLbl1 = XmCreateLabel(wBotLeftRC1,"DateLbl1",al,ac);
1251 	XtManageChild(wDateLbl1);
1252 
1253 	ac = 0;
1254 	wSizeLbl1 = XmCreateLabel(wBotLeftRC1,"SizeLbl1",al,ac);
1255 	XtManageChild(wSizeLbl1);
1256 
1257 	ac = 0;
1258 	wSamplingRateLbl1 = XmCreateLabel(wBotLeftRC1,"SamplingRateLbl1",al,ac);
1259 	XtManageChild(wSamplingRateLbl1);
1260 
1261 	ac = 0;
1262 	wSamplesLbl1 = XmCreateLabel(wBotLeftRC1,"SamplesLbl1",al,ac);
1263 	XtManageChild(wSamplesLbl1);
1264 
1265 	ac = 0;
1266 	wTimeLbl1 = XmCreateLabel(wBotLeftRC1,"TimeLbl1",al,ac);
1267 	XtManageChild(wTimeLbl1);
1268 
1269 	/*
1270          * Create the time scroll bar:
1271 	 */
1272 	ac = 0;
1273         XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1274         XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1275         wTimeFrame = XmCreateFrame(wLeftRC,"timeframe",al,ac);
1276         XtManageChild(wTimeFrame);
1277 
1278         ac = 0;
1279         wTimeRC = XmCreateRowColumn(wTimeFrame,"time_rolcol",al,ac);
1280         XtManageChild(wTimeRC);
1281 
1282         ac = 0;
1283         XtSetArg(al[ac],XmNorientation,XmHORIZONTAL); ++ac;
1284         wTimeDisplayRC = XmCreateRowColumn(wTimeRC,"timedisplay_rolcol",al,ac);
1285         XtManageChild(wTimeDisplayRC);
1286 
1287         ac = 0;
1288         wTimeDisplayLbl0 = XmCreateLabel(wTimeDisplayRC,"TimeDisplayLbl0",al,ac);
1289         XtManageChild(wTimeDisplayLbl0);
1290 
1291         ac = 0;
1292         wTimeDisplayLbl1 = XmCreateLabel(wTimeDisplayRC,"TimeDisplayLbl1",al,ac);
1293         XtManageChild(wTimeDisplayLbl1);
1294 
1295         ac = 0;
1296         XtSetArg(al[ac],XmNorientation,XmHORIZONTAL); ++ac;
1297         XtSetArg(al[ac],XmNminimum,0); ++ac;
1298         XtSetArg(al[ac],XmNsliderSize,4000); ++ac;
1299         XtSetArg(al[ac],XmNmaximum,0+4000); ++ac;
1300         XtSetArg(al[ac],XmNincrement,1); ++ac;
1301         wTimeScrollBar = XmCreateScrollBar(wTimeRC,"time_scrollbar",al,ac);
1302         XtManageChild(wTimeScrollBar);
1303         XtAddCallback(wTimeScrollBar,XmNvalueChangedCallback,TimeChg_CB,NULL);
1304 
1305         /*
1306          * Create the sampling rate scroll bar:
1307          */
1308         ac = time_display.sample_num = 0;
1309         time_display.sampling_rate = 1;
1310 	XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1311 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1312 	wRateSBFrame = XmCreateFrame(wLeftRC,"rateSBframe",al,ac);
1313 	XtManageChild(wRateSBFrame);
1314 
1315 	ac = 0;
1316 	wRateRC = XmCreateRowColumn(wRateSBFrame,"rate_rolcol",al,ac);
1317 	XtManageChild(wRateRC);
1318 
1319 	ac = 0;
1320 	wRateLbl = XmCreateLabel(wRateRC,"rate_lbl",al,ac);
1321 	XtManageChild(wRateLbl);
1322 
1323 	ac = 0;
1324 	XtSetArg(al[ac],XmNorientation,XmHORIZONTAL); ++ac;
1325 	XtSetArg(al[ac],XmNminimum,0); ++ac;			/* DSP_MIN is actual min */
1326 	XtSetArg(al[ac],XmNsliderSize,4000); ++ac;
1327 	XtSetArg(al[ac],XmNmaximum,DSP_MAX+4000); ++ac;
1328 	XtSetArg(al[ac],XmNincrement,100); ++ac;
1329 	wRateScrollBar = XmCreateScrollBar(wRateRC,"rate_scrollbar",al,ac);
1330 	XtManageChild(wRateScrollBar);
1331 	XtAddCallback(wRateScrollBar,XmNvalueChangedCallback,SampleRateChg_CB,NULL);
1332 
1333 	ac = 0;
1334 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1335 	XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1336 	wRecRateFrame = XmCreateFrame(wRateRC,"rec_rate_frame",al,ac);
1337 	XtManageChild(wRecRateFrame);
1338 
1339 	ac = 0;
1340 	XtSetArg(al[ac],XmNorientation,XmHORIZONTAL); ++ac;
1341 	XtSetArg(al[ac],XmNpacking,XmPACK_COLUMN); ++ac;
1342 	XtSetArg(al[ac],XmNnumColumns,1); ++ac;
1343 	wRecRateRC = XmCreateRowColumn(wRecRateFrame,"rec_rate_rc",al,ac);
1344 	XtManageChild(wRecRateRC);
1345 
1346 	ac = 0;
1347 	wRecRateTgl = XmCreateToggleButton(wRecRateRC,"rec_rate_tgl",al,ac);
1348 	XtManageChild(wRecRateTgl);
1349 	XtAddCallback(wRecRateTgl,XmNvalueChangedCallback,RecRateTgl_CB,NULL);
1350 
1351 	ac = 0;
1352 	XtSetArg(al[ac],XmNeditable,True); ++ac;
1353 	wRecRate = XmCreateText(wRecRateRC,"rec_rate",al,ac);
1354 	XtManageChild(wRecRate);
1355 	XtAddCallback(wRecRate,XmNfocusCallback,RecRateFocus_CB,NULL);
1356 
1357 	/*
1358 	 * Now build the right hand side:
1359 	 */
1360 	ac = 0;
1361 	XtSetArg(al[ac],XmNorientation,XmVERTICAL); ++ac;
1362 	XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1363 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1364 	XtSetArg(al[ac],XmNpacking,XmPACK_TIGHT); ++ac;
1365 	wRightRC = XmCreateRowColumn(wMainForm,"RightRC",al,ac);
1366 	XtManageChild(wRightRC);
1367 
1368 	ac = 0;
1369 	XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1370 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1371 	wBitsFrame = XmCreateFrame(wRightRC,"BitsFrame",al,ac);
1372 	XtManageChild(wBitsFrame);
1373 
1374 	ac = 0;
1375 	XtSetArg(al[ac],XmNorientation,XmVERTICAL); ++ac;
1376 	XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1377 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1378 	XtSetArg(al[ac],XmNpacking,XmPACK_TIGHT); ++ac;
1379 	XtSetArg(al[ac],XmNradioBehavior,True); ++ac;
1380 	wBitsRC = XmCreateRowColumn(wBitsFrame,"BitsRC",al,ac);
1381 	XtManageChild(wBitsRC);
1382 
1383 	ac = 0;
1384 	w8BitCB = XmCreateToggleButton(wBitsRC,"8Bit",al,ac);
1385 	XtManageChild(w8BitCB);
1386 	XtAddCallback(w8BitCB,XmNvalueChangedCallback,BitsChanged_CB,(XtPointer)8UL);
1387 
1388 	ac = 0;
1389 	w16BitCB = XmCreateToggleButton(wBitsRC,"16Bit",al,ac);
1390 	XtManageChild(w16BitCB);
1391 	XtAddCallback(w16BitCB,XmNvalueChangedCallback,BitsChanged_CB,(XtPointer)16UL);
1392 
1393 	ac = 0;
1394 	XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1395 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1396 	wPlayFrame = XmCreateFrame(wRightRC,"PlayFrame",al,ac);
1397 	XtManageChild(wPlayFrame);
1398 
1399 	ac = 0;
1400 	XtSetArg(al[ac],XmNorientation,XmVERTICAL); ++ac;
1401 	XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1402 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1403 	XtSetArg(al[ac],XmNpacking,XmPACK_TIGHT); ++ac;
1404 	wPlayRC = XmCreateRowColumn(wPlayFrame,"PlayRC",al,ac);
1405 	XtManageChild(wPlayRC);
1406 
1407 	ac = 0;
1408 	wPlayPB = XmCreatePushButton(wPlayRC,"Play",al,ac);
1409 	XtManageChild(wPlayPB);
1410 	XtAddCallback(wPlayPB,XmNactivateCallback,Play_CB,NULL);
1411 
1412 	ac = 0;
1413 	wPausePB = XmCreatePushButton(wPlayRC,"Pause",al,ac);
1414 	XtManageChild(wPausePB);
1415 	XtAddCallback(wPausePB,XmNactivateCallback,Pause_CB,NULL);
1416 
1417 	ac = 0;
1418 	wStopPlayPB = XmCreatePushButton(wPlayRC,"Stop",al,ac);
1419 	XtManageChild(wStopPlayPB);
1420 	XtAddCallback(wStopPlayPB,XmNactivateCallback,Stop_CB,NULL);
1421 
1422 	ac = 0;
1423 	wRestorePB = XmCreatePushButton(wPlayRC,"Restore",al,ac);
1424 	XtManageChild(wRestorePB);
1425 	XtAddCallback(wRestorePB,XmNactivateCallback,Restore_CB,NULL);
1426 
1427 	ac = 0;
1428 	XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1429 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1430 	wStereoFrame = XmCreateFrame(wRightRC,"StereoFrame",al,ac);
1431 	XtManageChild(wStereoFrame);
1432 
1433 	ac = 0;
1434 	wStereoCB = XmCreateToggleButton(wStereoFrame,"Stereo",al,ac);
1435 	XtManageChild(wStereoCB);
1436 	XtAddCallback(wStereoCB,XmNvalueChangedCallback,StereoChanged_CB,NULL);
1437 
1438 	ac = 0;
1439 	XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1440 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1441 	wRecordFrame = XmCreateFrame(wRightRC,"RecordFrame",al,ac);
1442 	XtManageChild(wRecordFrame);
1443 
1444 	ac = 0;
1445 	XtSetArg(al[ac],XmNorientation,XmVERTICAL); ++ac;
1446 	XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1447 	XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1448 	XtSetArg(al[ac],XmNpacking,XmPACK_TIGHT); ++ac;
1449 	wRecordRC = XmCreateRowColumn(wRecordFrame,"RecordRC",al,ac);
1450 	XtManageChild(wRecordRC);
1451 
1452 	ac = 0;
1453 	wRecordPB = XmCreatePushButton(wRecordRC,"Record",al,ac);
1454 	XtManageChild(wRecordPB);
1455 	XtAddCallback(wRecordPB,XmNactivateCallback,Record_CB,NULL);
1456 
1457 	ac = 0;
1458 	wStopPB = XmCreatePushButton(wRecordRC,"Stop",al,ac);
1459 	XtManageChild(wStopPB);
1460 	XtAddCallback(wStopPB,XmNactivateCallback,Stop_CB,NULL);
1461 
1462 	/*
1463 	 * Main form layout details:
1464 	 */
1465 	XtVaSetValues(wLeftRC,
1466 		XmNtopAttachment,XmATTACH_FORM,
1467 		XmNleftAttachment,XmATTACH_FORM,
1468 		NULL);
1469 
1470 	XtVaSetValues(wRightRC,
1471 		XmNtopAttachment,XmATTACH_FORM,
1472 		XmNleftAttachment,XmATTACH_WIDGET,		XmNleftWidget,wLeftRC,
1473 		XmNrightAttachment,XmATTACH_FORM,
1474 		NULL);
1475 
1476 	/*
1477 	 * Realize the widgets now:
1478 	 */
1479 	BitsShow();						/* Initialize bit settings */
1480 	XtVaSetValues(wOptions_DebugTgl,XmNset,bOptionsDebug ? True : False,NULL);
1481 	cmdopt_x = bOptionsDebug != False ? 1 : 0;
1482 	XtRealizeWidget(wParent);				/* OK, realize it all.. */
1483 
1484 	/*
1485 	 * Fork a new process, and try to start the server:
1486 	 */
1487 	if ( tosvr_start(v_erf) < 0 ) {
1488 		ReportErrorf("Failure to start the wavplay server is fatal.\n"
1489 			"Please check the executability of the 'wavplay' command.");
1490 		exit(3);
1491 	}
1492 
1493 	/*
1494 	 * Tell the server our debug setting:
1495 	 */
1496 	tosvr_debug(0,NULL,cmdopt_x);
1497 
1498 	/*
1499 	 * This work process receives and operates on messages
1500 	 * from the server to this client program:
1501 	 */
1502 	XtAppAddTimeOut(aContext,TIMER1_MS,TimerProc1,NULL);
1503 
1504 	/*
1505 	 * Clear out the "XXXXXXXXXXXX" in the initial resources,
1506 	 * to set the width of those labels.
1507 	 */
1508 	XmSprintfLabel(wFileLbl1,"No file(s) selected.");
1509 	XmSprintfLabel(wTypeLbl1,"Choose [File] -> [Select Files...]");
1510 	XmSprintfLabel(wDateLbl1,"then click on entries in the");
1511 	XmSprintfLabel(wSizeLbl1,"list box.");
1512 	XmSprintfLabel(wSamplingRateLbl1," ");
1513 	XmSprintfLabel(wSamplesLbl1," ");
1514 	XmSprintfLabel(wTimeLbl1," ");
1515 
1516 	/*
1517 	 * The MOTIF Main Loop:
1518 	 */
1519 	XtAppMainLoop(aContext);
1520 	return 0;						/* A cookie for the compiler */
1521 }
1522 
1523 /* $Source: /home/cvs/wavplay/xltwavplay.c,v $ */
1524