1 /*-
2  * Copyright (c) 1999 Thomas Runge (coto@core.de)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *
27  * Thanks to Roger Hardiman (roger@cs.strath.ac.uk) for helping
28  *             how to determine the signal strength. And of
29  *             course for his work on the driver
30  *           Randall Hopper (http://people.freebsd.org/~rhh/) for
31  *             his great fxtv
32  *           Matthias Scheler (tron@netbsd.de) for the first
33  *             bugreport, NetBSD support and his very helpful hints
34  *           Flemming Jacobsen (fj@dkuug.dk) for bugreports, hints
35  *             and suggestions.
36  *           Bernd Ernesti <bernd@arresum.inka.de> for additional
37  *             NetBSD support
38  *           John Preisler <john@vapornet.net> for requesting the
39  *             remote control feature (he was the second guy asking
40  *             for it) and the signal handlers.
41  *           Jamie Zawinski <jwz@netscape.com>. His netscape remote
42  *             control helped a lot implementing something similar
43  *             to xmradio
44  *           Ti Kan <ti@amb.org> for linux support
45  *           Karl Jeacle <karl@jeacle.ie> for the first
46  *             picture postcard :-)
47  *           Juha Nurmela <Juha.Nurmela@quicknet.inet.fi> for his new
48  *             driver and fixing some lesstif related problems
49  */
50 
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <unistd.h>
54 #include <errno.h>
55 #include <math.h>
56 #include <ctype.h>
57 #include <signal.h>
58 #include <sys/fcntl.h>
59 #include <sys/ioctl.h>
60 #include <sys/stat.h>
61 #include <sys/param.h>
62 #include <sys/wait.h>
63 #ifdef __NetBSD__
64 #include <dev/ic/bt8xx.h>
65 #include <soundcard.h>
66 #elif defined(linux)
67 #include <linux/bttv.h>
68 #include <sys/soundcard.h>
69 #elif defined(__DragonFly__)
70 #include <sys/soundcard.h>
71 #include <dev/video/bktr/ioctl_bt848.h>
72 #elif defined(__FreeBSD__)
73 #include <sys/soundcard.h>
74 #include <dev/bktr/ioctl_bt848.h>
75 #else
76 #include <machine/soundcard.h>
77 #endif
78 #ifdef JUHA_DRIVER
79 #include <machine/ioctl_tuner.h>
80 #endif
81 
82 #include <X11/X.h>
83 #include <X11/Xlib.h>
84 #include <X11/Intrinsic.h>
85 #include <X11/StringDefs.h>
86 #include <X11/Shell.h>
87 #include <X11/cursorfont.h>
88 #include <X11/Xmu/WinUtil.h>
89 
90 #ifdef HAS_XPM
91 #include <X11/xpm.h>
92 #include "icon.xpm"
93 #endif
94 
95 #include <Xm/XmAll.h>
96 #include "LiteClue.h"
97 
98 #include "radio.h"
99 #include "analyzer.h"
100 #include "sample.h"
101 #include "remote.h"
102 #include "config.h"
103 #include "misc.h"
104 #include "version_check.h"
105 #include "lcd_net.h"
106 #ifdef HAS_XPM
107 #include "tom.xpm"
108 #endif
109 #include "tom.xbm"
110 #include "icon.xbm"
111 #include "icon_mask.xbm"
112 
113 #if (XmVERSION < 2)
114 enum { XmUNSET, XmSET, XmINDETERMINATE };
115 #endif
116 
117 #define RCFILENAME ".xmradiorc"
118 #define DEFBTNTAG  "X"
119 #define NODEFBTNTAG  "-"
120 
121 #define MINVOL 0
122 #define MAXVOL 100
123 #define VOLSTEP 1
124 
125 #define CONNECT 1
126 #define DISCONNECT 2
127 
128 #ifndef BANDWIDTH
129 #define BANDWIDTH 15
130 #endif
131 
132 /* devices */
133 static int tuner;
134 int mixer;
135 static int dsp;
136 char *TUNER_DEVICE;
137 char *MIXER_DEVICE;
138 char *DSP_DEVICE;
139 
140 /* some values */
141 static int volume;
142 static int balance;
143 static int treble;
144 static int bass;
145 static int stereo;
146 static int afc;
147 static int chnlset;
148 
149 /* lcdproc support */
150 int sockfd;
151 Widget lcdConnectW, lcdDisconnectW;
152 const char *global_station_name;
153 int lcd_stereo;
154 int lcd_fieldstrength;
155 
156 /* some flags */
157 char *startStation;
158 int startStationPos;
159 static int initHack;
160 static int useLcdProc;
161 static int connectToLCDOnStartup;
162 static int wantLiteClue;
163 static int seeking;
164 static int seekstop;
165 static int quit_command;
166 static int gui;
167 #define MINIMAL_GUI  1
168 #define NORMAL_GUI   2
169 #define EXPANDED_GUI 3
170 #define TINY_GUI     4
171 
172 static int slider_mode;
173 #define VOLUME_MODE  1
174 #define FREQ_MODE    2
175 #define STATION_MODE 3
176 XmString volume_label_string, frequency_label_string, stations_label_string;
177 XmString def_label_string, stereo_label_string, mono_label_string;
178 
179 XtAppContext app_con;
180 Widget mainW, sliderFormW, freqFormW, soundFormW, buttonFormW, toggleFormW;
181 Widget stabtnW, nostationslabelW, afcW, muteW, stereoW, stationPopupW;
182 Widget stationUpW, stationDownW, seekDownW, seekUpW, buttonSepW, guiW, volFormW;
183 Widget menuW, moreW, versionW;
184 Widget quitW, aboutW, analyzerW, sampleW, configW, liteClue, fieldstrengthW;
185 
186 Widget freqW, freqDummyW, freqValueW, freqButtonW, volW;
187 Widget volumeW, volumeValueW, volumeLabelW;
188 Widget balanceW, balanceValueW, balanceLabelW;
189 Widget trebleW, trebleValueW, trebleLabelW;
190 Widget bassW, bassValueW, bassLabelW;
191 
192 extern Widget format_optionW, capture_optionW, stereo_optionW, rate_optionW;
193 extern Widget recordB, stopB, playbackB, dismissB, searchB, filename_textW;
194 extern Widget overwriteW;
195 
196 #ifdef JUHA_DRIVER
197 Widget indicatorW;
198 static Widget create_radio_indicator(char *name, Widget parent);
199 static void update_radio_indicator(Widget w);
200 #endif
201 
202 int SetSound(int dev, int arg);
203 int SetStation(const char* name);
204 int SetVolume(int);
205 int SetBalance(int);
206 int SetTreble(int);
207 int SetBass(int);
208 int SetAFC(int);
209 int SetStereo(int);
210 void SetStereoIndicator(int);
211 void UpdateStatus(XtPointer clientData, XtIntervalId *id);
212 
213 enum slider { FREQUENCY=0, VOL, VOLUME, BALANCE, TREBLE, BASS };
214 static const struct
215 {
216  Widget *widget;
217  Widget *widgetValue;
218  int (*func)(int);
219 } sliderTable[] =
220 {
221  { &freqW, &freqValueW, SetFrequency },
222  { &volW, &volumeValueW, SetVolume },
223  { &volumeW, &volumeValueW, SetVolume },
224  { &balanceW, &balanceValueW, SetBalance },
225  { &trebleW, &trebleValueW, SetTreble },
226  { &bassW, &bassValueW, SetBass }
227 };
228 
229 static const struct
230 {
231  Widget *widget;
232  const char *helpText;
233 } liteClueTable[] =
234  {
235   { &freqW,      "tune frequency" },
236   { &volW,       "adjust volume" },
237   { &freqButtonW, "toggle between volume slider, frequency slider and buttons" },
238   { &volumeW,    "adjust volume" },
239   { &balanceW,   "adjust balance" },
240   { &trebleW,    "adjust treble" },
241   { &bassW,      "adjust bass" },
242 #ifdef linux
243   { &afcW,       "automatic frequency control (doesn't work on Linux)" },
244 #else
245   { &afcW,       "automatic frequency control" },
246 #endif
247   { &muteW,      "stop sound" },
248 #ifdef linux
249   { &stereoW,    "stereo/mono (doesn't work on Linux)" },
250 #else
251   { &stereoW,    "stereo/mono" },
252 #endif
253   { &stationDownW,"previous registered station" },
254   { &stationUpW, "next registered station" },
255 #ifdef linux
256   { &seekDownW,  "seek station down (doesn't work on Linux)" },
257   { &seekUpW,    "seek station up (doesn't work on Linux)" },
258   { &fieldstrengthW, "field strength (doesn't work on Linux)" },
259 #else
260   { &seekDownW,  "seek station down" },
261   { &seekUpW,    "seek station up" },
262   { &fieldstrengthW, "field strength" },
263 #endif
264   { &buttonSepW, "Wow! Tooltips on separator widgets!" },
265   { &guiW,       "show/hide mixer" },
266   { &quitW,      "quit program" },
267   { &aboutW,     "about" },
268   { &analyzerW,  "start/stop analyzer" },
269   { &moreW,      "more..." },
270   { &sampleW,    "record to file" },
271   { &configW,    "shows configuration dialog" },
272   { &versionW,   "check for new version" },
273   { &searchB,    "search directories" },
274   { &dismissB,   "dismiss dialog" },
275   { &playbackB,  "start playing" },
276   { &stopB,      "stop recording/playing" },
277   { &recordB,    "start recording" },
278   { &overwriteW, "check, if file should be overwritten without asking" },
279   { &rate_optionW,    "sample speed" },
280   { &stereo_optionW,  "mono/stereo" },
281   { &capture_optionW, "choose sample format" },
282   { &format_optionW,  "choose file format" },
283   { &filename_textW,  "record/play this file" },
284   { &lcdConnectW,     "connect to LCD" },
285   { &lcdDisconnectW,  "disconnect from LCD" }
286  };
287 
288 typedef struct
289 {
290  int freq;
291  int strength;
292 } search_point;
293 
294 /* station list */
295 Stations station;
296 
297 /* which station on which button */
298 StationButtons station_buttons;
299 
300 static String fbres[] =
301 {
302 #ifdef linux
303  "*tunerDevice:               /dev/radio",
304 #else
305  "*tunerDevice:               /dev/tuner",
306 #endif
307  "*mixerDevice:               /dev/mixer",
308 #ifdef __NetBSD__
309  "*dspDevice:                 /dev/sound",
310 #else
311  "*dspDevice:                 /dev/dsp",
312 #endif
313  "*startFrequency:            87.50",
314  "*gui:                       mixer",
315  "*frequency.labelString:    Frequency",
316  "*volume.labelString:       Volume",
317  "*stations.labelString:     Stations",
318  "*startMode:                volume",
319  "*.background:               #bfbfbf",
320  "*.font:                     -*-lucida-medium-r-*-*-11-*-*-*-*-*-*-*",
321  "*.fontList:                 -*-lucida-medium-r-*-*-11-*-*-*-*-*-*-*",
322  "*debug:                     false",
323  "*XcgLiteClue.background:    yellow",
324  "*XcgLiteClue.foreground:    black",
325  "*fieldStrength.showArrows:  False",
326  "*analyzer.title:            Realtime Analyzer",
327  "*analyzer.width:            250",
328  "*analyzer.height:           60",
329  "*config_dialog.width:       450",
330  "*config_dialog.height:      350",
331  "*volumeLabel.labelString:   Volume",
332  "*freqButton.labelString:    Vol/Freq/Stn",
333  "*balanceLabel.labelString:  Balance",
334  "*trebleLabel.labelString:   Treble",
335  "*bassLabel.labelString:     Bass",
336  "*no_stations_label.labelString: no station predefined",
337  "*volumeValue.labelString:   0",
338  "*balanceValue.labelString:  0",
339  "*trebleValue.labelString:   0",
340  "*bassValue.labelString:     0",
341  "*quit_button.labelString:   Quit",
342  "*about_button.labelString:  About",
343  "*sample_button.labelString: Sample",
344  "*config_button.labelString: Config...",
345  "*analyzer_button.labelString: Analyzer",
346  "*version_check_button.labelString: Check Version",
347  "*lcd_connect_button.labelString: Connect to LCD",
348  "*lcd_disconnect_button.labelString: Disconnect from LCD",
349  "*AboutDialog.dialogTitle:   About",
350  "*afc.labelString:           AFC",
351  "*stereo.labelString:        stereo",
352  "*mono.labelString:          mono",
353  "*mute.labelString:          mute",
354  "*baseTranslations:          #override\\n\\ <Btn3Down>: StationPopup()	\n\
355                              Shift<Btn4Down>,<Btn4Up>: StationSeekUp()	\n\
356                              Shift<Btn5Down>,<Btn5Up>:StationSeekDown()	\n\
357                              <Btn4Down>,<Btn4Up>: StationUp()		\n\
358                              <Btn5Down>,<Btn5Up>: StationDown()		\n\
359                              <Key>plus: StationUp()			\n\
360                              <Key>minus: StationDown()			\n\
361                              <Key>KP_Add: StationUp()			\n\
362                              <Key>KP_Subtract: StationDown()",
363  "*more.baseTranslations:     #override \\n\\ <Btn1Down>:  MorePopup()	\n\
364                              <Btn3Down>: StationPopup()			\n\
365                              Shift<Btn4Down>,<Btn4Up>:   StationSeekUp()\n\
366                              Shift<Btn5Down>,<Btn5Up>:   StationSeekDown()  \n\
367                              <Btn4Down>,<Btn4Up>:   StationUp()		\n\
368                              <Btn5Down>,<Btn5Up>:   StationDown()	\n\
369                              <Key>plus:   StationUp()			\n\
370                              <Key>minus:  StationDown()			\n\
371                              <Key>KP_Add: StationUp()			\n\
372                              <Key>KP_Subtract: StationDown()",
373  "*config_dialog.*.baseTranslations: #override \n\
374                              <Btn4Down>,<Btn4Up>: ConfScrollUp() \n\
375                              <Btn5Down>,<Btn5Up>: ConfScrollDown()",
376  "*sample_dialog.title:       Sampling",
377  "*config_dialog.title:       Configuration",
378  "*filename_label.labelString:Filename:",
379  "*format_label.labelString:  FileFormat:",
380  "*capture_label.labelString: Capture:",
381  "*raw_format.labelString:    RAW",
382  "*au_format.labelString:     AU",
383  "*wav_format.labelString:    WAV",
384  "*voc_format.labelString:    VOC",
385  "*aiff_format.labelString:   AIFF",
386  "*mp3_format.labelString:    MP3",
387  "*overwrite_button.labelString:  Ask before overwriting file",
388  "*record_button.labelString: Record",
389  "*stop_button.labelString:   Stop",
390  "*playback_button.labelString: Playback",
391  "*dismiss_button.labelString:  Dismiss",
392  "*question.cancelLabelString: No",
393  "*question.okLabelString:     Yes",
394  "*filename_search.labelString: ...",
395  "*save_button.labelString:    Save",
396  "*apply_button.labelString:   Apply",
397  "*cancel_button.labelString:  Cancel",
398  "*delEntry_button.labelString:Delete Entry",
399  "*newEntry_button.labelString:New Entry",
400  "*delLabel.labelString:       Delete",
401  "*statLabel.labelString:      Station",
402  "*freqLabel.labelString:      Frequency",
403  "*defLabel.labelString:       Station Button",
404  "*def.labelString:            define",
405  "*start.labelString:          start",
406  "*visualization_mode.labelString: Visualization",
407  "*analyzer_mode.labelString:  Analyzer",
408  "*scope_mode.labelString:     Scope",
409  "*refresh_mode.labelString:   Refresh",
410  "*vis_analyzer.labelString:   Analyzer",
411  "*vis_scope.labelString:      Scope",
412  "*ana_normal.labelString:     Normal",
413  "*ana_fire.labelString:       Fire",
414  "*ana_vert.labelString:       Vertical Lines",
415  "*ana_lines.labelString:      Lines",
416  "*ana_bars.labelString:       Bars",
417  "*scope_dot.labelString:      Dot Scope",
418  "*scope_line.labelString:     Line Scope",
419  "*scope_solid.labelString:    Solid Scope ",
420  "*refresh_full.labelString:   Full (~50 fps)",
421  "*refresh_half.labelString:   Half (~25 fps)",
422  "*refresh_quarter.labelString:  Quarter (~13 fps)",
423  "*refresh_eight.labelString:  Eight (~6 fps)",
424  NULL
425 };
426 
427 extern void _XEditResCheckMessages(Widget, XtPointer, XEvent*, Boolean*);
428 
429 void ParseStationList();
430 void ParseStationListAppDef();
431 void ParseStationButtonListAppDef();
432 void CreateMenuWidget(char *widget_name, Widget parent);
433 void MakeMotifInterface();
434 int UpdateMixerSlider();
435 void UpdateSliderValue(int, int, int);
436 int UpdateChanges();
437 int CalcFieldStrength();
438 void UpdateFieldstrength(int strength);
439 void more_popup(Widget, XEvent*, String*, Cardinal*);
440 void station_popup(Widget, XEvent*, String*, Cardinal*);
441 void station_up(Widget, XEvent*, String*, Cardinal*);
442 void station_down(Widget, XEvent*, String*, Cardinal*);
443 void station_seek_up(Widget, XEvent*, String*, Cardinal*);
444 void station_seek_down(Widget, XEvent*, String*, Cardinal*);
445 static void HandleXEvents();
446 static XmString xmStringFromInt(int value, int decimalPoints);
447 #ifdef HAS_XPM
448 Pixmap skin;
449 void SetSkin();
450 #endif
451 
452 void InitHack()
453 {
454 #ifdef linux
455  return;
456 #else
457 
458 #ifdef JUHA_DRIVER
459  return;
460 #else
461 
462  int channel=3;
463 
464  if(!initHack)
465   return;
466 
467  if(ioctl(tuner, TVTUNER_SETCHNL, &channel) == -1)
468  {
469   fprintf(stderr, "TVTUNER_SETCHNL failed in InitHack(): %s\n",
470                     strerror(errno));
471  }
472 #endif
473 #endif
474 }
475 
476 int InitTuner(int startfreq)
477 {
478 #ifdef linux
479  struct video_audio arg;
480 #else
481  int new_audio;
482 #endif
483 
484  if((tuner = open(TUNER_DEVICE, O_RDONLY)) == -1)
485  {
486   fprintf(stderr, "could not open %s: %s\n", TUNER_DEVICE, strerror(errno));
487   tuner = -1;
488   return False;
489  }
490 
491 #ifdef linux
492 
493  if (ioctl(tuner, VIDIOCGAUDIO, &arg))
494  {
495   fprintf(stderr, "VIDIOCGAUDIO failed in InitTuner(): %s\n", strerror(errno));
496  }
497  if (arg.volume == 0)
498   arg.volume = 65535;
499  arg.flags &= ~VIDEO_AUDIO_MUTE;
500  if(ioctl(tuner, VIDIOCSAUDIO, &arg))
501  {
502   fprintf(stderr, "VIDIOCSAUDIO failed in InitTuner(): %s\n", strerror(errno));
503  }
504 
505 #else
506 
507 #ifdef JUHA_DRIVER
508  new_audio = AUDIO_TUNER;
509  if(ioctl(tuner, TUNER_SAUDIO, &new_audio) == -1)
510  {
511   fprintf(stderr, "could not set audio to tuner: %s\n", strerror(errno));
512  }
513 #else
514 
515  InitHack();
516 
517  // explicitly set channel set (problem with older drivers)
518  if(ioctl(tuner, TVTUNER_SETTYPE, &chnlset) == -1)
519  {
520   fprintf(stderr, "TVTUNER_SETTYPE failed in InitTuner(); %s\n",
521                     strerror(errno));
522  }
523 
524  // better switch it off at the beginning (problem with older drivers)
525  SetAFC(False);
526 
527  new_audio = AUDIO_INTERN;
528  if(ioctl(tuner, BT848_SAUDIO, &new_audio) == -1)
529  {
530   fprintf(stderr, "could not set audio to intern: %s\n", strerror(errno));
531  }
532 #endif
533 
534 #endif
535 
536  SetFrequency(startfreq);
537 
538  return(True);
539 }
540 
541 int InitMixer()
542 {
543  int ctrls, ret;
544 
545  ret = True;
546 
547  if((mixer = open(MIXER_DEVICE, O_RDWR)) == -1)
548  {
549   fprintf(stderr, "could not open %s: %s\n", MIXER_DEVICE, strerror(errno));
550   mixer = -1;
551   return(False);
552  }
553 
554  if(ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &ctrls) == -1)
555  {
556   fprintf(stderr, "%s query for devices failed: %s\n", MIXER_DEVICE, strerror(errno));
557   close(mixer);
558   mixer = -1;
559   return(False);
560  }
561 
562  if(!(ctrls & SOUND_MASK_LINE))
563  {
564   fprintf(stderr, "Can't control radio volume via %s: %s\n", MIXER_DEVICE, strerror(errno));
565   mixer = -1;
566   ret = False;
567  }
568 
569  if(!(ctrls & SOUND_MASK_BASS))
570  {
571   fprintf(stderr, "Can't control bass via %s: %s\n", MIXER_DEVICE, strerror(errno));
572   bass = -1;
573   ret = False;
574  }
575 
576  if(!(ctrls & SOUND_MASK_TREBLE))
577  {
578   fprintf(stderr, "Can't control treble via %s: %s\n", MIXER_DEVICE, strerror(errno));
579   treble = -1;
580   ret = False;
581  }
582 
583  if(ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &ctrls) == -1)
584  {
585   fprintf(stderr, "%s query for stereo capable device: %s\n", MIXER_DEVICE, strerror(errno));
586   ret = False;
587  }
588 
589  if(!(ctrls & SOUND_MASK_LINE))
590   stereo = False;
591 
592  if(ioctl(mixer, SOUND_MIXER_READ_RECMASK, &ctrls) == -1)
593  {
594   fprintf(stderr, "%s query for record capable device: %s\n", MIXER_DEVICE, strerror(errno));
595   ret = False;
596  }
597 
598  if(!(ctrls & SOUND_MASK_LINE))
599  {
600   XtSetSensitive(analyzerW, False);
601   XtSetSensitive(sampleW, False);
602  }
603 
604  UpdateMixerSlider();
605 
606  return(ret);
607 }
608 
609 int SetMute(int mute)
610 {
611  int ret = 0;
612 
613 #ifdef linux
614 
615  struct video_audio arg;
616 
617  if(ioctl(tuner, VIDIOCGAUDIO, &arg) == -1)
618  {
619   fprintf(stderr, "failed to get audio mode (mute): %s\n", strerror(errno));
620   return -1;
621  }
622 
623  ret = arg.flags & VIDEO_AUDIO_MUTE;
624 
625  if (mute)
626   arg.flags |= VIDEO_AUDIO_MUTE;
627  else
628   arg.flags &= ~VIDEO_AUDIO_MUTE;
629 
630  if(ioctl(tuner, VIDIOCSAUDIO, &arg) == -1)
631  {
632   fprintf(stderr, "failed to set audio mode (mute): %s\n", strerror(errno));
633   return -1;
634  }
635 
636 #else
637 
638  int arg;
639 
640 #ifdef JUHA_DRIVER
641 
642  if(ioctl(tuner, TUNER_GAUDIO, &arg) == -1)
643  {
644   fprintf(stderr, "failed to get radio mode (mute): %s\n", strerror(errno));
645   return -1;
646  }
647 
648  ret = arg & AUDIO_MUTE;
649 
650  if(mute)
651   arg = AUDIO_MUTE;
652  else
653   arg = AUDIO_UNMUTE;
654 
655  if(ioctl(tuner, TUNER_SAUDIO, &arg) == -1)
656  {
657   fprintf(stderr, "failed to mute radio: %s\n", strerror(errno));
658   return -1;
659  }
660 
661 #else
662 
663  if(ioctl(tuner, BT848_GAUDIO, &arg) == -1)
664  {
665   fprintf(stderr, "failed to get radio mode (mute): %s\n", strerror(errno));
666   return -1;
667  }
668 
669  ret = arg & AUDIO_MUTE;
670 
671  if(mute)
672   arg = AUDIO_MUTE;
673  else
674   arg = AUDIO_UNMUTE;
675 
676  if(ioctl(tuner, BT848_SAUDIO, &arg) == -1)
677  {
678   fprintf(stderr, "failed to set radio mode (mute): %s\n", strerror(errno));
679   return -1;
680  }
681 #endif
682 
683 #endif
684 
685  UpdateChanges();
686 
687  return ret;
688 }
689 
690 int SetFrequency(int freq)
691 {
692 #ifdef linux
693  int ifreq;
694 #endif
695 #ifdef JUHA_DRIVER
696  freq_t ifreq;
697 #endif
698 
699  if(freq < MINFREQ || freq > MAXFREQ)
700  {
701   fprintf(stderr, "frequency out of range: %d\n", freq);
702   return False;
703  }
704 
705 #ifdef linux
706 
707  ifreq = freq * 16 / 100;
708 
709  if(ioctl(tuner, VIDIOCSFREQ, &ifreq) == -1)
710  {
711   fprintf(stderr, "failed to set new frequency: %s\n", strerror(errno));
712   return False;
713  }
714  frequency = freq;
715 
716 #else
717 
718 #ifdef JUHA_DRIVER
719  ifreq = freq*10;
720  if(ioctl(tuner, FM_SETFREQUENCY, &ifreq) == -1)
721  {
722   fprintf(stderr, "failed to set new frequency: %s\n", strerror(errno));
723   return False;
724  }
725 
726  if(ioctl(tuner, FM_GETFREQUENCY, &ifreq) == -1)
727  {
728   fprintf(stderr, "failed to get new frequency: %s\n", strerror(errno));
729   fprintf(stderr, "trying to handle it...\n");
730  }
731  frequency = ifreq/10;
732 
733 #else
734  if(ioctl(tuner, RADIO_SETFREQ, &freq) == -1)
735  {
736   fprintf(stderr, "failed to set new frequency: %s\n", strerror(errno));
737   return False;
738  }
739 
740  if(ioctl(tuner, RADIO_GETFREQ, &frequency) == -1)
741  {
742   fprintf(stderr, "failed to get new frequency: %s\n", strerror(errno));
743   fprintf(stderr, "trying to handle it...\n");
744  }
745 #endif
746 
747 #endif
748 
749  UpdateSliderValue(FREQUENCY, frequency, 2);
750  UpdateFieldstrength(CalcFieldStrength());
751  UpdateTitle();
752 
753  return True;
754 }
755 
756 int SetStation(const char *name)
757 {
758  int i;
759 
760  if(debug)
761   printf("SetStation to %s\n", name);
762 
763  for(i = 0; i < station.cnt; i++)
764  {
765   if(!strcmp(name, station.name[i]))
766   {
767    SetFrequency(station.freq[i]);
768    return True;
769   }
770  }
771  return False;
772 }
773 
774 int SetVolume(int new_vol)
775 {
776  int vol, left, right;
777 
778  if(mixer == -1)
779   return False;
780 
781  if(new_vol < MINVOL || new_vol > MAXVOL)
782  {
783   fprintf(stderr, "volume out of range (%d).\n", new_vol);
784   return False;
785  }
786 
787  if(balance < 0)
788  {
789   left  = new_vol + (new_vol*balance/100.);
790   right = new_vol;
791  }
792  else
793  {
794   right = new_vol - (new_vol*balance/100.);
795   left  = new_vol;
796  }
797 
798  vol = (left << 8) | right;
799 
800  if(ioctl(mixer, MIXER_WRITE(SOUND_MIXER_LINE), &vol) == -1)
801  {
802   fprintf(stderr, "failed to set new volume: %s\n", strerror(errno));
803   return False;
804  }
805 
806  if(volume != new_vol)
807  {
808   volume = new_vol;
809   UpdateSliderValue(VOLUME, volume, 0);
810  }
811 
812  return True;
813 }
814 
815 int SetBalance(int new_bal)
816 {
817  int ret;
818 
819  if(new_bal < -100 || new_bal > 100)
820   return False;
821 
822  balance = new_bal;
823  ret = SetVolume(volume);
824  UpdateSliderValue(BALANCE, balance, 0);
825  return ret;
826 }
827 
828 int SetTreble(int new_trb)
829 {
830  treble = new_trb;
831  if(SetSound(SOUND_MIXER_TREBLE, new_trb) == False)
832   return False;
833  UpdateSliderValue(TREBLE, new_trb, 0);
834  return True;
835 }
836 
837 int SetBass(int new_bss)
838 {
839  bass = new_bss;
840  if(SetSound(SOUND_MIXER_BASS, new_bss) == False)
841   return False;
842  UpdateSliderValue(BASS, new_bss, 0);
843  return True;
844 }
845 
846 int SetAFC(int onoff)
847 {
848 #ifdef linux
849  /* This functionality is not available */
850  return True;
851 #else
852 
853  int arg;
854 
855  if(ioctl(tuner, RADIO_GETMODE, &arg) == -1)
856  {
857   fprintf(stderr, "failed to get radio mode: %s\n", strerror(errno));
858   return False;
859  }
860 
861  if(onoff)
862  {
863   arg |= RADIO_AFC;
864   afc = True;
865   SetFrequency(frequency);
866  }
867  else
868  {
869   arg &= ~RADIO_AFC;
870   afc = False;
871  }
872 
873  if(ioctl(tuner, RADIO_SETMODE, &arg) == -1)
874  {
875   fprintf(stderr, "failed to set radio mode: %s\n", strerror(errno));
876   return False;
877  }
878 
879  UpdateChanges();
880 
881  return True;
882 #endif
883 }
884 
885 void UpdateStatus(XtPointer clientData, XtIntervalId *id)
886 {
887  if(!seeking)
888  {
889   UpdateFieldstrength(CalcFieldStrength());
890 #ifdef JUHA_DRIVER
891   update_radio_indicator(indicatorW);
892 #endif
893  }
894 
895  XtAppAddTimeOut(app_con, 250, UpdateStatus, NULL);
896 }
897 
898 void SetStereoIndicator(int onoff)
899 {
900  if(onoff)
901   XtVaSetValues(stereoW, XmNlabelString, stereo_label_string,
902                          XmNset, XmSET, NULL);
903  else
904   XtVaSetValues(stereoW, XmNlabelString, mono_label_string,
905                          XmNset, XmUNSET, NULL);
906  lcd_stereo = onoff;
907 }
908 
909 int SetStereo(int onoff)
910 {
911 #ifdef linux
912 
913  struct video_audio arg;
914 
915  if(ioctl(tuner, VIDIOCGAUDIO, &arg) == -1)
916  {
917   fprintf(stderr, "failed to get audio mode (mute): %s\n", strerror(errno));
918   return -1;
919  }
920 
921  arg.audio = 0;
922  arg.flags = 0;
923 
924  if (onoff)
925   arg.mode = VIDEO_SOUND_STEREO;
926  else
927   arg.mode = VIDEO_SOUND_MONO;
928 
929  if(ioctl(tuner, VIDIOCSAUDIO, &arg) == -1)
930  {
931   fprintf(stderr, "failed to set audio mode: %s\n", strerror(errno));
932   return False;
933  }
934 
935 #else
936 
937  int arg;
938 
939  if(ioctl(tuner, RADIO_GETMODE, &arg) == -1)
940  {
941   fprintf(stderr, "failed to get radio mode: %s\n", strerror(errno));
942   return False;
943  }
944 
945  if(onoff)
946   arg &= ~RADIO_MONO;
947  else
948   arg |= RADIO_MONO;
949 
950  if(ioctl(tuner, RADIO_SETMODE, &arg) == -1)
951  {
952   fprintf(stderr, "failed to set radio mode: %s\n", strerror(errno));
953   return False;
954  }
955 
956 #endif
957 
958  UpdateChanges();
959 
960  return True;
961 }
962 
963 int SetSound(int dev, int arg)
964 {
965  if(mixer == -1)
966   return False;
967 
968  if(arg < 0 || arg > 100)
969  {
970   fprintf(stderr, "value out of range (%d).\n", arg);
971   return False;
972  }
973 
974  arg = (arg << 8) | arg;
975 
976  if(dev == SOUND_MIXER_TREBLE)
977  {
978   if(ioctl(mixer, MIXER_WRITE(dev), &arg) == -1)
979   {
980    fprintf(stderr, "failed to set treble: %s\n", strerror(errno));
981   }
982   return True;
983  }
984 
985  if(dev == SOUND_MIXER_BASS)
986  {
987   if(ioctl(mixer, MIXER_WRITE(dev), &arg) == -1)
988   {
989    fprintf(stderr, "failed to set bass: %s\n", strerror(errno));
990   }
991   return True;
992  }
993 
994  fprintf(stderr, "wrong dev to change.\n");
995  return False;
996 }
997 
998 void SeekChannel(int direction)
999 {
1000  int oldmute, oldfrequency, raising;
1001  search_point start, last, actual;
1002 
1003  seeking  = True;
1004  seekstop = False;
1005 
1006  XDefineCursor(dpy, XtWindow(toplevel), workingCursor);
1007  XmUpdateDisplay(toplevel);
1008 
1009  oldmute = SetMute(True);
1010  oldfrequency = frequency;
1011  raising = 0;
1012 
1013  SetStereo(True);
1014 
1015  start.freq     = frequency;
1016  start.strength = CalcFieldStrength();
1017  last.freq = last.strength  = 0;
1018 
1019  while(!seekstop)
1020  {
1021   for(actual.freq = frequency;
1022       actual.freq <= MAXFREQ && actual.freq >= MINFREQ && !seekstop;
1023       actual.freq += direction * FREQSTEP)
1024   {
1025    SetFrequency(actual.freq);
1026    actual.strength = CalcFieldStrength();
1027 
1028    if(actual.strength > start.strength)
1029    {
1030     start = actual;
1031     raising = 1;
1032    }
1033 
1034    if(actual.strength < start.strength)
1035    {
1036     if(raising > 0)
1037     {
1038      oldfrequency = (last.freq + start.freq + 0.5) / 2;
1039      oldfrequency = (oldfrequency / 5) * 5;
1040      seekstop = True;
1041     }
1042     else
1043     {
1044      start = actual;
1045     }
1046     raising = -1;
1047    }
1048    last = actual;
1049    HandleXEvents();
1050   }
1051   frequency = (direction > 0) ? MINFREQ : MAXFREQ;
1052  }
1053 
1054  seeking = False;
1055 
1056  XSync(dpy, True);
1057  XUndefineCursor(dpy, XtWindow(toplevel));
1058 
1059  SetFrequency(oldfrequency);
1060  SetMute(oldmute);
1061 }
1062 
1063 int CalcFieldStrength()
1064 {
1065 #ifdef JUHA_DRIVER
1066  struct tuner_status arg;
1067  static int old_afc;
1068 #else
1069  int arg;
1070 #endif
1071 
1072 #ifdef linux
1073 
1074  struct video_tuner v;
1075 
1076  if(tuner < 0)
1077   return(0);
1078 
1079  v.tuner = 0;
1080  if(ioctl(tuner, VIDIOCGTUNER, &v) == -1)
1081  {
1082   fprintf(stderr, "failed to get RF input level: %s\n", strerror(errno));
1083   lcd_fieldstrength = 0;
1084   return 0;
1085  }
1086 
1087  /* Not sure if this is the right scaling factor */
1088  arg = v.signal / (0xffff / 0x7);
1089 
1090  lcd_fieldstrength = arg;
1091 
1092  return arg;
1093 
1094 #else
1095 
1096  static int old_stereo;
1097 
1098 #ifdef JUHA_DRIVER
1099 
1100  if(tuner < 0)
1101   return(0);
1102 
1103  if(ioctl(tuner, TUNER_GETSTATUS, &arg) != 0)
1104  {
1105   fprintf(stderr, "failed to get tuner status level: %s\n", strerror(errno));
1106   lcd_fieldstrength = 0;
1107   return 0;
1108  }
1109  else
1110  {
1111   //if(debug)
1112   // printf("signal: %d  afc: %d   stereo: %s\n", arg.rssi, arg.afc,
1113   //                                             arg.stereo ? "True" : "False");
1114 
1115   if(old_afc != arg.afc)
1116   {
1117    if(arg.afc)
1118     XtVaSetValues(afcW, XmNset, XmUNSET, NULL);
1119    else
1120     XtVaSetValues(afcW, XmNset, XmSET, NULL);
1121    old_afc = arg.afc;
1122   }
1123 
1124   if(old_stereo != arg.stereo)
1125   {
1126    if(arg.stereo)
1127     SetStereoIndicator(True);
1128    else
1129     SetStereoIndicator(False);
1130    old_stereo = arg.stereo;
1131   }
1132 
1133 //printf("AFC : %d kHz\n", arg.afc);
1134 //printf("RSSI: %d\n", arg.rssi);
1135 
1136   lcd_fieldstrength = arg.rssi * 4 / 255;
1137   return(lcd_fieldstrength);
1138  }
1139 
1140 #else
1141 
1142  int stereo_i;
1143 
1144  if(tuner < 0)
1145   return(0);
1146 
1147  if(ioctl(tuner, TVTUNER_GETSTATUS, &arg) == -1)
1148  {
1149   fprintf(stderr, "failed to get RF input level: %s\n", strerror(errno));
1150   lcd_fieldstrength = 0;
1151   return 0;
1152  }
1153 
1154  stereo_i = arg & 0x10;
1155 
1156  if(old_stereo != stereo_i)
1157  {
1158   if(stereo_i)
1159    SetStereoIndicator(True);
1160   else
1161    SetStereoIndicator(False);
1162   old_stereo = stereo_i;
1163  }
1164 
1165   lcd_fieldstrength = (arg &= 0x07);
1166 
1167  return arg &= 0x07;
1168 #endif
1169 #endif
1170 }
1171 
1172 int UpdateChanges()
1173 {
1174 #ifdef linux
1175  /* Why do we need this?  Works fine without this */
1176 #else
1177  /* believe me, at least FreeBSD up to 3.0 needs it */
1178 
1179 #ifdef JUHA_DRIVER
1180 
1181  freq_t ifreq;
1182 
1183  if(ioctl(tuner, FM_GETFREQUENCY, &ifreq) == -1)
1184  {
1185   fprintf(stderr, "failed to get current frequency: %s\n", strerror(errno));
1186   return False;
1187  }
1188 
1189  if(ifreq > 10*MAXFREQ || ifreq < 10*MINFREQ)
1190   ifreq = 10*MINFREQ;
1191 
1192  if(ioctl(tuner, FM_SETFREQUENCY, &ifreq) == -1)
1193  {
1194   fprintf(stderr, "failed to set current frequency: %s\n", strerror(errno));
1195   return(False);
1196  }
1197 
1198 #else
1199 
1200  int freq;
1201 
1202  if(ioctl(tuner, RADIO_GETFREQ, &freq) == -1)
1203  {
1204   fprintf(stderr, "failed to get current frequency: %s\n", strerror(errno));
1205   return(False);
1206  }
1207 
1208  if(freq > MAXFREQ || freq < MINFREQ)
1209   freq = MINFREQ;
1210 
1211  if(ioctl(tuner, RADIO_SETFREQ, &freq) == -1)
1212  {
1213   fprintf(stderr, "failed to set current frequency: %s\n", strerror(errno));
1214   return(False);
1215  }
1216 #endif
1217 #endif
1218 
1219  return(True);
1220 }
1221 
1222 void UpdateSliderValue(int slider, int value, int decPoints)
1223 {
1224  XmString xstr = xmStringFromInt(value, decPoints);
1225 
1226  if((!*(sliderTable[slider].widget)) && !XtIsRealized(*(sliderTable[slider].widget)))
1227   return;
1228 
1229  XtVaSetValues(*(sliderTable[slider].widgetValue), XmNlabelString, xstr, NULL);
1230 
1231  if(slider != BALANCE && value < 0)
1232  {
1233   XtSetSensitive(*(sliderTable[slider].widget), False);
1234   XtVaSetValues(*(sliderTable[slider].widget), XmNvalue, 0, NULL);
1235 
1236   if(slider == VOLUME)
1237   {
1238    XtSetSensitive(*(sliderTable[VOL].widget), False);
1239    XtVaSetValues(*(sliderTable[VOL].widget), XmNvalue, 0, NULL);
1240   }
1241   if(slider == VOL)
1242   {
1243    XtSetSensitive(*(sliderTable[VOLUME].widget), False);
1244    XtVaSetValues(*(sliderTable[VOLUME].widget), XmNvalue, 0, NULL);
1245   }
1246  }
1247  else
1248  {
1249   XtSetSensitive(*(sliderTable[slider].widget), True);
1250   XtVaSetValues(*(sliderTable[slider].widget), XmNvalue, value, NULL);
1251 
1252   if(slider == VOLUME)
1253   {
1254    XtSetSensitive(*(sliderTable[VOL].widget), True);
1255    XtVaSetValues(*(sliderTable[VOL].widget), XmNvalue, value, NULL);
1256   }
1257   if(slider == VOL)
1258   {
1259    XtSetSensitive(*(sliderTable[VOLUME].widget), True);
1260    XtVaSetValues(*(sliderTable[VOLUME].widget), XmNvalue, value, NULL);
1261   }
1262  }
1263 
1264  XmUpdateDisplay(*(sliderTable[slider].widgetValue));
1265  XmUpdateDisplay(*(sliderTable[slider].widget));
1266 
1267  XmStringFree(xstr);
1268 }
1269 
1270 int UpdateMixerSlider()
1271 {
1272  int right, left, ret = True;
1273  int new_vol, new_bas, new_trbl;
1274 
1275  if(mixer == -1)
1276   return False;
1277 
1278  if(ioctl(mixer, MIXER_READ(SOUND_MIXER_LINE), &new_vol) == -1)
1279  {
1280   fprintf(stderr, "Can't read line volume: %s\n", strerror(errno));
1281   new_vol = -1;
1282   ret = False;
1283  }
1284 
1285  if(ioctl(mixer, MIXER_READ(SOUND_MIXER_BASS), &new_bas) == -1)
1286  {
1287   fprintf(stderr, "Can't read bass value: %s\n", strerror(errno));
1288   new_bas = -1;
1289   ret = False;
1290  }
1291 
1292  if(ioctl(mixer, MIXER_READ(SOUND_MIXER_TREBLE), &new_trbl) == -1)
1293  {
1294   fprintf(stderr, "Can't read treble value: %s\n", strerror(errno));
1295   new_trbl = -1;
1296   ret = False;
1297  }
1298 
1299  left  = (new_vol & 0xff);
1300  right = ((new_vol >> 8) & 0xff);
1301  balance = right - left;
1302  new_vol = left > right ? left : right;
1303 
1304  left = (new_bas & 0xff);
1305  right = ((new_bas >> 8) & 0xff);
1306  if(right == left)
1307   new_bas = left;
1308  else
1309   new_bas = -1;
1310 
1311  left = (new_trbl & 0xff);
1312  right = ((new_trbl >> 8) & 0xff);
1313  if(right == left)
1314   new_trbl = left;
1315  else
1316   new_trbl = -1;
1317 
1318  if(new_vol != volume)
1319  {
1320   volume = new_vol;
1321   UpdateSliderValue(VOLUME, volume, 0);
1322   UpdateSliderValue(BALANCE, balance, 0);
1323  }
1324 
1325  if(new_bas != bass)
1326  {
1327   bass = new_bas;
1328   UpdateSliderValue(BASS, bass, 0);
1329  }
1330 
1331  if(new_trbl != treble)
1332  {
1333   treble = new_trbl;
1334   UpdateSliderValue(TREBLE, treble, 0);
1335  }
1336 
1337  return(ret);
1338 }
1339 
1340 void UpdateFieldstrength(int strength)
1341 {
1342  static int pos, oldvalue;
1343 
1344  strength++;
1345 
1346  if(strength == oldvalue)
1347   return;
1348 
1349  // hardware has 3 bits for field strength
1350  if(strength < 0 || strength > 7)
1351  {
1352   fprintf(stderr, "UpdateFieldstrength() value out of range: %d\n", strength);
1353   return;
1354  }
1355 
1356  oldvalue = strength;
1357 
1358  if(wantLiteClue && !pos)
1359  {
1360   int i;
1361 
1362   pos = -1;
1363   for(i=0; i<XtNumber(liteClueTable); i++)
1364    if(*(liteClueTable[i].widget) == fieldstrengthW)
1365     pos=i;
1366  }
1367 
1368  XtVaSetValues(fieldstrengthW,
1369                XmNsliderSize, strength,
1370                XmNvalue, 0,  /* lesstiff workaround, keep slider at 0 */
1371                NULL);
1372 
1373  XmUpdateDisplay(fieldstrengthW);
1374 
1375  if(wantLiteClue && pos != -1)
1376  {
1377   char buf[64];
1378 
1379   strncpy(buf, liteClueTable[pos].helpText, 57);
1380 
1381   sprintf(buf, "%s ( %d )", buf, strength);
1382   XcgLiteClueAddWidget(liteClue, fieldstrengthW, buf, 0, 0);
1383  }
1384 }
1385 
1386 void UpdateTitle()
1387 {
1388  static char *title;
1389  static char *laststation;
1390  static char buffer[1024];
1391  char *st;
1392  char freq[16];
1393  int i;
1394 
1395  if(!title)
1396  {
1397   char *t;
1398   XtVaGetValues(toplevel, XtNtitle, &t, NULL);
1399   title = strdup(t);
1400  }
1401 
1402  st = NULL;
1403  for(i = 0; i<station.cnt; i++)
1404  {
1405   if((frequency > station.freq[i] - BANDWIDTH) &&
1406      (frequency < station.freq[i] + BANDWIDTH))
1407   {
1408    st = station.name[i];
1409    sprintf(freq, "%d", station.freq[i]);
1410   }
1411  }
1412  if(!st)
1413  {
1414   if(seeking)
1415    st = "*seeking*";
1416   else
1417    st = "*unknown station*";
1418  }
1419 
1420  if(st && strlen(st) && laststation && strlen(laststation))
1421  {
1422   if(!strcmp(st, laststation))
1423    return;
1424  }
1425  laststation = st;
1426 
1427  global_station_name = st;
1428  XChangeProperty(dpy, XtWindow(toplevel), XA_XMRADIO_STATION, XA_STRING, 8,
1429                  PropModeReplace, st, strlen(st));
1430 
1431  if(gui == MINIMAL_GUI)
1432   XtVaSetValues(toplevel, XmNtitle, st, NULL);
1433  else
1434  {
1435   strncpy(buffer, title, 1020);
1436   strcat(buffer, ": ");
1437   strncat(buffer, st, 1023-strlen(buffer));
1438   XtVaSetValues(toplevel, XmNtitle, buffer, NULL);
1439  }
1440  XtVaSetValues(toplevel, XmNiconName, st, NULL);
1441  XmUpdateDisplay(toplevel);
1442 }
1443 
1444 static void quitCB(Widget widget, XtPointer clientData, XtPointer callData)
1445 {
1446  close(mixer);
1447  close(tuner);
1448 
1449  if(sockfd)
1450   lcd_net_stop();
1451 
1452  exit(EXIT_SUCCESS);
1453 }
1454 
1455 static void HandleXEvents()
1456 {
1457  XtInputMask mask;
1458 
1459  while((mask = XtAppPending(app_con)) != 0)
1460   XtAppProcessEvent(app_con, mask);
1461 
1462  return;
1463 }
1464 
1465 static XmString xmStringFromInt(int value, int decimalPoints)
1466 {
1467  static char buffer[17];
1468  float fval;
1469 
1470  fval = value / pow(10, decimalPoints);
1471  if(decimalPoints)
1472   sprintf(buffer, "%.2f", fval);
1473  else
1474   sprintf(buffer, "%.0f", fval);
1475 
1476  return XmStringCreateLtoR(buffer, XmSTRING_DEFAULT_CHARSET);
1477 }
1478 
1479 static int HandleXError(Display *dpy, XErrorEvent *event)
1480 {
1481  char msg[80];
1482 
1483  XGetErrorText(dpy, event->error_code, msg, 80);
1484  fprintf(stderr, "Error code %s\n", msg);
1485 
1486  return 0;
1487 }
1488 
1489 static void aboutCB(Widget widget, XtPointer clientData, XtPointer callData)
1490 {
1491  static Widget dialog;
1492  char buffer[512];
1493 
1494  if(dialog == NULL)
1495  {
1496   Pixel fg, bg;
1497   Display *dpy = XtDisplay(toplevel);
1498   Pixmap bitmap;
1499   XmString string;
1500 
1501   dialog = XmCreateInformationDialog(toplevel, "AboutDialog", NULL, 0);
1502   XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
1503   XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
1504   XtVaGetValues(dialog, XmNbackground, &bg, NULL);
1505   fg = BlackPixel(dpy, DefaultScreen(dpy));
1506 
1507   sprintf(buffer, "%s\n\nMotif based radio application for NetBSD, FreeBSD and Linux\n\nAuthor: Thomas Runge (coto@core.de)\n\nLinux support by Ti Kan (ti@amb.org)\n\nHomepage: http://core.de/~coto/\n\nVersion: %s", APPTITLE, APPVERSION);
1508 
1509   string = XmStringCreateLtoR(buffer, XmSTRING_DEFAULT_CHARSET);
1510 
1511 #ifdef HAS_XPM
1512  if(XpmCreatePixmapFromData(dpy, DefaultRootWindow(dpy), tom_xpm,
1513                             &bitmap, NULL, NULL) < XpmSuccess)
1514 #endif /* HAS_XPM */
1515   bitmap = XCreatePixmapFromBitmapData(dpy, XtWindow(toplevel),
1516                                        (char *) tom_bits,
1517                                        tom_width, tom_height, fg, bg,
1518                                        DefaultDepth (dpy, DefaultScreen(dpy)));
1519 
1520   XtVaSetValues(dialog, XmNautoUnmanage,  True,
1521                         XmNsymbolPixmap,  bitmap,
1522                         XmNmessageString, string,
1523                         NULL);
1524   XmStringFree(string);
1525 #ifdef HAS_XPM
1526   if(skin)
1527    SkinToWidgets(dialog, skin);
1528 #endif
1529  }
1530  XtManageChild (dialog);
1531 }
1532 
1533 static void sliderCB(Widget widget, XtPointer clientData, XtPointer callData)
1534 {
1535  int value = ((XmScrollBarCallbackStruct*) callData)->value;
1536  sliderTable[(int)clientData].func(value);
1537 }
1538 
1539 static void freqButtonCB(Widget widget, XtPointer clientData, XtPointer callData)
1540 {
1541  Dimension w;
1542 
1543  XtVaGetValues(freqDummyW, XmNwidth, &w, NULL);
1544 
1545  if(slider_mode == FREQ_MODE)
1546  {
1547   XtUnmanageChild(freqW);
1548 
1549   if(station_buttons.cnt)
1550   {
1551    XtVaSetValues(stabtnW, XmNwidth, w, NULL);
1552    XtManageChild(stabtnW);
1553   }
1554   else
1555   {
1556    XtManageChild(nostationslabelW);
1557   }
1558   XtVaSetValues(freqButtonW, XmNlabelString, stations_label_string, NULL);
1559   slider_mode = STATION_MODE;
1560   return;
1561  }
1562 
1563  if(slider_mode == VOLUME_MODE)
1564  {
1565   XtUnmanageChild(volW);
1566   XtManageChild(freqW);
1567   XtVaSetValues(freqButtonW, XmNlabelString, frequency_label_string, NULL);
1568   slider_mode = FREQ_MODE;
1569   return;
1570  }
1571 
1572  if(slider_mode == STATION_MODE)
1573  {
1574   if(station_buttons.cnt)
1575   {
1576    XtUnmanageChild(stabtnW);
1577   }
1578   else
1579   {
1580    XtUnmanageChild(nostationslabelW);
1581   }
1582   XtManageChild(volW);
1583   XtVaSetValues(freqButtonW, XmNlabelString, volume_label_string, NULL);
1584   slider_mode = VOLUME_MODE;
1585   return;
1586  }
1587 
1588  return;
1589 }
1590 
1591 static void sampleCB(Widget widget, XtPointer clientData, XtPointer callData)
1592 {
1593  SampleDialogToggle();
1594 }
1595 
1596 static void configCB(Widget widget, XtPointer clientData, XtPointer callData)
1597 {
1598  ConfigDialogToggle();
1599 }
1600 
1601 void lcdDisconnectCB(const char *reason)
1602 {
1603  XtSetSensitive(lcdConnectW, True);
1604  XtSetSensitive(lcdDisconnectW, False);
1605  sockfd = 0;
1606 
1607  if(reason)
1608   XtomShowMessage(toplevel, XmDIALOG_ERROR, "LCD connect", reason);
1609 }
1610 
1611 static void lcdConnectCB(Widget widget, XtPointer clientData, XtPointer
1612                                                                    callData)
1613 {
1614  const char *neterror;
1615  int what = (int)clientData;
1616  if(what == CONNECT)
1617  {
1618   sockfd = lcd_net_init(lcd_host, lcd_port, &neterror);
1619   if(sockfd < 0)
1620   {
1621     fprintf(stderr, "error initializing connection to lcd server: %s\n",
1622 																neterror);
1623     lcdDisconnectCB(neterror);
1624   }
1625   else
1626   {
1627    XtSetSensitive(lcdConnectW, False);
1628    XtSetSensitive(lcdDisconnectW, True);
1629    lcd_net_start();
1630   }
1631  }
1632  else
1633  {
1634   lcd_net_stop();
1635  }
1636 }
1637 
1638 static void versionCB(Widget widget, XtPointer clientData, XtPointer callData)
1639 {
1640  CheckNewVersion();
1641 }
1642 
1643 void version_check(int result, char *description)
1644 {
1645  if(result == -1)
1646  {
1647   char *buf;
1648   char msg[] = "Error asking for new version!";
1649   char err[] = "(no error description available)";
1650 
1651   buf = (char*)malloc(strlen(msg) +
1652 				(description ? strlen(description) : strlen(err)) + 5);
1653   sprintf(buf, "%s\n\n%s", msg, description ? description : err);
1654 
1655   XtomShowMessage(toplevel, XmDIALOG_ERROR, "Versioncheck", buf);
1656   free(buf);
1657  }
1658  else
1659  {
1660   float old_ver, new_ver;
1661   char *t1, *t2;
1662   char *buf;
1663 
1664   t1 = strtok(description, "\\");
1665   t2 = strtok(NULL, "\\");
1666 
1667   new_ver = atof(t1);
1668   old_ver = atof(APPVERSION);
1669 
1670   if(debug)
1671   {
1672    printf("version check returned: %s\n",
1673 						description ? description : "nothing(?)");
1674    printf("this version: %f\nnew version: %f\nmessage: %s\n",
1675             old_ver, new_ver, t2);
1676   }
1677   if(new_ver > old_ver)
1678   {
1679    char msg[] = "Newer version available: ";
1680 
1681    buf = (char*)malloc(strlen(msg) + strlen(t1) + strlen(t2) + 5);
1682    sprintf(buf, "%s%s\n\n%s", msg, t1, t2);
1683    XtomShowMessage(toplevel, XmDIALOG_INFORMATION, "Versioncheck", buf);
1684    free(buf);
1685   }
1686   else
1687   {
1688    char msg1[] = "Sorry, no newer version available.";
1689    char msg2[] = "Actual version: ";
1690 
1691    buf = (char*)malloc(strlen(msg1) + strlen(msg2) +
1692                        strlen(t1) + strlen(t2) + 5);
1693    sprintf(buf, "%s\n\n%s%s\n\n%s", msg1, msg2, t1, t2);
1694    XtomShowMessage(toplevel, XmDIALOG_MESSAGE, "Versioncheck", buf);
1695   }
1696  }
1697  free(description);
1698 }
1699 
1700 void version_enable_button(int onoff)
1701 {
1702  XtSetSensitive(versionW, onoff);
1703 }
1704 
1705 static void analyzerCB(Widget widget, XtPointer clientData, XtPointer callData)
1706 {
1707  AnalyzerToggle();
1708 }
1709 
1710 static int old_recsrc;
1711 static Boolean sampling;
1712 static sampleStruct *ss;
1713 int SampleStart(sampleStruct *_ss)
1714 {
1715  int new_recsrc, tmp;
1716 
1717  ss = _ss;
1718 
1719  if(mixer == -1)
1720   return False;
1721 
1722  /* getting old rec mask */
1723  if(ioctl(mixer, SOUND_MIXER_READ_RECSRC, &old_recsrc) == -1)
1724  {
1725   fprintf(stderr, "couldnt get mixer recsrc: %s\n", strerror(errno));
1726   return False;
1727  }
1728 
1729  /* setting rec mask to line channel */
1730  new_recsrc = SOUND_MASK_LINE;
1731  if(ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &new_recsrc) == -1)
1732  {
1733   fprintf(stderr, "couldnt set mixer recsrc: %s\n", strerror(errno));
1734   return False;
1735  }
1736 
1737  /* opening dsp device */
1738  if((dsp = open(DSP_DEVICE, O_RDONLY, 0)) == -1)
1739  {
1740   fprintf(stderr, "couldnt open %s: %s\n", DSP_DEVICE, strerror(errno));
1741   dsp = -1;
1742   return False;
1743  }
1744 
1745  /* selecting sample format */
1746  tmp = ss->format;
1747  if(ioctl(dsp, SNDCTL_DSP_SETFMT, &ss->format) == -1)
1748  {
1749   fprintf(stderr, "couldnt set dsp format: %s\n", strerror(errno));
1750   SampleEnd();
1751   dsp = -1;
1752   return False;
1753  }
1754 
1755  /* checking sample format */
1756  if(ss->format != tmp && debug)
1757  {
1758   fprintf(stderr, "couldnt set dsp to sample format: %s\n", strerror(errno));
1759   fprintf(stderr, " (wanted %d, got %d; trying to handle it)\n", tmp, ss->format);
1760  }
1761 
1762  /* selecting number of channels */
1763  tmp = ss->stereo;
1764  if(ioctl(dsp, SNDCTL_DSP_STEREO, &ss->stereo) == -1)
1765  {
1766   fprintf(stderr, "couldnt set dsp to stereo: %s\n", strerror(errno));
1767   SampleEnd();
1768   dsp = -1;
1769   return False;
1770  }
1771 
1772  /* checking sample format */
1773  if((ss->stereo != tmp) && debug)
1774  {
1775   fprintf(stderr, "couldnt set dsp to stereo/mono: %s\n", strerror(errno));
1776   fprintf(stderr, " (trying to handle it)\n");
1777  }
1778 
1779  /* selecting sampling rate */
1780  tmp = ss->speed;
1781  if(ioctl(dsp, SNDCTL_DSP_SPEED, &ss->speed) == -1)
1782  {
1783   fprintf(stderr, "couldnt set dsp speed: %s\n", strerror(errno));
1784   SampleEnd();
1785   dsp = -1;
1786   return False;
1787  }
1788 
1789  /* checking sample format */
1790  if((ss->speed != tmp) && debug)
1791  {
1792   fprintf(stderr, "couldnt set dsp to stereo, got mono: %s\n", strerror(errno));
1793   fprintf(stderr, " (trying to handle it)\n");
1794  }
1795 
1796  sampling = True;
1797  XtAppAddWorkProc(app_con, Sample, (XtPointer)NULL);
1798 
1799  return True;
1800 }
1801 
1802 int SampleEnd()
1803 {
1804  sampling = False;
1805 
1806  if(mixer == -1)
1807   return True;
1808 
1809  close(dsp);
1810 
1811  /* resetting rec mask */
1812  if(ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &old_recsrc) == -1)
1813  {
1814   fprintf(stderr, "couldnt reset mixer recsrc: %s\n", strerror(errno));
1815   return False;
1816  }
1817 
1818  return True;
1819 }
1820 
1821 Boolean Sample(XtPointer clientData)
1822 {
1823  if(!sampling || dsp == -1)
1824  return True;
1825 
1826  ss->bufsize = ss->speed * (ss->stereo+1) * ss->bps / REFRESHRATE;
1827  ss->bufsize = ss->bufsize > BUF_SIZE ? BUF_SIZE : ss->bufsize;
1828 
1829  if(read(dsp, audio_buffer, ss->bufsize * ss->bps) == -1)
1830  {
1831   fprintf(stderr, "couldnt get all data: %s\n", strerror(errno));
1832  }
1833 
1834  ss->func();
1835 
1836  return False;
1837 }
1838 
1839 static void guiExpandCB(Widget widget, XtPointer clientData, XtPointer callData)
1840 {
1841  if(gui < NORMAL_GUI)
1842   return;
1843 
1844  if(gui == TINY_GUI)
1845  {
1846   gui = EXPANDED_GUI;
1847   XtVaSetValues(widget, XmNarrowDirection, XmARROW_UP, NULL);
1848   XtManageChild(soundFormW);
1849  }
1850  else
1851  {
1852   gui = TINY_GUI;
1853   XtVaSetValues(widget, XmNarrowDirection, XmARROW_DOWN, NULL);
1854   XtUnmanageChild(soundFormW);
1855  }
1856 }
1857 
1858 static void switchCB(Widget widget, XtPointer clientData, XtPointer callData)
1859 {
1860 
1861  XmPushButtonCallbackStruct *pbcbstr;
1862 
1863  pbcbstr = (XmPushButtonCallbackStruct*) callData;
1864 
1865  if(pbcbstr->event->xbutton.state&ShiftMask)
1866  {
1867   if((int)clientData == DOWN)
1868    XtCallActionProc(toplevel, "StationSeekDown", NULL, NULL, 0);
1869   else
1870    XtCallActionProc(toplevel, "StationSeekUp", NULL, NULL, 0);
1871  }
1872  else
1873  {
1874   if((int)clientData == DOWN)
1875    XtCallActionProc(toplevel, "StationDown", NULL, NULL, 0);
1876   else
1877    XtCallActionProc(toplevel, "StationUp", NULL, NULL, 0);
1878  }
1879 }
1880 
1881 static void seekCB(Widget widget, XtPointer clientData, XtPointer callData)
1882 {
1883  if(seeking)
1884   seekstop = True;
1885  else
1886   SeekChannel((int)clientData);
1887 }
1888 
1889 static void afcCB(Widget widget, XtPointer clientData, XtPointer callData)
1890 {
1891  XmToggleButtonCallbackStruct *tbcs;
1892 
1893  tbcs = (XmToggleButtonCallbackStruct*) callData;
1894 
1895  SetAFC(tbcs->set);
1896 }
1897 
1898 static void stereoCB(Widget widget, XtPointer clientData, XtPointer callData)
1899 {
1900  XmToggleButtonCallbackStruct *tbcs;
1901 
1902  tbcs = (XmToggleButtonCallbackStruct*) callData;
1903 
1904  SetStereo(tbcs->set);
1905 }
1906 
1907 static void muteCB(Widget widget, XtPointer clientData, XtPointer callData)
1908 {
1909  XmToggleButtonCallbackStruct *tbcs;
1910 
1911  tbcs = (XmToggleButtonCallbackStruct*) callData;
1912 
1913  SetMute(tbcs->set);
1914 }
1915 
1916 void propertyCB(Widget widget, XtPointer clientData, XEvent* event,
1917                    Boolean *continueToDispatch)
1918 {
1919  int error = False;
1920  char *cmd;
1921  *continueToDispatch = True;
1922 
1923  if(debug)
1924  {
1925   if(event->xany.type == PropertyNotify &&
1926      event->xproperty.window == XtWindow(toplevel) &&
1927      event->xproperty.atom == XA_XMRADIO_LOCK &&
1928      event->xproperty.state == PropertyNewValue)
1929   {
1930    printf("property changed: Got a lock!\n");
1931   }
1932 
1933   if(event->xany.type == PropertyNotify &&
1934      event->xproperty.window == XtWindow(toplevel) &&
1935      event->xproperty.atom == XA_XMRADIO_LOCK &&
1936      event->xproperty.state == PropertyDelete)
1937   {
1938    printf("property changed: Lock removed!\n");
1939    if(quit_command)
1940     XtCallCallbacks(quitW, XmNactivateCallback, (XtPointer)NULL);
1941   }
1942 
1943   if(event->xany.type == PropertyNotify &&
1944      event->xproperty.window == XtWindow(toplevel) &&
1945      event->xproperty.atom == XA_XMRADIO_COMMAND &&
1946      event->xproperty.state == PropertyDelete)
1947   {
1948    printf("property changed: command removed!\n");
1949   }
1950  }
1951 
1952  if(event->xany.type == PropertyNotify &&
1953     event->xproperty.window == XtWindow(toplevel) &&
1954     event->xproperty.atom == XA_XMRADIO_COMMAND &&
1955     event->xproperty.state == PropertyNewValue)
1956  {
1957   Atom actual_type;
1958   int actual_format;
1959   unsigned long nitems, bytes_after;
1960   unsigned char *prop;
1961   char *tmp;
1962 
1963   if(debug)
1964    printf("property changed: Got a command\n");
1965 
1966   XGetWindowProperty(dpy, XtWindow(widget), XA_XMRADIO_COMMAND,
1967                      0, 8192, True, XA_STRING, &actual_type, &actual_format,
1968                      &nitems, &bytes_after, &prop);
1969 
1970   if(debug)
1971    printf("  command: %s\n", prop ? (char*)prop : "nothing?!?");
1972 
1973   // ack
1974   XChangeProperty(dpy, XtWindow(widget), XA_XMRADIO_RESPONSE, XA_STRING, 8,
1975                   PropModeReplace, "1", 1);
1976 
1977   XSync(dpy, False);
1978 
1979   if(!strcmp(prop, "StationUp") ||
1980      !strcmp(prop, "StationDown") ||
1981      !strcmp(prop, "StationSeekUp") ||
1982      !strcmp(prop, "StationSeekDown"))
1983   {
1984    XtCallActionProc(toplevel, prop, NULL, NULL, 0);
1985    goto READY;
1986   }
1987 
1988   cmd = strtok(prop, "=");
1989   if(!strcmp(cmd, "Frequency"))
1990   {
1991    tmp = strtok(NULL, "=");
1992    if(tmp)
1993    {
1994     if(debug)
1995      printf("set frequency command. changing to %d\n",
1996                           (int)(100. * (atof(tmp) + 0.5)));
1997     error = !SetFrequency((int)(100. * (atof(tmp) + 0.5)));
1998    }
1999    else
2000     error = True;
2001 
2002    goto READY;
2003   }
2004 
2005   if(!strcmp(cmd, "Station"))
2006   {
2007    tmp = strtok(NULL, "=");
2008    if(tmp)
2009    {
2010     if(debug)
2011      printf("set station command. changing to %s\n", tmp);
2012     error = !SetStation(tmp);
2013    }
2014    else
2015     error = True;
2016 
2017    goto READY;
2018   }
2019 
2020   if(!strcmp(cmd, "AFC"))
2021   {
2022    tmp = strtok(NULL, "=");
2023    if(tmp)
2024    {
2025     int res = 0;
2026     res = strcmp(tmp, "on") ? 0 : 1;
2027 
2028     if(debug)
2029      printf("set afc command. changing to %s\n", res ? "on" : "off");
2030     error = !SetAFC(res);
2031 
2032     XtVaSetValues(afcW, XmNset, res, NULL);
2033    }
2034    else
2035     error = True;
2036 
2037    goto READY;
2038   }
2039 
2040   if(!strcmp(cmd, "Stereo"))
2041   {
2042    tmp = strtok(NULL, "=");
2043    if(tmp)
2044    {
2045     int res = 0;
2046     res = strcmp(tmp, "on") ? 0 : 1;
2047 
2048     if(debug)
2049      printf("set stereo command. changing to %s\n", res ? "on" : "off");
2050     error = !SetStereo(res);
2051 
2052     XtVaSetValues(stereoW, XmNset, res, NULL);
2053    }
2054    else
2055     error = True;
2056 
2057    goto READY;
2058   }
2059 
2060   if(!strcmp(cmd, "Mute"))
2061   {
2062    tmp = strtok(NULL, "=");
2063    if(tmp)
2064    {
2065     int res = 0;
2066     res = strcmp(tmp, "on") ? 0 : 1;
2067 
2068     if(debug)
2069      printf("set mute command. changing to %s\n", res ? "on" : "off");
2070     error = (SetMute(res) == -1) ? True : False;
2071 
2072     XtVaSetValues(muteW, XmNset, res, NULL);
2073    }
2074    else
2075     error = True;
2076 
2077    goto READY;
2078   }
2079 
2080   if(!strcmp(cmd, "Balance"))
2081   {
2082    tmp = strtok(NULL, "=");
2083    if(tmp)
2084    {
2085     if(debug)
2086      printf("balance command. changing to %d\n", atoi(tmp));
2087     error = !SetBalance(atoi(tmp));
2088    }
2089    else
2090     error = True;
2091 
2092    goto READY;
2093   }
2094 
2095   if(!strcmp(cmd, "Treble"))
2096   {
2097    tmp = strtok(NULL, "=");
2098    if(tmp)
2099    {
2100     if(debug)
2101      printf("treble command. changing to %d\n", atoi(tmp));
2102     error = !SetTreble(atoi(tmp));
2103    }
2104    else
2105     error = True;
2106 
2107    goto READY;
2108   }
2109 
2110   if(!strcmp(cmd, "Bass"))
2111   {
2112    tmp = strtok(NULL, "=");
2113    if(tmp)
2114    {
2115     if(debug)
2116      printf("bass command. changing to %d\n", atoi(tmp));
2117     error = !SetBass(atoi(tmp));
2118    }
2119    else
2120     error = True;
2121 
2122    goto READY;
2123   }
2124 
2125   if(!strcmp(cmd, "Volume"))
2126   {
2127    tmp = strtok(NULL, "=");
2128    if(tmp)
2129    {
2130     if(debug)
2131      printf("volume command. changing to %d\n", atoi(tmp));
2132     error = !SetVolume(atoi(tmp));
2133    }
2134    else
2135     error = True;
2136 
2137    goto READY;
2138   }
2139 
2140   if(!strcmp(cmd, "ShowAbout"))
2141   {
2142    XtCallCallbacks(aboutW, XmNactivateCallback, (XtPointer)NULL);
2143    goto READY;
2144   }
2145 
2146   if(!strcmp(cmd, "ShowAnalyzer"))
2147   {
2148    XtCallCallbacks(analyzerW, XmNactivateCallback, (XtPointer)NULL);
2149    goto READY;
2150   }
2151 
2152   if(!strcmp(cmd, "Iconify"))
2153   {
2154    XIconifyWindow(dpy, XtWindow(toplevel), DefaultScreen(dpy));
2155    goto READY;
2156   }
2157 
2158   if(!strcmp(cmd, "Withdraw"))
2159   {
2160    XWithdrawWindow(dpy, XtWindow(toplevel), DefaultScreen(dpy));
2161    goto READY;
2162   }
2163 
2164   if(!strcmp(cmd, "Deiconify"))
2165   {
2166    XMapWindow(dpy, XtWindow(toplevel));
2167    goto READY;
2168   }
2169 
2170   if(!strcmp(cmd, "Raise"))
2171   {
2172    XRaiseWindow(dpy, XtWindow(toplevel));
2173    goto READY;
2174   }
2175 
2176   if(!strcmp(cmd, "Lower"))
2177   {
2178    XLowerWindow(dpy, XtWindow(toplevel));
2179    goto READY;
2180   }
2181 
2182   if(!strcmp(cmd, "Quit"))
2183   {
2184    quit_command = True;
2185    goto READY;
2186   }
2187 
2188   // unknown command
2189   XChangeProperty(dpy, XtWindow(widget), XA_XMRADIO_RESPONSE, XA_STRING, 8,
2190                   PropModeReplace, "4", 1);
2191   goto DONE;
2192 
2193 READY:
2194   if(error)
2195    XChangeProperty(dpy, XtWindow(widget), XA_XMRADIO_RESPONSE, XA_STRING, 8,
2196                    PropModeReplace, "5", 1);
2197   else
2198    XChangeProperty(dpy, XtWindow(widget), XA_XMRADIO_RESPONSE, XA_STRING, 8,
2199                    PropModeReplace, "2", 1);
2200 
2201 DONE:
2202   if(prop)
2203    XFree(prop);
2204  }
2205 }
2206 
2207 void enterCursorCB(Widget widget, XtPointer clientData, XEvent* event,
2208                    Boolean *continueToDispatch)
2209 {
2210  *continueToDispatch = True;
2211 
2212  UpdateMixerSlider();
2213 }
2214 
2215 void station_buttonCB(Widget widget, XtPointer clientData, XtPointer callData)
2216 {
2217  SetFrequency((int)clientData);
2218 }
2219 
2220 void usage(char *progname)
2221 {
2222  printf("       xmradio, a Motif based tuner for radio cards.\n");
2223  printf("                   Version %s\n\n", APPVERSION);
2224  printf("Available options:\n\n");
2225  printf(" -station <name>     - tell it a specific start station.\n");
2226  printf(" -frequency <freq>   - tell it a specific start frequency.\n");
2227  printf(" -volume <value>     - tell it a specific start volume.\n");
2228  printf(" -remote <command>   - execute commands in a running radio.\n\n");
2229  printf("      where command is one of:\n");
2230  printf("   StationUp                 - tune up one station\n");
2231  printf("   StationDown               - tune down one station\n");
2232  printf("   StationSeekUp             - seek up one station\n");
2233  printf("   StationSeekDown           - seek down one station\n");
2234  printf("   Frequency=<freq in MHz>   - tune to specified frequency\n");
2235  printf("   Station=<registered name> - tune to specified station\n");
2236  printf("   AFC=<on|off>              - switch automatic frequency control on or off\n");
2237  printf("   Stereo=<on|off>           - switch stereo on or off\n");
2238  printf("   Mute=<on|off>             - mute audio on or off\n");
2239  printf("   Balance=<new_value>       - set balance (value must be between and\n");
2240  printf("                                including -100 and +100\n");
2241  printf("   Treble=<new_value>        - set treble (value must be between and\n");
2242  printf("                                including 0 and +100\n");
2243  printf("   Bass=<new_value>          - set treble (value must be between and\n");
2244  printf("                                including 0 and +100\n");
2245  printf("   Volume=<new_value>        - set treble (value must be between and\n");
2246  printf("                                including 0 and +100\n");
2247  printf("   ShowAbout                 - toggle about box\n");
2248  printf("   ShowAnalyzer              - toggle analyzer\n");
2249  printf("   Iconify                   - iconify window\n");
2250  printf("   Deiconify                 - deiconify window\n");
2251  printf("   Withdraw                  - withdraw window\n");
2252  printf("   Raise                     - raise window\n");
2253  printf("   Lower                     - lower window\n");
2254  printf("   Quit                      - quit application\n\n");
2255  printf(" You can add as many commands as you like in one line, say something like this:\n");
2256  printf("  xmradio -remote Station=\"Delta Radio\" -remote Volume=30 -remote Stereo=off\n\n");
2257  printf(" If you send xmradio a SIGUSR1 or SIGUSR2, it will seek or\n");
2258  printf(" switch to the next/previous station depending on the\n");
2259  printf(" configuration.\n");
2260 }
2261 
2262 void NewInterface()
2263 {
2264  toplevel = XtVaAppCreateShell(APPNAME, APPCLASS,
2265                                sessionShellWidgetClass, dpy,
2266                                XtNallowShellResize, True,
2267                                XtNtitle,            APPTITLE,
2268                                XtNiconPixmap,       icon_pm,
2269                                XtNiconMask,         icon_pm_mask,
2270                                XmNdeleteResponse,   XmDO_NOTHING,
2271                                NULL);
2272 
2273  XtAddEventHandler(toplevel, (EventMask)0, True, _XEditResCheckMessages, NULL);
2274 
2275  XtAddEventHandler(toplevel, EnterWindowMask, False,
2276                    (XtEventHandler) enterCursorCB, (XtPointer)NULL);
2277 
2278  XtAddEventHandler(toplevel, PropertyChangeMask, False,
2279                    (XtEventHandler) propertyCB, (XtPointer)NULL);
2280 
2281  if(wantLiteClue)
2282   liteClue = XtVaCreatePopupShell("lite_clue",
2283                                   xcgLiteClueWidgetClass,
2284                                   toplevel,
2285                                   XgcNwaitPeriod,       1000,
2286                                   XgcNcancelWaitPeriod, 1000,
2287                                   NULL);
2288 
2289  ParseStationList();
2290  MakeMotifInterface();
2291  AddTooltipsToWidgets();
2292 
2293 #ifdef HAS_XPM
2294  SetSkin();
2295 #endif
2296 
2297  GeneratePopupMenu(False);
2298  XtRealizeWidget(toplevel);
2299 }
2300 
2301 void CreateMenuWidget(char *widget_name, Widget parent)
2302 {
2303  moreW = XtVaCreateManagedWidget(widget_name,
2304                                  xmArrowButtonWidgetClass,
2305                                  parent,
2306                                  XmNarrowDirection,   XmARROW_RIGHT,
2307                                  NULL);
2308 
2309  menuW = XmCreatePopupMenu(toplevel, "more_popup", (Arg*)NULL, 0);
2310 
2311  aboutW =
2312    XtVaCreateManagedWidget("about_button",
2313                            xmPushButtonWidgetClass,
2314                            menuW,
2315                            NULL);
2316  XtAddCallback(aboutW, XmNactivateCallback, aboutCB, (XtPointer)NULL);
2317 
2318  analyzerW =
2319    XtVaCreateManagedWidget("analyzer_button",
2320                            xmPushButtonWidgetClass,
2321                            menuW,
2322                            NULL);
2323  XtAddCallback(analyzerW, XmNactivateCallback, analyzerCB, (XtPointer)NULL);
2324 
2325  sampleW =
2326    XtVaCreateManagedWidget("sample_button",
2327                            xmPushButtonWidgetClass,
2328                            menuW,
2329                            NULL);
2330  XtAddCallback(sampleW, XmNactivateCallback, sampleCB, (XtPointer)NULL);
2331 
2332  configW =
2333    XtVaCreateManagedWidget("config_button",
2334                            xmPushButtonWidgetClass,
2335                            menuW,
2336                            NULL);
2337  XtAddCallback(configW, XmNactivateCallback, configCB, (XtPointer)NULL);
2338 
2339  versionW =
2340    XtVaCreateManagedWidget("version_check_button",
2341                            xmPushButtonWidgetClass,
2342                            menuW,
2343                            NULL);
2344  XtAddCallback(versionW, XmNactivateCallback, versionCB, (XtPointer)NULL);
2345 
2346  if(useLcdProc)
2347  {
2348   lcdConnectW =
2349    XtVaCreateManagedWidget("lcd_connect_button",
2350                            xmPushButtonWidgetClass,
2351                            menuW,
2352                            NULL);
2353   XtAddCallback(lcdConnectW, XmNactivateCallback, lcdConnectCB,
2354                                                        (XtPointer)CONNECT);
2355   lcdDisconnectW =
2356    XtVaCreateManagedWidget("lcd_disconnect_button",
2357                            xmPushButtonWidgetClass,
2358                            menuW,
2359                            NULL);
2360   XtAddCallback(lcdDisconnectW, XmNactivateCallback, lcdConnectCB,
2361                                                        (XtPointer)DISCONNECT);
2362  }
2363 }
2364 
2365 void MakeMotifInterface()
2366 {
2367  Atom wmDeleteAtom;
2368  Widget sep1, sep2, sep3, sep4;
2369 
2370  wmDeleteAtom = XmInternAtom(dpy, "WM_DELETE_WINDOW", False);
2371  XmAddWMProtocolCallback(toplevel, wmDeleteAtom, quitCB, (XtPointer)NULL);
2372 
2373  stabtnW = NULL;
2374  nostationslabelW = NULL;
2375  stationPopupW = NULL;
2376 
2377  mainW =
2378    XtVaCreateManagedWidget("radio_main",
2379                            xmFormWidgetClass,
2380                            toplevel,
2381                            NULL);
2382 
2383  if(gui == MINIMAL_GUI)
2384  {
2385   fieldstrengthW =
2386     XtVaCreateManagedWidget("fieldStrength",
2387                             xmScrollBarWidgetClass,
2388                             mainW,
2389                             XmNeditable,         False,
2390                             XmNprocessingDirection, XmMAX_ON_TOP,
2391                             XmNvalue,            0,
2392                             XmNmaximum,          8,
2393                             XmNminimum,          0,
2394                             XmNvalue,            0,
2395                             XmNrightAttachment,  XmATTACH_FORM,
2396                             XmNrightOffset,      3,
2397                             XmNtopAttachment,    XmATTACH_FORM,
2398                             XmNbottomAttachment, XmATTACH_FORM,
2399                             NULL);
2400 
2401 #ifdef JUHA_DRIVER
2402   indicatorW =
2403     create_radio_indicator("indicator", mainW);
2404      XtVaSetValues(indicatorW,
2405                             XmNrightAttachment,  XmATTACH_WIDGET,
2406                             XmNrightWidget,      fieldstrengthW,
2407                             XmNtopAttachment,    XmATTACH_FORM,
2408                             XmNbottomAttachment, XmATTACH_FORM,
2409                             NULL);
2410 #endif
2411 
2412   freqFormW =
2413     XtVaCreateManagedWidget("freq_main",
2414                             xmFormWidgetClass,
2415                             mainW,
2416                             XmNtopAttachment,    XmATTACH_FORM,
2417                             XmNbottomAttachment, XmATTACH_FORM,
2418                             XmNleftAttachment,   XmATTACH_FORM,
2419                             XmNrightAttachment,  XmATTACH_WIDGET,
2420 #ifdef JUHA_DRIVER
2421                             XmNrightWidget,      indicatorW,
2422 #else
2423                             XmNrightWidget,      fieldstrengthW,
2424 #endif
2425                             NULL);
2426 
2427   stationDownW =
2428     XtVaCreateManagedWidget("stationDown",
2429                             xmArrowButtonWidgetClass,
2430                             freqFormW,
2431                             XmNarrowDirection,   XmARROW_LEFT,
2432                             XmNleftAttachment,   XmATTACH_FORM,
2433                             XmNrightAttachment,  XmATTACH_POSITION,
2434                             XmNrightPosition,    50,
2435                             XmNtopAttachment,    XmATTACH_FORM,
2436                             XmNbottomAttachment, XmATTACH_FORM,
2437                             NULL);
2438   XtAddCallback(stationDownW, XmNactivateCallback, switchCB, (XtPointer)DOWN);
2439 
2440   stationUpW =
2441     XtVaCreateManagedWidget("stationUp",
2442                             xmArrowButtonWidgetClass,
2443                             freqFormW,
2444                             XmNarrowDirection,   XmARROW_RIGHT,
2445                             XmNrightAttachment,  XmATTACH_FORM,
2446                             XmNrightOffset,      3,
2447                             XmNleftAttachment,   XmATTACH_POSITION,
2448                             XmNleftPosition,     50,
2449                             XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET,
2450                             XmNtopWidget,        stationDownW,
2451                             XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
2452                             XmNbottomWidget,     stationDownW,
2453                             NULL);
2454   XtAddCallback(stationUpW, XmNactivateCallback, switchCB, (XtPointer)UP);
2455 
2456   return;
2457  }
2458 
2459  buttonFormW =
2460    XtVaCreateManagedWidget("button_main",
2461                            xmFormWidgetClass,
2462                            mainW,
2463                            XmNleftAttachment,   XmATTACH_FORM,
2464                            XmNrightAttachment,  XmATTACH_FORM,
2465                            XmNbottomAttachment, XmATTACH_FORM,
2466                            NULL);
2467 
2468  quitW =
2469    XtVaCreateManagedWidget("quit_button",
2470                            xmPushButtonWidgetClass,
2471                            buttonFormW,
2472                            XmNmarginWidth,      10,
2473                            XmNrightAttachment,  XmATTACH_FORM,
2474                            XmNrightOffset,      2,
2475                            XmNbottomAttachment, XmATTACH_FORM,
2476                            XmNbottomOffset,     2,
2477                            NULL);
2478  XtAddCallback(quitW, XmNactivateCallback, quitCB, (XtPointer)NULL);
2479 
2480  CreateMenuWidget("more", buttonFormW);
2481 
2482  XtVaSetValues(moreW,
2483                XmNleftAttachment,   XmATTACH_FORM,
2484                XmNleftOffset,       2,
2485                XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET,
2486                XmNtopWidget,        quitW,
2487                XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
2488                XmNbottomWidget,     quitW,
2489                NULL);
2490 
2491  buttonSepW =
2492    XtVaCreateManagedWidget("button_separator",
2493                            xmSeparatorWidgetClass,
2494                            buttonFormW,
2495                            XmNleftAttachment,  XmATTACH_FORM,
2496                            XmNleftOffset,      3,
2497                            XmNrightAttachment, XmATTACH_FORM,
2498                            XmNrightOffset,     3,
2499                            XmNtopAttachment,   XmATTACH_FORM,
2500                            NULL);
2501 
2502  toggleFormW =
2503    XtVaCreateManagedWidget("toggle_main",
2504                            xmFormWidgetClass,
2505                            buttonFormW,
2506                            XmNleftAttachment,   XmATTACH_WIDGET,
2507                            XmNleftWidget,       moreW,
2508                            XmNrightAttachment,  XmATTACH_WIDGET,
2509                            XmNrightWidget,      quitW,
2510                            XmNbottomAttachment, XmATTACH_FORM,
2511                            XmNtopAttachment,    XmATTACH_WIDGET,
2512                            XmNtopWidget,        buttonSepW,
2513                            XmNtopOffset,        3,
2514                            NULL);
2515 
2516  afcW =
2517    XtVaCreateManagedWidget("afc",
2518                            xmToggleButtonWidgetClass,
2519                            toggleFormW,
2520                            XmNalignment,        XmALIGNMENT_BEGINNING,
2521                            XmNset,              False,
2522                            XmNleftAttachment,   XmATTACH_FORM,
2523                            XmNleftOffset,       3,
2524                            XmNrightAttachment,  XmATTACH_POSITION,
2525                            XmNrightPosition,    33,
2526                            NULL);
2527  XtAddCallback(afcW, XmNvalueChangedCallback, afcCB, (XtPointer)NULL);
2528 #ifdef JUHA_DRIVER
2529  XtSetSensitive(afcW, False);
2530 #endif
2531 
2532  stereoW =
2533    XtVaCreateManagedWidget("stereo",
2534                            xmToggleButtonWidgetClass,
2535                            toggleFormW,
2536                            XmNlabelString,      stereo_label_string,
2537                            XmNrecomputeSize,    False,
2538                            XmNalignment,        XmALIGNMENT_BEGINNING,
2539                            XmNset,              True,
2540                            XmNleftAttachment,   XmATTACH_POSITION,
2541                            XmNleftPosition,     33,
2542                            XmNrightAttachment,  XmATTACH_POSITION,
2543                            XmNrightPosition,    66,
2544                            NULL);
2545  XtAddCallback(stereoW, XmNvalueChangedCallback, stereoCB, (XtPointer)NULL);
2546 
2547  muteW =
2548    XtVaCreateManagedWidget("mute",
2549                            xmToggleButtonWidgetClass,
2550                            toggleFormW,
2551                            XmNalignment,        XmALIGNMENT_BEGINNING,
2552                            XmNset,              False,
2553                            XmNleftAttachment,   XmATTACH_POSITION,
2554                            XmNleftPosition,     66,
2555                            XmNrightAttachment,  XmATTACH_FORM,
2556                            XmNrightOffset,      3,
2557                            NULL);
2558  XtAddCallback(muteW, XmNvalueChangedCallback, muteCB, (XtPointer)NULL);
2559 
2560  sliderFormW =
2561    XtVaCreateManagedWidget("slider_main",
2562                            xmRowColumnWidgetClass,
2563                            mainW,
2564                            XmNorientation,      XmVERTICAL,
2565                            XmNleftAttachment,   XmATTACH_FORM,
2566                            XmNrightAttachment,  XmATTACH_FORM,
2567                            XmNtopAttachment,    XmATTACH_FORM,
2568                            XmNbottomAttachment, XmATTACH_WIDGET,
2569                            XmNbottomWidget,     buttonFormW,
2570                            NULL);
2571 
2572  freqFormW =
2573    XtVaCreateManagedWidget("freq_main",
2574                            xmFormWidgetClass,
2575                            sliderFormW,
2576                            XmNtopAttachment,    XmATTACH_FORM,
2577                            XmNleftAttachment,   XmATTACH_FORM,
2578                            XmNrightAttachment,  XmATTACH_FORM,
2579                            NULL);
2580 
2581  stationDownW =
2582    XtVaCreateManagedWidget("stationDown",
2583                            xmArrowButtonWidgetClass,
2584                            freqFormW,
2585                            XmNarrowDirection,   XmARROW_LEFT,
2586                            XmNleftAttachment,   XmATTACH_FORM,
2587                            XmNleftOffset,       0,
2588                            XmNtopAttachment,    XmATTACH_FORM,
2589                            XmNtopOffset,        3,
2590                            XmNbottomAttachment, XmATTACH_FORM,
2591                            XmNbottomOffset,     3,
2592                            NULL);
2593  XtAddCallback(stationDownW, XmNactivateCallback, switchCB, (XtPointer)DOWN);
2594 
2595 #ifdef LESSTIF_VERSION
2596 {
2597  Dimension x;
2598  XtVaGetValues(stationDownW, XmNheight, &x, NULL);
2599 #endif
2600 
2601  fieldstrengthW =
2602    XtVaCreateManagedWidget("fieldStrength",
2603                            xmScrollBarWidgetClass,
2604                            freqFormW,
2605                            XmNeditable,         False,
2606                            XmNprocessingDirection, XmMAX_ON_TOP,
2607 #ifdef LESSTIF_VERSION
2608                            XmNheight,           x,
2609 #endif
2610                            XmNmaximum,          8,
2611                            XmNminimum,          0,
2612                            XmNvalue,            0,
2613                            XmNrightAttachment,  XmATTACH_FORM,
2614                            XmNrightOffset,      3,
2615                            XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET,
2616                            XmNtopWidget,        stationDownW,
2617                            XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
2618                            XmNbottomWidget,     stationDownW,
2619                            NULL);
2620 #ifdef LESSTIF_VERSION
2621 }
2622 #endif
2623 
2624 #ifdef JUHA_DRIVER
2625  indicatorW =
2626    create_radio_indicator("indicator", freqFormW);
2627     XtVaSetValues(indicatorW,
2628                            XmNrightAttachment,  XmATTACH_WIDGET,
2629                            XmNrightWidget,      fieldstrengthW,
2630                            XmNrightOffset,      1,
2631                            XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET,
2632                            XmNtopWidget,        stationDownW,
2633                            XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
2634                            XmNbottomWidget,     stationDownW,
2635                            NULL);
2636 #endif
2637 
2638  stationUpW =
2639    XtVaCreateManagedWidget("stationUp",
2640                            xmArrowButtonWidgetClass,
2641                            freqFormW,
2642                            XmNarrowDirection,   XmARROW_RIGHT,
2643                            XmNrightAttachment,  XmATTACH_WIDGET,
2644 #ifdef JUHA_DRIVER
2645                            XmNrightWidget,      indicatorW,
2646 #else
2647                            XmNrightWidget,      fieldstrengthW,
2648 #endif
2649                            XmNrightOffset,      3,
2650                            XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET,
2651                            XmNtopWidget,        stationDownW,
2652                            XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
2653                            XmNbottomWidget,     stationDownW,
2654                            NULL);
2655  XtAddCallback(stationUpW, XmNactivateCallback, switchCB, (XtPointer)UP);
2656 
2657  freqDummyW =
2658   XtVaCreateManagedWidget("freqDummy",
2659                           xmFormWidgetClass,
2660                           freqFormW,
2661                           XmNorientation,      XmHORIZONTAL,
2662                           XmNrightAttachment,  XmATTACH_WIDGET,
2663                           XmNrightWidget,      stationUpW,
2664                           XmNrightOffset,      3,
2665                           XmNleftAttachment,   XmATTACH_WIDGET,
2666                           XmNleftWidget,       stationDownW,
2667                           XmNleftOffset,       3,
2668                           XmNtopAttachment,    XmATTACH_FORM,
2669                           NULL);
2670 
2671  volW =
2672   XtVaCreateManagedWidget("volume",
2673                           xmScaleWidgetClass,
2674                           freqDummyW,
2675                           XmNminimum,         0,
2676                           XmNmaximum,         100,
2677                           XmNorientation,      XmHORIZONTAL,
2678                           XmNrightAttachment,  XmATTACH_FORM,
2679                           XmNleftAttachment,   XmATTACH_FORM,
2680                           XmNtopAttachment,    XmATTACH_FORM,
2681                           XmNbottomAttachment, XmATTACH_FORM,
2682                           NULL);
2683  XtAddCallback(volW, XmNdragCallback, sliderCB,
2684                (XtPointer)VOLUME);
2685  XtAddCallback(volW, XmNvalueChangedCallback, sliderCB,
2686                (XtPointer)VOLUME);
2687 
2688  freqW =
2689   XtVaCreateWidget("frequency",
2690                           xmScaleWidgetClass,
2691                           freqDummyW,
2692                           XmNminimum,          MINFREQ,
2693                           XmNmaximum,          MAXFREQ,
2694                           XmNscaleMultiple,    FREQSTEP,
2695                           XmNorientation,      XmHORIZONTAL,
2696                           XmNrightAttachment,  XmATTACH_FORM,
2697                           XmNleftAttachment,   XmATTACH_FORM,
2698                           XmNtopAttachment,    XmATTACH_FORM,
2699                           XmNbottomAttachment, XmATTACH_FORM,
2700                           NULL);
2701  XtAddCallback(freqW, XmNdragCallback, sliderCB,
2702                (XtPointer)FREQUENCY);
2703  XtAddCallback(freqW, XmNvalueChangedCallback, sliderCB,
2704                (XtPointer)FREQUENCY);
2705 
2706  MakeStationButtonWidgets(False);
2707 
2708  freqButtonW =
2709   XtVaCreateManagedWidget("freqButton",
2710                           xmPushButtonWidgetClass,
2711                           freqFormW,
2712                           XmNlabelString,        volume_label_string,
2713                           XmNborderWidth,        0,
2714                           XmNmarginHeight,       1,
2715                           XmNmarginWidth,        1,
2716                           XmNhighlightThickness, 1,
2717                           XmNshadowThickness,    1,
2718                           XmNleftAttachment,     XmATTACH_OPPOSITE_WIDGET,
2719                           XmNleftWidget,         freqDummyW,
2720                           XmNtopAttachment,      XmATTACH_WIDGET,
2721                           XmNtopWidget,          freqDummyW,
2722                           XmNtopOffset,          1,
2723                           NULL);
2724  XtAddCallback(freqButtonW, XmNactivateCallback, freqButtonCB,
2725                (XtPointer)NULL);
2726 
2727  guiW =
2728   XtVaCreateManagedWidget("gui_expand",
2729                           xmArrowButtonWidgetClass,
2730                           freqFormW,
2731                           XmNarrowDirection,   XmARROW_DOWN,
2732                           XmNleftAttachment,   XmATTACH_WIDGET,
2733                           XmNleftWidget,       freqButtonW,
2734                           XmNleftOffset,       2,
2735                           XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET,
2736                           XmNtopWidget,        freqButtonW,
2737                           XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
2738                           XmNbottomWidget,     freqButtonW,
2739                           NULL);
2740  XtAddCallback(guiW, XmNactivateCallback, guiExpandCB, (XtPointer)NULL);
2741 
2742  seekDownW =
2743    XtVaCreateManagedWidget("seekDown",
2744                            xmArrowButtonWidgetClass,
2745                            freqFormW,
2746                            XmNarrowDirection,   XmARROW_LEFT,
2747                            XmNleftAttachment,   XmATTACH_WIDGET,
2748                            XmNleftWidget,       guiW,
2749                            XmNleftOffset,       2,
2750                            XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET,
2751                            XmNtopWidget,        freqButtonW,
2752                            XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
2753                            XmNbottomWidget,     freqButtonW,
2754                            NULL);
2755  XtAddCallback(seekDownW, XmNactivateCallback, seekCB, (XtPointer)DOWN);
2756 
2757  seekUpW =
2758    XtVaCreateManagedWidget("seekUp",
2759                            xmArrowButtonWidgetClass,
2760                            freqFormW,
2761                            XmNarrowDirection,   XmARROW_RIGHT,
2762                            XmNleftAttachment,   XmATTACH_WIDGET,
2763                            XmNleftWidget,       seekDownW,
2764                            XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET,
2765                            XmNtopWidget,        seekDownW,
2766                            XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
2767                            XmNbottomWidget,     seekDownW,
2768                            NULL);
2769  XtAddCallback(seekUpW, XmNactivateCallback, seekCB, (XtPointer)UP);
2770 
2771  freqValueW =
2772   XtVaCreateManagedWidget("freqValue",
2773                           xmLabelWidgetClass,
2774                           freqFormW,
2775                           XmNalignment,        XmALIGNMENT_END,
2776                           XmNleftAttachment,   XmATTACH_WIDGET,
2777                           XmNleftWidget,       seekUpW,
2778                           XmNrightAttachment,  XmATTACH_WIDGET,
2779                           XmNrightWidget,      stationUpW,
2780                           XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET,
2781                           XmNtopWidget,        freqButtonW,
2782                           XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
2783                           XmNbottomWidget,     freqButtonW,
2784                           NULL);
2785 
2786  soundFormW =
2787    XtVaCreateWidget("sound_main",
2788                            xmFormWidgetClass,
2789                            sliderFormW,
2790                            XmNorientation,      XmVERTICAL,
2791                            XmNtopAttachment,    XmATTACH_WIDGET,
2792                            XmNtopWidget,        volFormW,
2793                            XmNleftAttachment,   XmATTACH_FORM,
2794                            XmNrightAttachment,  XmATTACH_FORM,
2795                            XmNbottomAttachment, XmATTACH_FORM,
2796                            NULL);
2797 
2798  sep1 =
2799    XtVaCreateManagedWidget("separator1",
2800                            xmSeparatorWidgetClass,
2801                            soundFormW,
2802                            XmNleftAttachment,  XmATTACH_FORM,
2803                            XmNleftOffset,      3,
2804                            XmNrightAttachment, XmATTACH_FORM,
2805                            XmNrightOffset,     3,
2806                            XmNtopAttachment,   XmATTACH_FORM,
2807                            NULL);
2808 
2809  volumeLabelW =
2810    XtVaCreateManagedWidget("volumeLabel",
2811                            xmLabelWidgetClass,
2812                            soundFormW,
2813                            XmNleftAttachment,   XmATTACH_FORM,
2814                            XmNleftOffset,       3,
2815                            XmNtopAttachment,    XmATTACH_WIDGET,
2816                            XmNtopWidget,        sep1,
2817                            NULL);
2818 
2819  volumeValueW =
2820    XtVaCreateManagedWidget("volumeValue",
2821                            xmLabelWidgetClass,
2822                            soundFormW,
2823                            XmNrightAttachment,  XmATTACH_FORM,
2824                            XmNrightOffset,      3,
2825                            XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET,
2826                            XmNtopWidget,        volumeLabelW,
2827                            NULL);
2828 
2829  volumeW =
2830    XtVaCreateManagedWidget("volume_mixer",
2831                            xmScaleWidgetClass,
2832                            soundFormW,
2833                            XmNminimum,         0,
2834                            XmNmaximum,         100,
2835                            XmNorientation,     XmHORIZONTAL,
2836                            XmNleftAttachment,  XmATTACH_OPPOSITE_WIDGET,
2837                            XmNleftWidget,      volumeLabelW,
2838                            XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET,
2839                            XmNrightWidget,     volumeValueW,
2840                            XmNtopAttachment,   XmATTACH_WIDGET,
2841                            XmNtopWidget,       volumeLabelW,
2842                            NULL);
2843  XtAddCallback(volumeW, XmNdragCallback, sliderCB,
2844                (XtPointer)VOLUME);
2845  XtAddCallback(volumeW, XmNvalueChangedCallback, sliderCB,
2846                (XtPointer)VOLUME);
2847 
2848  sep2 =
2849    XtVaCreateManagedWidget("separator2",
2850                            xmSeparatorWidgetClass,
2851                            soundFormW,
2852                            XmNleftAttachment,  XmATTACH_FORM,
2853                            XmNleftOffset,      3,
2854                            XmNrightAttachment, XmATTACH_FORM,
2855                            XmNrightOffset,     3,
2856                            XmNtopAttachment,   XmATTACH_WIDGET,
2857                            XmNtopWidget,       volumeW,
2858                            NULL);
2859 
2860  balanceLabelW =
2861    XtVaCreateManagedWidget("balanceLabel",
2862                            xmLabelWidgetClass,
2863                            soundFormW,
2864                            XmNleftAttachment,   XmATTACH_FORM,
2865                            XmNleftOffset,       3,
2866                            XmNtopAttachment,    XmATTACH_WIDGET,
2867                            XmNtopWidget,        sep2,
2868                            NULL);
2869 
2870  balanceValueW =
2871    XtVaCreateManagedWidget("balanceValue",
2872                            xmLabelWidgetClass,
2873                            soundFormW,
2874                            XmNrightAttachment,  XmATTACH_FORM,
2875                            XmNrightOffset,      3,
2876                            XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET,
2877                            XmNtopWidget,        balanceLabelW,
2878                            NULL);
2879 
2880  balanceW =
2881    XtVaCreateManagedWidget("balance",
2882                            xmScaleWidgetClass,
2883                            soundFormW,
2884                            XmNminimum,         -100,
2885                            XmNmaximum,         100,
2886                            XmNvalue,           0,
2887                            XmNorientation,     XmHORIZONTAL,
2888                            XmNleftAttachment,  XmATTACH_OPPOSITE_WIDGET,
2889                            XmNleftWidget,      balanceLabelW,
2890                            XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET,
2891                            XmNrightWidget,     balanceValueW,
2892                            XmNtopAttachment,   XmATTACH_WIDGET,
2893                            XmNtopWidget,       balanceLabelW,
2894                            NULL);
2895  XtAddCallback(balanceW, XmNdragCallback, sliderCB,
2896                (XtPointer)BALANCE);
2897  XtAddCallback(balanceW, XmNvalueChangedCallback, sliderCB,
2898                (XtPointer)BALANCE);
2899 
2900  sep3 =
2901    XtVaCreateManagedWidget("separator3",
2902                            xmSeparatorWidgetClass,
2903                            soundFormW,
2904                            XmNleftAttachment,  XmATTACH_FORM,
2905                            XmNleftOffset,      3,
2906                            XmNrightAttachment, XmATTACH_FORM,
2907                            XmNrightOffset,     3,
2908                            XmNtopAttachment,   XmATTACH_WIDGET,
2909                            XmNtopWidget,       balanceW,
2910                            NULL);
2911 
2912  trebleLabelW =
2913    XtVaCreateManagedWidget("trebleLabel",
2914                            xmLabelWidgetClass,
2915                            soundFormW,
2916                            XmNleftAttachment,   XmATTACH_FORM,
2917                            XmNleftOffset,       3,
2918                            XmNtopAttachment,    XmATTACH_WIDGET,
2919                            XmNtopWidget,        sep3,
2920                            NULL);
2921 
2922  trebleValueW =
2923    XtVaCreateManagedWidget("trebleValue",
2924                            xmLabelWidgetClass,
2925                            soundFormW,
2926                            XmNrightAttachment,  XmATTACH_FORM,
2927                            XmNrightOffset,      3,
2928                            XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET,
2929                            XmNtopWidget,        trebleLabelW,
2930                            NULL);
2931 
2932  trebleW =
2933    XtVaCreateManagedWidget("treble",
2934                            xmScaleWidgetClass,
2935                            soundFormW,
2936                            XmNminimum,          0,
2937                            XmNmaximum,          100,
2938                            XmNvalue,            0,
2939                            XmNorientation,      XmHORIZONTAL,
2940                            XmNleftAttachment,   XmATTACH_OPPOSITE_WIDGET,
2941                            XmNleftWidget,       trebleLabelW,
2942                            XmNrightAttachment,  XmATTACH_OPPOSITE_WIDGET,
2943                            XmNrightWidget,      trebleValueW,
2944                            XmNtopAttachment,    XmATTACH_WIDGET,
2945                            XmNtopWidget,        trebleLabelW,
2946                            NULL);
2947  XtAddCallback(trebleW, XmNdragCallback, sliderCB,
2948                (XtPointer)TREBLE);
2949  XtAddCallback(trebleW, XmNvalueChangedCallback, sliderCB,
2950                (XtPointer)TREBLE);
2951 
2952  sep4 =
2953    XtVaCreateManagedWidget("separator4",
2954                            xmSeparatorWidgetClass,
2955                            soundFormW,
2956                            XmNleftAttachment,  XmATTACH_FORM,
2957                            XmNleftOffset,      3,
2958                            XmNrightAttachment, XmATTACH_FORM,
2959                            XmNrightOffset,     3,
2960                            XmNtopAttachment,   XmATTACH_WIDGET,
2961                            XmNtopWidget,       trebleW,
2962                            NULL);
2963 
2964  bassLabelW =
2965    XtVaCreateManagedWidget("bassLabel",
2966                            xmLabelWidgetClass,
2967                            soundFormW,
2968                            XmNleftAttachment,   XmATTACH_FORM,
2969                            XmNleftOffset,       3,
2970                            XmNtopAttachment,    XmATTACH_WIDGET,
2971                            XmNtopWidget,        sep4,
2972                            NULL);
2973 
2974  bassValueW =
2975    XtVaCreateManagedWidget("bassValue",
2976                            xmLabelWidgetClass,
2977                            soundFormW,
2978                            XmNrightAttachment,  XmATTACH_FORM,
2979                            XmNrightOffset,      3,
2980                            XmNtopAttachment,    XmATTACH_OPPOSITE_WIDGET,
2981                            XmNtopWidget,        bassLabelW,
2982                            NULL);
2983 
2984  bassW =
2985    XtVaCreateManagedWidget("bass",
2986                            xmScaleWidgetClass,
2987                            soundFormW,
2988                            XmNminimum,          0,
2989                            XmNmaximum,          100,
2990                            XmNvalue,            0,
2991                            XmNorientation,      XmHORIZONTAL,
2992                            XmNleftAttachment,   XmATTACH_OPPOSITE_WIDGET,
2993                            XmNleftWidget,       bassLabelW,
2994                            XmNrightAttachment,  XmATTACH_OPPOSITE_WIDGET,
2995                            XmNrightWidget,      bassValueW,
2996                            XmNtopAttachment,    XmATTACH_WIDGET,
2997                            XmNtopWidget,        bassLabelW,
2998                            NULL);
2999  XtAddCallback(bassW, XmNdragCallback, sliderCB,
3000                (XtPointer)BASS);
3001  XtAddCallback(bassW, XmNvalueChangedCallback, sliderCB,
3002                (XtPointer)BASS);
3003 }
3004 
3005 void MakeStationButtonWidgets(int realize)
3006 {
3007  if(stabtnW)
3008  {
3009   XtDestroyWidget(stabtnW);
3010   stabtnW = NULL;
3011  }
3012  if(nostationslabelW)
3013  {
3014   XtDestroyWidget(nostationslabelW);
3015   nostationslabelW = NULL;
3016  }
3017 
3018  if(station_buttons.cnt)
3019  {
3020   int i, j;
3021   float width;
3022   XmString xstr;
3023   Widget w;
3024   char *name, *tmp;
3025 
3026   stabtnW =
3027    XtVaCreateWidget("station_button_form",
3028                     xmFormWidgetClass,
3029                     freqDummyW,
3030                     XmNrightAttachment,  XmATTACH_FORM,
3031                     XmNleftAttachment,   XmATTACH_FORM,
3032                     XmNtopAttachment,    XmATTACH_FORM,
3033                     NULL);
3034 
3035   width = 100./station_buttons.cnt;
3036   for(i=0; i<station_buttons.cnt; i++)
3037   {
3038    name = station.name[station_buttons.pos[i]-1];
3039    j    = station.freq[station_buttons.pos[i]-1];
3040    xstr = XmStringCreateLtoR(name, XmSTRING_DEFAULT_CHARSET);
3041 
3042    w = XtVaCreateManagedWidget("station_button",
3043                                xmPushButtonWidgetClass,
3044                                stabtnW,
3045                                XmNlabelString,     xstr,
3046                                XmNalignment,       XmALIGNMENT_BEGINNING,
3047                                XmNleftAttachment,  XmATTACH_POSITION,
3048                                XmNleftPosition,    (int)(i*width),
3049                                XmNrightAttachment, XmATTACH_POSITION,
3050                                XmNrightPosition,   (int)((i+1)*width),
3051                                NULL);
3052    XtAddCallback(w, XmNactivateCallback, station_buttonCB,
3053                  (XtPointer)j);
3054 
3055    XmStringFree(xstr);
3056 
3057    if(wantLiteClue)
3058    {
3059     tmp = (char*)malloc(strlen(name) + 20);
3060     sprintf(tmp, "%s ( %u.%02u )", name, j / 100, j % 100);
3061 
3062     XcgLiteClueAddWidget(liteClue, w, tmp, 0, 0);
3063     free(tmp);
3064    }
3065   }
3066   if(realize)
3067   {
3068    Dimension w;
3069    XtVaGetValues(freqDummyW, XmNwidth, &w, NULL);
3070    XtVaSetValues(stabtnW, XmNwidth, w, NULL);
3071    XtRealizeWidget(stabtnW);
3072   }
3073 
3074   if(slider_mode == STATION_MODE)
3075    XtManageChild(stabtnW);
3076 
3077 #ifdef HAS_XPM
3078   if(skin)
3079    SkinToWidgets(stabtnW, skin);
3080 #endif
3081  }
3082  else
3083  {
3084   nostationslabelW =
3085    XtVaCreateWidget("no_stations_label",
3086                     xmLabelWidgetClass,
3087                     freqDummyW,
3088                     XmNrightAttachment,  XmATTACH_FORM,
3089                     XmNleftAttachment,   XmATTACH_FORM,
3090                     XmNtopAttachment,    XmATTACH_FORM,
3091                     NULL);
3092   if(realize)
3093    XtRealizeWidget(nostationslabelW);
3094 
3095   if(slider_mode == STATION_MODE)
3096    XtManageChild(nostationslabelW);
3097 
3098 #ifdef HAS_XPM
3099   if(skin)
3100    SkinToWidgets(nostationslabelW, skin);
3101 #endif
3102  }
3103 }
3104 
3105 void AddTooltipsToWidgets()
3106 {
3107  int i, size;
3108 
3109  if(!wantLiteClue)
3110   return;
3111 
3112  size = XtNumber(liteClueTable);
3113 
3114  for(i=0; i<size; i++)
3115  {
3116   if(*(liteClueTable[i].widget))
3117    XcgLiteClueAddWidget(liteClue, *(liteClueTable[i].widget),
3118                                   liteClueTable[i].helpText,
3119                                   0, 0);
3120  }
3121 }
3122 
3123 void ParseStationList()
3124 {
3125  char filepath[MAXPATHLEN];
3126  char buf[512];
3127  char *sta;
3128  char *tmp;
3129  int freq, def, n, nd, line;
3130  int startFreq = 0;
3131  FILE *fp;
3132  float version;
3133 
3134  sprintf(filepath, "%s/%s", getenv("HOME"), RCFILENAME);
3135 
3136  fp = fopen(filepath, "r");
3137 
3138  if(!fp)
3139  {
3140   printf("Couldn't open rc file \"%s\": %s\n", filepath, strerror(errno));
3141   goto failure;
3142  }
3143 
3144  version = 0.7;
3145  while(!feof(fp))
3146  {
3147   if(fgets(buf, 512, fp) != NULL)
3148   {
3149    if(!strncmp(buf, "# Version: ", 11))
3150    {
3151     tmp = &buf[11];
3152     version = atof(tmp);
3153     break;
3154    }
3155   }
3156  }
3157 
3158  if(debug)
3159  {
3160   printf("found config file version: %f\n", version);
3161   if(version < atof(APPVERSION))
3162    printf("%s generated by an older version of xmradio.\n", filepath);
3163  }
3164 
3165  rewind(fp);
3166 
3167  line = n = nd = 0;
3168  while(!feof(fp))
3169  {
3170   if(fgets(buf, 512, fp) != NULL)
3171   {
3172    line++;
3173 
3174    if((*buf == '#') || (*buf == '\n'))
3175     continue;
3176 
3177    tmp = strtok(buf, ":");
3178    if(!tmp)
3179    {
3180     printf("%s: wrong file format (line %d)\n", filepath, line);
3181     goto failure;
3182    }
3183 
3184    tmp = strtok(NULL, ":");
3185    if(!tmp)
3186    {
3187     printf("%s: wrong file format (line %d)\n", filepath, line);
3188     goto failure;
3189    }
3190 
3191    tmp = strtok(NULL, ":");
3192    if(!tmp)
3193    {
3194     printf("%s: wrong file format (line %d)\n", filepath, line);
3195     goto failure;
3196    }
3197 
3198    if(!strcasecmp(tmp, DEFBTNTAG))
3199     nd++;
3200 
3201    if(version > 0.7)
3202    {
3203     tmp = strtok(NULL, ":");
3204     if(!tmp)
3205     {
3206      printf("%s: wrong file format (line %d)\n", filepath, line);
3207      goto failure;
3208     }
3209    }
3210 
3211    n++;
3212   }
3213  }
3214 
3215  rewind(fp);
3216 
3217  station.name = (char**)calloc(n, sizeof(char*));
3218  station.freq =   (int*)calloc(n, sizeof(int));
3219  station.def  =   (int*)calloc(n, sizeof(int));
3220  station.cnt  = 0;
3221  station_buttons.pos = (int*)calloc(n, sizeof(int));
3222  station_buttons.cnt = 0;
3223 
3224  while(!feof(fp))
3225  {
3226   if(fgets(buf, 512, fp) != NULL)
3227   {
3228    if((*buf == '#') || (*buf == '\n'))
3229     continue;
3230 
3231    sta = strtok(buf, ":");
3232    if(!sta)
3233    {
3234     fprintf(stderr, "wrong file format for rc file \"%s\"\n", filepath);
3235     continue;
3236    }
3237 
3238    tmp = strtok(NULL, ":");
3239    if(tmp)
3240     freq = (int) (100. * atof(tmp) + 0.5);
3241    else
3242    {
3243     fprintf(stderr, "wrong file format for rc file \"%s\"\n", filepath);
3244     continue;
3245    }
3246 
3247    tmp = strtok(NULL, ":");
3248    if(tmp && !strcasecmp(tmp, DEFBTNTAG))
3249     def = True;
3250    else
3251     def = False;
3252 
3253    if(version > 0.7)
3254    {
3255     tmp = strtok(NULL, ":");
3256     if(tmp && !strcasecmp(tmp, DEFBTNTAG) && !startStation)
3257      startFreq = freq;
3258    }
3259 
3260    station.name[station.cnt] = strdup(sta);
3261    station.freq[station.cnt] = freq;
3262    station.def[station.cnt]  = def;
3263 
3264    if(startFreq && !startStation)
3265    {
3266     startStation = station.name[station.cnt];
3267     startStationPos = station.cnt;
3268    }
3269 
3270    station.cnt++;
3271 
3272    if(debug)
3273     printf("found \"%s\" on %d (%s) %s\n", sta, freq, def ? "def" : "no def",
3274                                            startFreq ? "start station" : "");
3275 
3276    if(def)
3277     station_buttons.pos[station_buttons.cnt++] = station.cnt;
3278 
3279    startFreq = 0;
3280   }
3281  }
3282  fclose(fp);
3283 
3284  SortStationList();
3285  return;
3286 
3287 failure:
3288  printf("using application defaults.\n");
3289 
3290  ParseStationListAppDef();
3291  ParseStationButtonListAppDef();
3292  SortStationList();
3293  return;
3294 }
3295 
3296 void ParseStationListAppDef()
3297 {
3298  int i, j, n, m;
3299  char *def_string, *string, *tmp, **stations;
3300  char *limiter = ")";
3301 
3302  station.cnt = 0;
3303  def_string = XGetDefault(dpy, APPCLASS, "stationList");
3304  if(!def_string)
3305   return;
3306 
3307  string = strdup(def_string);
3308 
3309  n = 0;
3310  j = strlen(string);
3311  for(i=0; i<j; i++)
3312   if(string[i] == ')')
3313    n++;
3314  m = 0;
3315  for(i=0; i<j; i++)
3316   if(string[i] == '(')
3317    m++;
3318 
3319  if(n != m)
3320  {
3321   fprintf(stderr, "station list has wrong format. feature disabled.\n");
3322   return;
3323  }
3324 
3325  stations     = (char**)calloc(n, sizeof(char*));
3326  station.name = (char**)calloc(n, sizeof(char*));
3327  station.freq =   (int*)calloc(n, sizeof(int));
3328  station.def  =   (int*)calloc(n, sizeof(int));
3329  station.cnt  = n;
3330 
3331  stations[0] = strtok(string, limiter);
3332  for(i=1; i<n; i++)
3333  {
3334   stations[i] = strtok(NULL, limiter);
3335   if(!stations[i])
3336   {
3337    fprintf(stderr, "station list has wrong format. feature disabled.\n");
3338    goto cleanup;
3339   }
3340   while(*stations[i] && isspace(*stations[i]))
3341    stations[i]++;
3342  }
3343 
3344  for(i=0; i<n; i++)
3345  {
3346   tmp = strtok(stations[i], "(");
3347   if(!tmp)
3348   {
3349    fprintf(stderr, "station list has wrong format. feature disabled.\n");
3350    goto cleanup;
3351   }
3352   station.name[i] = strdup(tmp);
3353   tmp = strtok(NULL, "(");
3354   if(!tmp)
3355   {
3356    fprintf(stderr, "station list has wrong format. feature disabled.\n");
3357    goto cleanup;
3358   }
3359   station.freq[i] = (int)(100. * atof(tmp) + 0.5);
3360   station.def[i]  = 0;
3361  }
3362 
3363  free(stations);
3364  free(string);
3365  return;
3366 
3367 cleanup:
3368  for(i=0; i<station.cnt; i++)
3369   if(station.name[i])
3370    free(station.name[i]);
3371  free(station.name);
3372  free(station.freq);
3373  station.cnt = 0;
3374  free(stations);
3375  free(string);
3376  return;
3377 }
3378 
3379 void ParseStationButtonListAppDef()
3380 {
3381  int i, j, n;
3382  char *def_string, *string, *tmp;
3383  char *limiter = " \t";
3384 
3385  station_buttons.cnt = 0;
3386  def_string = XGetDefault(dpy, APPCLASS, "stationbuttons");
3387 
3388  if(!def_string)
3389   return;
3390 
3391  string = strdup(def_string);
3392 
3393  n = 0;
3394  j = strlen(string);
3395  for(i=0; i<j; i++)
3396   if(isspace(string[i]))
3397    n++;
3398  n++;
3399 
3400  station_buttons.pos = (int*)calloc(n, sizeof(int));
3401  station_buttons.cnt = n;
3402 
3403  tmp = strtok(string, limiter);
3404  if(!tmp)
3405  {
3406   fprintf(stderr, "station_button list has wrong format. feature disabled.\n");
3407   station_buttons.cnt = 0;
3408   free(station_buttons.pos);
3409   goto cleanup;
3410  }
3411  j = atoi(tmp);
3412  station_buttons.pos[0] = j;
3413  station.def[j-1] = 1;
3414 
3415  for(i=1; i<n;i++)
3416  {
3417   tmp = strtok(NULL, limiter);
3418   if(!tmp)
3419   {
3420    fprintf(stderr, "station_button list has wrong format. feature disabled.\n");
3421    station_buttons.cnt = 0;
3422    free(station_buttons.pos);
3423    goto cleanup;
3424   }
3425   j = atoi(tmp);
3426   station_buttons.pos[i] = j;
3427   station.def[j-1] = 1;
3428  }
3429 
3430 cleanup:
3431  free(string);
3432 }
3433 
3434 void SortStationList()
3435 {
3436  int i, j, min, ti, n;
3437  char *ts;
3438 
3439  n = station.cnt-1;
3440 
3441  for(i = 0; i < n; i++)
3442  {
3443   min = i;
3444   for(j = i+1; j <= n; j++)
3445    if(station.freq[j] < station.freq[min])
3446     min = j;
3447 
3448   ti = station.freq[min];
3449   station.freq[min] = station.freq[i];
3450   station.freq[i] = ti;
3451   ts = station.name[min];
3452   station.name[min] = station.name[i];
3453   station.name[i] = ts;
3454   ti = station.def[min];
3455   station.def[min] = station.def[i];
3456   station.def[i] = ti;
3457  }
3458 
3459  for(j=0, i = 0; i < station.cnt; i++)
3460  {
3461   if(station.def[i])
3462    station_buttons.pos[j++] = i+1;
3463  }
3464 }
3465 
3466 void SaveRCFile()
3467 {
3468  int i;
3469  char filepath[MAXPATHLEN];
3470  FILE *fp;
3471 
3472  sprintf(filepath, "%s/%s", getenv("HOME"), RCFILENAME);
3473 
3474  fp = fopen(filepath, "w");
3475 
3476  if(!fp)
3477  {
3478   fprintf(stderr, "Couldn't create rc file \"%s\": %s\n",
3479                                  filepath, strerror(errno));
3480   return;
3481  }
3482 
3483  fprintf(fp, "#\n# automatically generated file, do not edit manually!\n#\n\n");
3484  fprintf(fp, "# Version: %s\n\n", APPVERSION);
3485 
3486  if(!startStation)
3487  {
3488   startStation = station.name[0];
3489   startStationPos = 0;
3490  }
3491 
3492  for(i = 0; i < station.cnt; i++)
3493  {
3494   fprintf(fp, "%s:%u.%02u:%s:%s:\n", station.name[i],
3495                               station.freq[i] / 100,
3496                               station.freq[i] % 100,
3497                               station.def[i] ? DEFBTNTAG : NODEFBTNTAG,
3498                               (i == startStationPos ? DEFBTNTAG : NODEFBTNTAG));
3499  }
3500  fprintf(fp, "\n");
3501 
3502  fclose(fp);
3503 }
3504 
3505 static void station_popupCB(Widget widget, XtPointer clientData,
3506                                            XtPointer callData)
3507 {
3508  SetFrequency((int)clientData);
3509 }
3510 
3511 void signal_error()
3512 {
3513  fprintf(stderr, "wrong argument for \"signalReaction\" in app-def file!\n");
3514 }
3515 
3516 void station_up(Widget w, XEvent *event, String *params, Cardinal *paramscnt)
3517 {
3518  int i;
3519 
3520  if(!station.cnt)
3521   return;
3522 
3523  for(i = 0; i < station.cnt; i++)
3524  {
3525   if(station.freq[i] > frequency)
3526   {
3527    SetFrequency(station.freq[i]);
3528    return;
3529   }
3530  }
3531  SetFrequency(station.freq[0]);
3532 }
3533 
3534 void station_down(Widget w, XEvent *event, String *params, Cardinal *paramscnt)
3535 {
3536  int i;
3537 
3538  if(!station.cnt)
3539   return;
3540 
3541  for(i = station.cnt-1; i >= 0; i--)
3542  {
3543   if(station.freq[i] < frequency)
3544   {
3545    SetFrequency(station.freq[i]);
3546    return;
3547   }
3548  }
3549  SetFrequency(station.freq[station.cnt-1]);
3550 }
3551 
3552 void station_seek_up(Widget w, XEvent *event, String *params, Cardinal *paramscnt)
3553 {
3554  SeekChannel(UP);
3555 }
3556 
3557 void station_seek_down(Widget w, XEvent *event, String *params, Cardinal *paramscnt)
3558 {
3559  SeekChannel(DOWN);
3560 }
3561 
3562 void more_popup(Widget w, XEvent *event, String *params, Cardinal *paramscnt)
3563 {
3564  Window w1, w2;
3565  int i1, i2, i3;
3566  XButtonEvent bEvent;
3567 
3568  if(!menuW)
3569   return;
3570 
3571  if(event->type == ButtonPress || event->type == ButtonRelease)
3572  {
3573   XmMenuPosition(menuW, &event->xbutton);
3574  }
3575  else
3576  {
3577   XQueryPointer(dpy, XtWindow(w), &w1, &w2, &bEvent.x_root, &bEvent.y_root,
3578                 &i1, &i2, &i3);
3579   XmMenuPosition(menuW, &bEvent);
3580  }
3581 
3582  XtManageChild(menuW);
3583 }
3584 
3585 void station_popup(Widget w, XEvent *event, String *params, Cardinal *paramscnt)
3586 {
3587  Window w1, w2;
3588  int i1, i2, i3;
3589  XButtonEvent bEvent;
3590 
3591  if(!stationPopupW)
3592   return;
3593 
3594  if(event->type == ButtonPress || event->type == ButtonRelease)
3595  {
3596   XmMenuPosition(stationPopupW, &event->xbutton);
3597  }
3598  else
3599  {
3600   XQueryPointer(dpy, XtWindow(w), &w1, &w2, &bEvent.x_root, &bEvent.y_root,
3601                 &i1, &i2, &i3);
3602   XmMenuPosition(stationPopupW, &bEvent);
3603  }
3604 
3605  XtManageChild(stationPopupW);
3606 }
3607 
3608 void GeneratePopupMenu(int realize)
3609 {
3610  int i;
3611  XmString xstr;
3612  Widget w;
3613 
3614  if(!station.cnt)
3615   return;
3616 
3617  if(stationPopupW)
3618   XtDestroyWidget(stationPopupW);
3619 
3620  stationPopupW = XmCreatePopupMenu(toplevel, "station_popup", (Arg*)NULL, 0);
3621 
3622  for(i = 0; i < station.cnt; i++)
3623  {
3624   xstr = XmStringCreateLtoR(station.name[i], XmSTRING_DEFAULT_CHARSET);
3625   w = XtVaCreateManagedWidget("station_button",
3626                               xmPushButtonWidgetClass,
3627                               stationPopupW,
3628                               XmNlabelString, xstr,
3629                               NULL);
3630   XtAddCallback(w, XmNactivateCallback, station_popupCB,
3631                 (XtPointer)station.freq[i]);
3632   XmStringFree(xstr);
3633  }
3634 #ifdef HAS_XPM
3635  if(skin)
3636   SkinToWidgets(stationPopupW, skin);
3637 #endif
3638 
3639  if(realize)
3640   XtRealizeWidget(stationPopupW);
3641 }
3642 
3643 #ifdef HAS_XPM
3644 void SkinToWidgets(Widget w, Pixmap skin)
3645 {
3646  WidgetList wl;
3647  int i, x;
3648 
3649  if(XtIsComposite(w))
3650  {
3651   XtVaGetValues(w, XtNnumChildren, &x,
3652                    XtNchildren, &wl,
3653                    NULL);
3654   for(i = 0; i < x; i++)
3655    SkinToWidgets(wl[i], skin);
3656  }
3657  XtVaSetValues(w, XmNbackgroundPixmap, skin, NULL);
3658 
3659  if(XmIsPushButton(w))
3660   XtVaSetValues(w, XmNarmPixmap, skin, NULL);
3661 
3662  return;
3663 }
3664 
3665 void SetSkin()
3666 {
3667  int err;
3668 
3669  char *file = XGetDefault(dpy, APPCLASS, "skinPixmap");
3670 
3671  if(!file)
3672   return;
3673 
3674  if((err = XpmReadFileToPixmap(dpy, DefaultRootWindow(dpy), file,
3675                                &skin, NULL, NULL)) < XpmSuccess)
3676  {
3677   skin = 0;
3678   fprintf(stderr, "couldnt create pixmap: %s (%s)\n",
3679                          XpmGetErrorString(err), file);
3680   fprintf(stderr, "no skin available.\n");
3681   return;
3682  }
3683 
3684  /* parse all widgets and set pixmap */
3685  SkinToWidgets(toplevel, skin);
3686  SkinToWidgets(menuW, skin);
3687 }
3688 #endif
3689 
3690 int main(int argc, char** argv)
3691 {
3692  char *tmp;
3693  int startFreq=0;
3694  int startFreqCmdLine=0;
3695  int startVolume=0;
3696  int i;
3697  char **remote_commands = NULL;
3698  int remote_command_count = 0;
3699  int remote_command_size = 0;
3700 
3701 
3702  static XtActionsRec actions[] =
3703  {
3704   { "MorePopup",    more_popup    },
3705   { "StationPopup", station_popup },
3706   { "StationUp",    station_up    },
3707   { "StationDown",  station_down  },
3708   { "StationSeekUp",    station_seek_up    },
3709   { "StationSeekDown",  station_seek_down  },
3710   { "ConfScrollUp",    conf_scroll_up    },
3711   { "ConfScrollDown",  conf_scroll_down  }
3712  };
3713 
3714  quit_command = False;
3715  stereo  = True;
3716  seeking = False;
3717  afc     = False;
3718  initHack     = False;
3719  useLcdProc   = False;
3720  connectToLCDOnStartup = False;
3721  wantLiteClue = True;
3722  startStation = NULL;
3723  startStationPos = 0;
3724  sockfd = 0;
3725 
3726  XtToolkitInitialize();
3727 
3728  app_con = XtCreateApplicationContext();
3729 
3730  XtAppAddActions(app_con, (XtActionsRec *) actions, XtNumber(actions));
3731 
3732  XtAppSetFallbackResources(app_con, fbres);
3733 
3734  dpy = XtOpenDisplay(app_con, NULL, APPNAME, APPCLASS, NULL, 0, &argc, argv);
3735  if(!dpy)
3736  {
3737   fprintf(stderr, "can't open display, exiting...\n");
3738   usage(argv[0]);
3739   exit(EXIT_FAILURE);
3740  }
3741 
3742  XSetErrorHandler(HandleXError);
3743 
3744  tmp = XGetDefault(dpy, APPCLASS, "debug");
3745  if(tmp && !strcasecmp("true", tmp))
3746   debug = True;
3747  else
3748   debug = False;
3749 
3750  initAtoms();
3751 
3752  for(i = 1; i < argc; i++)
3753  {
3754   if(!strcasecmp(argv[i], "-h") ||
3755      !strcasecmp(argv[i], "-help"))
3756   {
3757    usage(argv[0]);
3758    exit(EXIT_SUCCESS);
3759   }
3760 
3761   if(!strcasecmp(argv[i], "-volume"))
3762   {
3763    i++;
3764    if(!argv[i] ||  *argv[i] == '-' || *argv[i] == 0)
3765    {
3766     fprintf(stderr, "%s: invalid `-volume' option \"%s\"\n",
3767             argv[0], argv[i] ? argv[i] : "");
3768     usage(argv[0]);
3769     exit(EXIT_FAILURE);
3770    }
3771    startVolume = atoi(argv[i]);
3772   }
3773 
3774   if(!strcasecmp(argv[i], "-frequency"))
3775   {
3776    i++;
3777    if(!argv[i] ||  *argv[i] == '-' || *argv[i] == 0)
3778    {
3779     fprintf(stderr, "%s: invalid `-frequency' option \"%s\"\n",
3780             argv[0], argv[i] ? argv[i] : "");
3781     usage(argv[0]);
3782     exit(EXIT_FAILURE);
3783    }
3784    startFreq = (int) (100. * atof(argv[i]) + 0.5);
3785    startFreqCmdLine = 1;
3786   }
3787 
3788   if(!strcasecmp(argv[i], "-station"))
3789   {
3790    i++;
3791    if(!argv[i] ||  *argv[i] == '-' || *argv[i] == 0)
3792    {
3793     fprintf(stderr, "%s: invalid `-station' option \"%s\"\n",
3794             argv[0], argv[i] ? argv[i] : "");
3795     usage(argv[0]);
3796     exit(EXIT_FAILURE);
3797    }
3798   }
3799 
3800   if(!strcasecmp(argv[i], "-remote"))
3801   {
3802    if(remote_command_count == remote_command_size)
3803    {
3804     remote_command_size += 20;
3805     remote_commands = remote_commands ?
3806                           realloc(remote_commands,
3807                            remote_command_size * sizeof (char*)) :
3808                           calloc(remote_command_size, sizeof (char*));
3809    }
3810 
3811    i++;
3812    if(!argv[i] ||  *argv[i] == '-' || *argv[i] == 0)
3813    {
3814     fprintf(stderr, "%s: invalid `-remote' option \"%s\"\n",
3815             argv[0], argv[i] ? argv[i] : "");
3816     usage(argv[0]);
3817     exit(EXIT_FAILURE);
3818    }
3819    remote_commands[remote_command_count++] = argv[i];
3820   }
3821  }
3822 
3823  if(remote_command_count)
3824  {
3825   sendCommands(remote_commands);
3826   free(remote_commands);
3827   exit(EXIT_SUCCESS);
3828  }
3829 
3830 #ifdef HAS_XPM
3831  if(XpmCreatePixmapFromData(dpy, DefaultRootWindow(dpy), icon_xpm,
3832                             &icon_pm, NULL, NULL) < XpmSuccess)
3833 #endif /* HAS_XPM */
3834 
3835  icon_pm = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy),
3836                           icon_bits, icon_width, icon_height);
3837 
3838  icon_pm_mask = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy),
3839                           icon_mask_bits, icon_mask_width, icon_mask_height);
3840 
3841  gui = NORMAL_GUI;
3842  tmp = XGetDefault(dpy, APPCLASS, "gui");
3843  if(tmp)
3844  {
3845   if(!strcasecmp("minimal", tmp))
3846    gui = MINIMAL_GUI;
3847  }
3848 
3849  slider_mode = VOLUME_MODE;
3850  tmp = XGetDefault(dpy, APPCLASS, "startMode");
3851  if(tmp)
3852  {
3853   if(!strcasecmp("frequency", tmp))
3854    slider_mode = FREQ_MODE;
3855   if(!strcasecmp("stations", tmp))
3856    slider_mode = STATION_MODE;
3857  }
3858 
3859  tmp = XGetDefault(dpy, APPCLASS, "volume.labelString");
3860  if(tmp)
3861   volume_label_string = XmStringCreateLtoR(tmp, XmSTRING_DEFAULT_CHARSET);
3862  else
3863   volume_label_string = XmStringCreateLtoR("Volume", XmSTRING_DEFAULT_CHARSET);
3864 
3865  tmp = XGetDefault(dpy, APPCLASS, "frequency.labelString");
3866  if(tmp)
3867   frequency_label_string = XmStringCreateLtoR(tmp, XmSTRING_DEFAULT_CHARSET);
3868  else
3869   frequency_label_string = XmStringCreateLtoR("Frequency", XmSTRING_DEFAULT_CHARSET);
3870 
3871  tmp = XGetDefault(dpy, APPCLASS, "stations.labelString");
3872  if(tmp)
3873   stations_label_string = XmStringCreateLtoR(tmp, XmSTRING_DEFAULT_CHARSET);
3874  else
3875   stations_label_string = XmStringCreateLtoR("Stations", XmSTRING_DEFAULT_CHARSET);
3876 
3877  tmp = XGetDefault(dpy, APPCLASS, "def.labelString");
3878  if(tmp)
3879   def_label_string = XmStringCreateLtoR(tmp, XmSTRING_DEFAULT_CHARSET);
3880  else
3881   def_label_string = XmStringCreateLtoR("define", XmSTRING_DEFAULT_CHARSET);
3882 
3883  tmp = XGetDefault(dpy, APPCLASS, "stereo.labelString");
3884  if(tmp)
3885   stereo_label_string = XmStringCreateLtoR(tmp, XmSTRING_DEFAULT_CHARSET);
3886  else
3887   stereo_label_string = XmStringCreateLtoR("stereo", XmSTRING_DEFAULT_CHARSET);
3888 
3889  tmp = XGetDefault(dpy, APPCLASS, "mono.labelString");
3890  if(tmp)
3891   mono_label_string = XmStringCreateLtoR(tmp, XmSTRING_DEFAULT_CHARSET);
3892  else
3893   mono_label_string = XmStringCreateLtoR("mono", XmSTRING_DEFAULT_CHARSET);
3894 
3895  tmp = XGetDefault(dpy, APPCLASS, "useLiteClue");
3896  if(tmp && !strcasecmp("false", tmp))
3897   wantLiteClue = False;
3898 
3899  tmp = XGetDefault(dpy, APPCLASS, "useInitHack");
3900  if(tmp && !strcasecmp("true", tmp))
3901   initHack = True;
3902 
3903  tmp = XGetDefault(dpy, APPCLASS, "useLcdProc");
3904  if(tmp && !strcasecmp("true", tmp))
3905   useLcdProc = True;
3906 
3907  tmp = XGetDefault(dpy, APPCLASS, "connectToLCDOnStartup");
3908  if(tmp && !strcasecmp("true", tmp))
3909   connectToLCDOnStartup = True;
3910 
3911  NewInterface();
3912 
3913  if(!startFreq)
3914  {
3915   tmp = XGetDefault(dpy, APPCLASS, "startFrequency");
3916   if(tmp)
3917    startFreq = (int) (100. * atof(tmp) + 0.5);
3918   else
3919    startFreq = MINFREQ;
3920  }
3921 
3922  TUNER_DEVICE = XGetDefault(dpy, APPCLASS, "tunerDevice");
3923  if(!TUNER_DEVICE)
3924   fprintf(stderr, "no tuner device specified!?!\n");
3925 
3926  MIXER_DEVICE = XGetDefault(dpy, APPCLASS, "mixerDevice");
3927  if(!MIXER_DEVICE)
3928   fprintf(stderr, "no mixer device specified!?!\n");
3929 
3930  DSP_DEVICE = XGetDefault(dpy, APPCLASS, "dspDevice");
3931  if(!DSP_DEVICE)
3932   fprintf(stderr, "no dsp device specified!?!\n");
3933 
3934  tmp = XGetDefault(dpy, APPCLASS, "channelSet");
3935  if(tmp)
3936   chnlset = atoi(tmp);
3937  if(chnlset<CHNLSET_MIN || chnlset>CHNLSET_MAX)
3938  {
3939   fprintf(stderr, "wrong channels set. using default for weurope.\n");
3940   chnlset = CHNLSET_WEUROPE;
3941  }
3942 
3943  if(!startFreqCmdLine && !startStation)
3944  {
3945   tmp = XGetDefault(dpy, APPCLASS, "startStation");
3946   if(tmp)
3947    startStation = tmp;
3948  }
3949 
3950  if(startStation)
3951  {
3952   int found = False;
3953   for(i = 0; i < station.cnt; i++)
3954    if(!strcmp(startStation, station.name[i]))
3955    {
3956     startStationPos = i;
3957     startFreq = station.freq[i];
3958     found = True;
3959    }
3960   if(!found)
3961   {
3962    fprintf(stderr, "Couldn't find starting station, using default one.\n");
3963    if(station.cnt)
3964     startFreq = station.freq[0];
3965    else
3966     startFreq = MINFREQ;
3967   }
3968  }
3969 
3970  if(debug)
3971  {
3972   printf("using startfreq : %d\n", startFreq);
3973   printf("using tuner     : %s\n", TUNER_DEVICE);
3974   printf("using mixer     : %s\n", MIXER_DEVICE);
3975   printf("using dsp       : %s\n", DSP_DEVICE);
3976   printf("using channelset: %d\n", chnlset);
3977  }
3978 
3979  if(InitTuner(startFreq) == False)
3980   fprintf(stderr, "FAILED TO INIT TUNER!\n");
3981 
3982  InitMixer();
3983 
3984  if(startVolume)
3985   SetVolume(startVolume);
3986 
3987  gui = TINY_GUI;
3988 
3989  gc = XCreateGC(dpy, DefaultRootWindow(dpy), 0, NULL);
3990 
3991  XtVaGetValues(fieldstrengthW, XmNforeground, &fg, NULL);
3992  XSetForeground(dpy, gc, fg);
3993 
3994  workingCursor = XCreateFontCursor(dpy, XC_watch);
3995 
3996  if(mixer == -1)
3997  {
3998   XtSetSensitive(volumeW,  False);
3999   XtSetSensitive(volW,     False);
4000   XtSetSensitive(balanceW, False);
4001   XtSetSensitive(trebleW,  False);
4002   XtSetSensitive(bassW,    False);
4003   XtSetSensitive(analyzerW,False);
4004   XtSetSensitive(sampleW,  False);
4005  }
4006 
4007  if(stereo == False)
4008   XtSetSensitive(balanceW, False);
4009 
4010  if(slider_mode == FREQ_MODE)
4011  {
4012   slider_mode = VOLUME_MODE;
4013   XtCallCallbacks(freqButtonW, XmNactivateCallback, (XtPointer)NULL);
4014  }
4015 
4016  if(slider_mode == STATION_MODE)
4017  {
4018   slider_mode = VOLUME_MODE;
4019   XtCallCallbacks(freqButtonW, XmNactivateCallback, (XtPointer)NULL);
4020   XtCallCallbacks(freqButtonW, XmNactivateCallback, (XtPointer)NULL);
4021  }
4022 
4023  tmp = XGetDefault(dpy, APPCLASS, "signalReaction");
4024  if(tmp)
4025  {
4026   if(!strcmp(tmp, "seek"))
4027   {
4028    signal(SIGUSR1, (void*) station_seek_up);
4029    signal(SIGUSR2, (void*) station_seek_down);
4030   }
4031   else if(!strcmp(tmp, "switch"))
4032   {
4033    signal(SIGUSR1, (void*) station_up);
4034    signal(SIGUSR2, (void*) station_down);
4035   }
4036   else
4037   {
4038    fprintf(stderr, "wrong argument for \"signalReaction\" in app-def file!\n");
4039    signal(SIGUSR1, signal_error);
4040    signal(SIGUSR2, signal_error);
4041   }
4042  }
4043 
4044  XChangeProperty(dpy, XtWindow(toplevel), XA_XMRADIO_VERSION, XA_STRING, 8,
4045                  PropModeReplace, APPVERSION, strlen(APPVERSION));
4046 
4047  if(useLcdProc && connectToLCDOnStartup)
4048   XtCallCallbacks(lcdConnectW, XmNactivateCallback, (XtPointer)CONNECT);
4049  else
4050   lcdDisconnectCB(NULL);
4051 
4052  XtAppAddTimeOut(app_con, 250, UpdateStatus, NULL);
4053 
4054  XtAppMainLoop(app_con);
4055 
4056  return(True);
4057 }
4058 
4059 #ifdef JUHA_DRIVER
4060 static void update_radio_indicator(Widget w)
4061 {
4062  Window   win = XtWindow(w);
4063  static GC gc;
4064  Colormap cmap;
4065  static XColor red, green, blue, black, white;
4066  Dimension width, height;
4067  Dimension shadow, marginw, marginh;
4068  int x, y;
4069  int v;
4070  struct tuner_status st;
4071  static struct tuner_status ost;
4072 
4073  if(!XtIsRealized(w))
4074   return;
4075 
4076  v = ioctl(tuner, TUNER_GETSTATUS, &st);
4077  if(v)
4078   st.lock = 0;
4079 
4080  if(ost.lock   == st.lock
4081      && ost.stereo == st.stereo
4082      && ost.afc    == st.afc
4083      && ost.rssi   == st.rssi)
4084   return;
4085 
4086  ost = st;
4087 
4088  XtVaGetValues(w, XtNwidth, &width,
4089                   XtNheight, &height,
4090                   XtNcolormap, &cmap,
4091                   XmNshadowThickness, &shadow,
4092                   XmNmarginWidth, &marginw,
4093                   XmNmarginHeight, &marginh,
4094                   NULL);
4095 
4096  x = shadow + marginw + 1;
4097  y = shadow + marginh + 1;
4098  width  -= 2 * x;
4099  height -= 2 * y;
4100 
4101  if(!gc)
4102  {
4103   red.red = ~0;
4104   green.green = ~0;
4105   blue.blue = ~0;
4106   XAllocColor(dpy, cmap, &red);
4107   XAllocColor(dpy, cmap, &green);
4108   XAllocColor(dpy, cmap, &blue);
4109   gc = XCreateGC(dpy, XtWindow(w), 0, NULL);
4110   black.pixel = BlackPixelOfScreen(XtScreen(w));
4111   white.pixel = WhitePixelOfScreen(XtScreen(w));
4112  }
4113 
4114  v = st.rssi * height / 255;
4115  if(!st.lock || v != height)
4116  {
4117   XSetForeground(dpy, gc, st.lock ? black.pixel : red.pixel);
4118   XFillRectangle(dpy, win, gc, x, y, width, height);
4119  }
4120  if(st.lock)
4121  {
4122   if(v)
4123   {
4124    XSetForeground(dpy, gc, st.stereo ? green.pixel : blue.pixel);
4125    XFillRectangle(dpy, win, gc, x, y + height - v, width, v);
4126   }
4127   v = st.afc * (width - 2) / 255;
4128   XSetForeground(dpy, gc, v ? red.pixel : white.pixel);
4129   XFillRectangle(dpy, win, gc, x + width / 2 - 1 + v, y, 2, height);
4130  }
4131 }
4132 
4133 static Widget create_radio_indicator(char *name, Widget parent)
4134 {
4135  Widget w;
4136 
4137  w = XtVaCreateWidget(name, xmDrawnButtonWidgetClass, parent,
4138                             XmNshadowType, XmSHADOW_IN,
4139                             XmNwidth, 24,
4140                             NULL);
4141 
4142  XtManageChild(w);
4143  update_radio_indicator(w);
4144 
4145  return (w);
4146 }
4147 #endif /* JUHA_DRIVER */
4148 
4149