/*- * Copyright (c) 1999 Thomas Runge (coto@core.de) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * * Thanks to Roger Hardiman (roger@cs.strath.ac.uk) for helping * how to determine the signal strength. And of * course for his work on the driver * Randall Hopper (http://people.freebsd.org/~rhh/) for * his great fxtv * Matthias Scheler (tron@netbsd.de) for the first * bugreport, NetBSD support and his very helpful hints * Flemming Jacobsen (fj@dkuug.dk) for bugreports, hints * and suggestions. * Bernd Ernesti for additional * NetBSD support * John Preisler for requesting the * remote control feature (he was the second guy asking * for it) and the signal handlers. * Jamie Zawinski . His netscape remote * control helped a lot implementing something similar * to xmradio * Ti Kan for linux support * Karl Jeacle for the first * picture postcard :-) * Juha Nurmela for his new * driver and fixing some lesstif related problems */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __NetBSD__ #include #include #elif defined(linux) #include #include #elif defined(__FreeBSD__) #include #include #else #include #endif #ifdef JUHA_DRIVER #include #endif #include #include #include #include #include #include #include #ifdef HAS_XPM #include #include "icon.xpm" #endif #include #include "LiteClue.h" #include "radio.h" #include "analyzer.h" #include "sample.h" #include "remote.h" #include "config.h" #include "misc.h" #include "version_check.h" #include "lcd_net.h" #ifdef HAS_XPM #include "tom.xpm" #endif #include "tom.xbm" #include "icon.xbm" #include "icon_mask.xbm" #if (XmVERSION < 2) enum { XmUNSET, XmSET, XmINDETERMINATE }; #endif #define RCFILENAME ".xmradiorc" #define DEFBTNTAG "X" #define NODEFBTNTAG "-" #define MINVOL 0 #define MAXVOL 100 #define VOLSTEP 1 #define CONNECT 1 #define DISCONNECT 2 #ifndef BANDWIDTH #define BANDWIDTH 15 #endif /* devices */ static int tuner; int mixer; static int dsp; char *TUNER_DEVICE; char *MIXER_DEVICE; char *DSP_DEVICE; /* some values */ static int volume; static int balance; static int treble; static int bass; static int stereo; static int afc; static int chnlset; /* lcdproc support */ int sockfd; Widget lcdConnectW, lcdDisconnectW; const char *global_station_name; int lcd_stereo; int lcd_fieldstrength; /* some flags */ char *startStation; int startStationPos; static int initHack; static int useLcdProc; static int connectToLCDOnStartup; static int wantLiteClue; static int seeking; static int seekstop; static int quit_command; static int gui; #define MINIMAL_GUI 1 #define NORMAL_GUI 2 #define EXPANDED_GUI 3 #define TINY_GUI 4 static int slider_mode; #define VOLUME_MODE 1 #define FREQ_MODE 2 #define STATION_MODE 3 XmString volume_label_string, frequency_label_string, stations_label_string; XmString def_label_string, stereo_label_string, mono_label_string; XtAppContext app_con; Widget mainW, sliderFormW, freqFormW, soundFormW, buttonFormW, toggleFormW; Widget stabtnW, nostationslabelW, afcW, muteW, stereoW, stationPopupW; Widget stationUpW, stationDownW, seekDownW, seekUpW, buttonSepW, guiW, volFormW; Widget menuW, moreW, versionW; Widget quitW, aboutW, analyzerW, sampleW, configW, liteClue, fieldstrengthW; Widget freqW, freqDummyW, freqValueW, freqButtonW, volW; Widget volumeW, volumeValueW, volumeLabelW; Widget balanceW, balanceValueW, balanceLabelW; Widget trebleW, trebleValueW, trebleLabelW; Widget bassW, bassValueW, bassLabelW; extern Widget format_optionW, capture_optionW, stereo_optionW, rate_optionW; extern Widget recordB, stopB, playbackB, dismissB, searchB, filename_textW; extern Widget overwriteW; #ifdef JUHA_DRIVER Widget indicatorW; static Widget create_radio_indicator(char *name, Widget parent); static void update_radio_indicator(Widget w); #endif int SetSound(int dev, int arg); int SetStation(const char* name); int SetVolume(int); int SetBalance(int); int SetTreble(int); int SetBass(int); int SetAFC(int); int SetStereo(int); void SetStereoIndicator(int); void UpdateStatus(XtPointer clientData, XtIntervalId *id); enum slider { FREQUENCY=0, VOL, VOLUME, BALANCE, TREBLE, BASS }; static const struct { Widget *widget; Widget *widgetValue; int (*func)(int); } sliderTable[] = { { &freqW, &freqValueW, SetFrequency }, { &volW, &volumeValueW, SetVolume }, { &volumeW, &volumeValueW, SetVolume }, { &balanceW, &balanceValueW, SetBalance }, { &trebleW, &trebleValueW, SetTreble }, { &bassW, &bassValueW, SetBass } }; static const struct { Widget *widget; const char *helpText; } liteClueTable[] = { { &freqW, "tune frequency" }, { &volW, "adjust volume" }, { &freqButtonW, "toggle between volume slider, frequency slider and buttons" }, { &volumeW, "adjust volume" }, { &balanceW, "adjust balance" }, { &trebleW, "adjust treble" }, { &bassW, "adjust bass" }, #ifdef linux { &afcW, "automatic frequency control (doesn't work on Linux)" }, #else { &afcW, "automatic frequency control" }, #endif { &muteW, "stop sound" }, #ifdef linux { &stereoW, "stereo/mono (doesn't work on Linux)" }, #else { &stereoW, "stereo/mono" }, #endif { &stationDownW,"previous registered station" }, { &stationUpW, "next registered station" }, #ifdef linux { &seekDownW, "seek station down (doesn't work on Linux)" }, { &seekUpW, "seek station up (doesn't work on Linux)" }, { &fieldstrengthW, "field strength (doesn't work on Linux)" }, #else { &seekDownW, "seek station down" }, { &seekUpW, "seek station up" }, { &fieldstrengthW, "field strength" }, #endif { &buttonSepW, "Wow! Tooltips on separator widgets!" }, { &guiW, "show/hide mixer" }, { &quitW, "quit program" }, { &aboutW, "about" }, { &analyzerW, "start/stop analyzer" }, { &moreW, "more..." }, { &sampleW, "record to file" }, { &configW, "shows configuration dialog" }, { &versionW, "check for new version" }, { &searchB, "search directories" }, { &dismissB, "dismiss dialog" }, { &playbackB, "start playing" }, { &stopB, "stop recording/playing" }, { &recordB, "start recording" }, { &overwriteW, "check, if file should be overwritten without asking" }, { &rate_optionW, "sample speed" }, { &stereo_optionW, "mono/stereo" }, { &capture_optionW, "choose sample format" }, { &format_optionW, "choose file format" }, { &filename_textW, "record/play this file" }, { &lcdConnectW, "connect to LCD" }, { &lcdDisconnectW, "disconnect from LCD" } }; typedef struct { int freq; int strength; } search_point; /* station list */ Stations station; /* which station on which button */ StationButtons station_buttons; static String fbres[] = { #ifdef linux "*tunerDevice: /dev/radio", #else "*tunerDevice: /dev/tuner", #endif "*mixerDevice: /dev/mixer", #ifdef __NetBSD__ "*dspDevice: /dev/sound", #else "*dspDevice: /dev/dsp", #endif "*startFrequency: 87.50", "*gui: mixer", "*frequency.labelString: Frequency", "*volume.labelString: Volume", "*stations.labelString: Stations", "*startMode: volume", "*.background: #bfbfbf", "*.font: -*-lucida-medium-r-*-*-11-*-*-*-*-*-*-*", "*.fontList: -*-lucida-medium-r-*-*-11-*-*-*-*-*-*-*", "*debug: false", "*XcgLiteClue.background: yellow", "*XcgLiteClue.foreground: black", "*fieldStrength.showArrows: False", "*analyzer.title: Realtime Analyzer", "*analyzer.width: 250", "*analyzer.height: 60", "*config_dialog.width: 450", "*config_dialog.height: 350", "*volumeLabel.labelString: Volume", "*freqButton.labelString: Vol/Freq/Stn", "*balanceLabel.labelString: Balance", "*trebleLabel.labelString: Treble", "*bassLabel.labelString: Bass", "*no_stations_label.labelString: no station predefined", "*volumeValue.labelString: 0", "*balanceValue.labelString: 0", "*trebleValue.labelString: 0", "*bassValue.labelString: 0", "*quit_button.labelString: Quit", "*about_button.labelString: About", "*sample_button.labelString: Sample", "*config_button.labelString: Config...", "*analyzer_button.labelString: Analyzer", "*version_check_button.labelString: Check Version", "*lcd_connect_button.labelString: Connect to LCD", "*lcd_disconnect_button.labelString: Disconnect from LCD", "*AboutDialog.dialogTitle: About", "*afc.labelString: AFC", "*stereo.labelString: stereo", "*mono.labelString: mono", "*mute.labelString: mute", "*baseTranslations: #override\\n\\ : StationPopup() \n\ Shift,: StationSeekUp() \n\ Shift,:StationSeekDown() \n\ ,: StationUp() \n\ ,: StationDown() \n\ plus: StationUp() \n\ minus: StationDown() \n\ KP_Add: StationUp() \n\ KP_Subtract: StationDown()", "*more.baseTranslations: #override \\n\\ : MorePopup() \n\ : StationPopup() \n\ Shift,: StationSeekUp()\n\ Shift,: StationSeekDown() \n\ ,: StationUp() \n\ ,: StationDown() \n\ plus: StationUp() \n\ minus: StationDown() \n\ KP_Add: StationUp() \n\ KP_Subtract: StationDown()", "*config_dialog.*.baseTranslations: #override \n\ ,: ConfScrollUp() \n\ ,: ConfScrollDown()", "*sample_dialog.title: Sampling", "*config_dialog.title: Configuration", "*filename_label.labelString:Filename:", "*format_label.labelString: FileFormat:", "*capture_label.labelString: Capture:", "*raw_format.labelString: RAW", "*au_format.labelString: AU", "*wav_format.labelString: WAV", "*voc_format.labelString: VOC", "*aiff_format.labelString: AIFF", "*mp3_format.labelString: MP3", "*overwrite_button.labelString: Ask before overwriting file", "*record_button.labelString: Record", "*stop_button.labelString: Stop", "*playback_button.labelString: Playback", "*dismiss_button.labelString: Dismiss", "*question.cancelLabelString: No", "*question.okLabelString: Yes", "*filename_search.labelString: ...", "*save_button.labelString: Save", "*apply_button.labelString: Apply", "*cancel_button.labelString: Cancel", "*delEntry_button.labelString:Delete Entry", "*newEntry_button.labelString:New Entry", "*delLabel.labelString: Delete", "*statLabel.labelString: Station", "*freqLabel.labelString: Frequency", "*defLabel.labelString: Station Button", "*def.labelString: define", "*start.labelString: start", "*visualization_mode.labelString: Visualization", "*analyzer_mode.labelString: Analyzer", "*scope_mode.labelString: Scope", "*refresh_mode.labelString: Refresh", "*vis_analyzer.labelString: Analyzer", "*vis_scope.labelString: Scope", "*ana_normal.labelString: Normal", "*ana_fire.labelString: Fire", "*ana_vert.labelString: Vertical Lines", "*ana_lines.labelString: Lines", "*ana_bars.labelString: Bars", "*scope_dot.labelString: Dot Scope", "*scope_line.labelString: Line Scope", "*scope_solid.labelString: Solid Scope ", "*refresh_full.labelString: Full (~50 fps)", "*refresh_half.labelString: Half (~25 fps)", "*refresh_quarter.labelString: Quarter (~13 fps)", "*refresh_eight.labelString: Eight (~6 fps)", NULL }; extern void _XEditResCheckMessages(Widget, XtPointer, XEvent*, Boolean*); void ParseStationList(); void ParseStationListAppDef(); void ParseStationButtonListAppDef(); void CreateMenuWidget(char *widget_name, Widget parent); void MakeMotifInterface(); int UpdateMixerSlider(); void UpdateSliderValue(int, int, int); int UpdateChanges(); int CalcFieldStrength(); void UpdateFieldstrength(int strength); void more_popup(Widget, XEvent*, String*, Cardinal*); void station_popup(Widget, XEvent*, String*, Cardinal*); void station_up(Widget, XEvent*, String*, Cardinal*); void station_down(Widget, XEvent*, String*, Cardinal*); void station_seek_up(Widget, XEvent*, String*, Cardinal*); void station_seek_down(Widget, XEvent*, String*, Cardinal*); static void HandleXEvents(); static XmString xmStringFromInt(int value, int decimalPoints); #ifdef HAS_XPM Pixmap skin; void SetSkin(); #endif void InitHack() { #ifdef linux return; #else #ifdef JUHA_DRIVER return; #else int channel=3; if(!initHack) return; if(ioctl(tuner, TVTUNER_SETCHNL, &channel) == -1) { fprintf(stderr, "TVTUNER_SETCHNL failed in InitHack(): %s\n", strerror(errno)); } #endif #endif } int InitTuner(int startfreq) { #ifdef linux struct video_audio arg; #else int new_audio; #endif if((tuner = open(TUNER_DEVICE, O_RDONLY)) == -1) { fprintf(stderr, "could not open %s: %s\n", TUNER_DEVICE, strerror(errno)); tuner = -1; return False; } #ifdef linux if (ioctl(tuner, VIDIOCGAUDIO, &arg)) { fprintf(stderr, "VIDIOCGAUDIO failed in InitTuner(): %s\n", strerror(errno)); } if (arg.volume == 0) arg.volume = 65535; arg.flags &= ~VIDEO_AUDIO_MUTE; if(ioctl(tuner, VIDIOCSAUDIO, &arg)) { fprintf(stderr, "VIDIOCSAUDIO failed in InitTuner(): %s\n", strerror(errno)); } #else #ifdef JUHA_DRIVER new_audio = AUDIO_TUNER; if(ioctl(tuner, TUNER_SAUDIO, &new_audio) == -1) { fprintf(stderr, "could not set audio to tuner: %s\n", strerror(errno)); } #else InitHack(); // explicitly set channel set (problem with older drivers) if(ioctl(tuner, TVTUNER_SETTYPE, &chnlset) == -1) { fprintf(stderr, "TVTUNER_SETTYPE failed in InitTuner(); %s\n", strerror(errno)); } // better switch it off at the beginning (problem with older drivers) SetAFC(False); new_audio = AUDIO_INTERN; if(ioctl(tuner, BT848_SAUDIO, &new_audio) == -1) { fprintf(stderr, "could not set audio to intern: %s\n", strerror(errno)); } #endif #endif SetFrequency(startfreq); return(True); } int InitMixer() { int ctrls, ret; ret = True; if((mixer = open(MIXER_DEVICE, O_RDWR)) == -1) { fprintf(stderr, "could not open %s: %s\n", MIXER_DEVICE, strerror(errno)); mixer = -1; return(False); } if(ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &ctrls) == -1) { fprintf(stderr, "%s query for devices failed: %s\n", MIXER_DEVICE, strerror(errno)); close(mixer); mixer = -1; return(False); } if(!(ctrls & SOUND_MASK_LINE)) { fprintf(stderr, "Can't control radio volume via %s: %s\n", MIXER_DEVICE, strerror(errno)); mixer = -1; ret = False; } if(!(ctrls & SOUND_MASK_BASS)) { fprintf(stderr, "Can't control bass via %s: %s\n", MIXER_DEVICE, strerror(errno)); bass = -1; ret = False; } if(!(ctrls & SOUND_MASK_TREBLE)) { fprintf(stderr, "Can't control treble via %s: %s\n", MIXER_DEVICE, strerror(errno)); treble = -1; ret = False; } if(ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &ctrls) == -1) { fprintf(stderr, "%s query for stereo capable device: %s\n", MIXER_DEVICE, strerror(errno)); ret = False; } if(!(ctrls & SOUND_MASK_LINE)) stereo = False; if(ioctl(mixer, SOUND_MIXER_READ_RECMASK, &ctrls) == -1) { fprintf(stderr, "%s query for record capable device: %s\n", MIXER_DEVICE, strerror(errno)); ret = False; } if(!(ctrls & SOUND_MASK_LINE)) { XtSetSensitive(analyzerW, False); XtSetSensitive(sampleW, False); } UpdateMixerSlider(); return(ret); } int SetMute(int mute) { int ret = 0; #ifdef linux struct video_audio arg; if(ioctl(tuner, VIDIOCGAUDIO, &arg) == -1) { fprintf(stderr, "failed to get audio mode (mute): %s\n", strerror(errno)); return -1; } ret = arg.flags & VIDEO_AUDIO_MUTE; if (mute) arg.flags |= VIDEO_AUDIO_MUTE; else arg.flags &= ~VIDEO_AUDIO_MUTE; if(ioctl(tuner, VIDIOCSAUDIO, &arg) == -1) { fprintf(stderr, "failed to set audio mode (mute): %s\n", strerror(errno)); return -1; } #else int arg; #ifdef JUHA_DRIVER if(ioctl(tuner, TUNER_GAUDIO, &arg) == -1) { fprintf(stderr, "failed to get radio mode (mute): %s\n", strerror(errno)); return -1; } ret = arg & AUDIO_MUTE; if(mute) arg = AUDIO_MUTE; else arg = AUDIO_UNMUTE; if(ioctl(tuner, TUNER_SAUDIO, &arg) == -1) { fprintf(stderr, "failed to mute radio: %s\n", strerror(errno)); return -1; } #else if(ioctl(tuner, BT848_GAUDIO, &arg) == -1) { fprintf(stderr, "failed to get radio mode (mute): %s\n", strerror(errno)); return -1; } ret = arg & AUDIO_MUTE; if(mute) arg = AUDIO_MUTE; else arg = AUDIO_UNMUTE; if(ioctl(tuner, BT848_SAUDIO, &arg) == -1) { fprintf(stderr, "failed to set radio mode (mute): %s\n", strerror(errno)); return -1; } #endif #endif UpdateChanges(); return ret; } int SetFrequency(int freq) { #ifdef linux int ifreq; #endif #ifdef JUHA_DRIVER freq_t ifreq; #endif if(freq < MINFREQ || freq > MAXFREQ) { fprintf(stderr, "frequency out of range: %d\n", freq); return False; } #ifdef linux ifreq = freq * 16 / 100; if(ioctl(tuner, VIDIOCSFREQ, &ifreq) == -1) { fprintf(stderr, "failed to set new frequency: %s\n", strerror(errno)); return False; } frequency = freq; #else #ifdef JUHA_DRIVER ifreq = freq*10; if(ioctl(tuner, FM_SETFREQUENCY, &ifreq) == -1) { fprintf(stderr, "failed to set new frequency: %s\n", strerror(errno)); return False; } if(ioctl(tuner, FM_GETFREQUENCY, &ifreq) == -1) { fprintf(stderr, "failed to get new frequency: %s\n", strerror(errno)); fprintf(stderr, "trying to handle it...\n"); } frequency = ifreq/10; #else if(ioctl(tuner, RADIO_SETFREQ, &freq) == -1) { fprintf(stderr, "failed to set new frequency: %s\n", strerror(errno)); return False; } if(ioctl(tuner, RADIO_GETFREQ, &frequency) == -1) { fprintf(stderr, "failed to get new frequency: %s\n", strerror(errno)); fprintf(stderr, "trying to handle it...\n"); } #endif #endif UpdateSliderValue(FREQUENCY, frequency, 2); UpdateFieldstrength(CalcFieldStrength()); UpdateTitle(); return True; } int SetStation(const char *name) { int i; if(debug) printf("SetStation to %s\n", name); for(i = 0; i < station.cnt; i++) { if(!strcmp(name, station.name[i])) { SetFrequency(station.freq[i]); return True; } } return False; } int SetVolume(int new_vol) { int vol, left, right; if(mixer == -1) return False; if(new_vol < MINVOL || new_vol > MAXVOL) { fprintf(stderr, "volume out of range (%d).\n", new_vol); return False; } if(balance < 0) { left = new_vol + (new_vol*balance/100.); right = new_vol; } else { right = new_vol - (new_vol*balance/100.); left = new_vol; } vol = (left << 8) | right; if(ioctl(mixer, MIXER_WRITE(SOUND_MIXER_LINE), &vol) == -1) { fprintf(stderr, "failed to set new volume: %s\n", strerror(errno)); return False; } if(volume != new_vol) { volume = new_vol; UpdateSliderValue(VOLUME, volume, 0); } return True; } int SetBalance(int new_bal) { int ret; if(new_bal < -100 || new_bal > 100) return False; balance = new_bal; ret = SetVolume(volume); UpdateSliderValue(BALANCE, balance, 0); return ret; } int SetTreble(int new_trb) { treble = new_trb; if(SetSound(SOUND_MIXER_TREBLE, new_trb) == False) return False; UpdateSliderValue(TREBLE, new_trb, 0); return True; } int SetBass(int new_bss) { bass = new_bss; if(SetSound(SOUND_MIXER_BASS, new_bss) == False) return False; UpdateSliderValue(BASS, new_bss, 0); return True; } int SetAFC(int onoff) { #ifdef linux /* This functionality is not available */ return True; #else int arg; if(ioctl(tuner, RADIO_GETMODE, &arg) == -1) { fprintf(stderr, "failed to get radio mode: %s\n", strerror(errno)); return False; } if(onoff) { arg |= RADIO_AFC; afc = True; SetFrequency(frequency); } else { arg &= ~RADIO_AFC; afc = False; } if(ioctl(tuner, RADIO_SETMODE, &arg) == -1) { fprintf(stderr, "failed to set radio mode: %s\n", strerror(errno)); return False; } UpdateChanges(); return True; #endif } void UpdateStatus(XtPointer clientData, XtIntervalId *id) { if(!seeking) { UpdateFieldstrength(CalcFieldStrength()); #ifdef JUHA_DRIVER update_radio_indicator(indicatorW); #endif } XtAppAddTimeOut(app_con, 250, UpdateStatus, NULL); } void SetStereoIndicator(int onoff) { if(onoff) XtVaSetValues(stereoW, XmNlabelString, stereo_label_string, XmNset, XmSET, NULL); else XtVaSetValues(stereoW, XmNlabelString, mono_label_string, XmNset, XmUNSET, NULL); lcd_stereo = onoff; } int SetStereo(int onoff) { #ifdef linux struct video_audio arg; if(ioctl(tuner, VIDIOCGAUDIO, &arg) == -1) { fprintf(stderr, "failed to get audio mode (mute): %s\n", strerror(errno)); return -1; } arg.audio = 0; arg.flags = 0; if (onoff) arg.mode = VIDEO_SOUND_STEREO; else arg.mode = VIDEO_SOUND_MONO; if(ioctl(tuner, VIDIOCSAUDIO, &arg) == -1) { fprintf(stderr, "failed to set audio mode: %s\n", strerror(errno)); return False; } #else int arg; if(ioctl(tuner, RADIO_GETMODE, &arg) == -1) { fprintf(stderr, "failed to get radio mode: %s\n", strerror(errno)); return False; } if(onoff) arg &= ~RADIO_MONO; else arg |= RADIO_MONO; if(ioctl(tuner, RADIO_SETMODE, &arg) == -1) { fprintf(stderr, "failed to set radio mode: %s\n", strerror(errno)); return False; } #endif UpdateChanges(); return True; } int SetSound(int dev, int arg) { if(mixer == -1) return False; if(arg < 0 || arg > 100) { fprintf(stderr, "value out of range (%d).\n", arg); return False; } arg = (arg << 8) | arg; if(dev == SOUND_MIXER_TREBLE) { if(ioctl(mixer, MIXER_WRITE(dev), &arg) == -1) { fprintf(stderr, "failed to set treble: %s\n", strerror(errno)); } return True; } if(dev == SOUND_MIXER_BASS) { if(ioctl(mixer, MIXER_WRITE(dev), &arg) == -1) { fprintf(stderr, "failed to set bass: %s\n", strerror(errno)); } return True; } fprintf(stderr, "wrong dev to change.\n"); return False; } void SeekChannel(int direction) { int oldmute, oldfrequency, raising; search_point start, last, actual; seeking = True; seekstop = False; XDefineCursor(dpy, XtWindow(toplevel), workingCursor); XmUpdateDisplay(toplevel); oldmute = SetMute(True); oldfrequency = frequency; raising = 0; SetStereo(True); start.freq = frequency; start.strength = CalcFieldStrength(); last.freq = last.strength = 0; while(!seekstop) { for(actual.freq = frequency; actual.freq <= MAXFREQ && actual.freq >= MINFREQ && !seekstop; actual.freq += direction * FREQSTEP) { SetFrequency(actual.freq); actual.strength = CalcFieldStrength(); if(actual.strength > start.strength) { start = actual; raising = 1; } if(actual.strength < start.strength) { if(raising > 0) { oldfrequency = (last.freq + start.freq + 0.5) / 2; oldfrequency = (oldfrequency / 5) * 5; seekstop = True; } else { start = actual; } raising = -1; } last = actual; HandleXEvents(); } frequency = (direction > 0) ? MINFREQ : MAXFREQ; } seeking = False; XSync(dpy, True); XUndefineCursor(dpy, XtWindow(toplevel)); SetFrequency(oldfrequency); SetMute(oldmute); } int CalcFieldStrength() { #ifdef JUHA_DRIVER struct tuner_status arg; static int old_afc; #else int arg; #endif #ifdef linux struct video_tuner v; if(tuner < 0) return(0); v.tuner = 0; if(ioctl(tuner, VIDIOCGTUNER, &v) == -1) { fprintf(stderr, "failed to get RF input level: %s\n", strerror(errno)); lcd_fieldstrength = 0; return 0; } /* Not sure if this is the right scaling factor */ arg = v.signal / (0xffff / 0x7); lcd_fieldstrength = arg; return arg; #else static int old_stereo; #ifdef JUHA_DRIVER if(tuner < 0) return(0); if(ioctl(tuner, TUNER_GETSTATUS, &arg) != 0) { fprintf(stderr, "failed to get tuner status level: %s\n", strerror(errno)); lcd_fieldstrength = 0; return 0; } else { //if(debug) // printf("signal: %d afc: %d stereo: %s\n", arg.rssi, arg.afc, // arg.stereo ? "True" : "False"); if(old_afc != arg.afc) { if(arg.afc) XtVaSetValues(afcW, XmNset, XmUNSET, NULL); else XtVaSetValues(afcW, XmNset, XmSET, NULL); old_afc = arg.afc; } if(old_stereo != arg.stereo) { if(arg.stereo) SetStereoIndicator(True); else SetStereoIndicator(False); old_stereo = arg.stereo; } //printf("AFC : %d kHz\n", arg.afc); //printf("RSSI: %d\n", arg.rssi); lcd_fieldstrength = arg.rssi * 4 / 255; return(lcd_fieldstrength); } #else int stereo_i; if(tuner < 0) return(0); if(ioctl(tuner, TVTUNER_GETSTATUS, &arg) == -1) { fprintf(stderr, "failed to get RF input level: %s\n", strerror(errno)); lcd_fieldstrength = 0; return 0; } stereo_i = arg & 0x10; if(old_stereo != stereo_i) { if(stereo_i) SetStereoIndicator(True); else SetStereoIndicator(False); old_stereo = stereo_i; } lcd_fieldstrength = (arg &= 0x07); return arg &= 0x07; #endif #endif } int UpdateChanges() { #ifdef linux /* Why do we need this? Works fine without this */ #else /* believe me, at least FreeBSD up to 3.0 needs it */ #ifdef JUHA_DRIVER freq_t ifreq; if(ioctl(tuner, FM_GETFREQUENCY, &ifreq) == -1) { fprintf(stderr, "failed to get current frequency: %s\n", strerror(errno)); return False; } if(ifreq > 10*MAXFREQ || ifreq < 10*MINFREQ) ifreq = 10*MINFREQ; if(ioctl(tuner, FM_SETFREQUENCY, &ifreq) == -1) { fprintf(stderr, "failed to set current frequency: %s\n", strerror(errno)); return(False); } #else int freq; if(ioctl(tuner, RADIO_GETFREQ, &freq) == -1) { fprintf(stderr, "failed to get current frequency: %s\n", strerror(errno)); return(False); } if(freq > MAXFREQ || freq < MINFREQ) freq = MINFREQ; if(ioctl(tuner, RADIO_SETFREQ, &freq) == -1) { fprintf(stderr, "failed to set current frequency: %s\n", strerror(errno)); return(False); } #endif #endif return(True); } void UpdateSliderValue(int slider, int value, int decPoints) { XmString xstr = xmStringFromInt(value, decPoints); if((!*(sliderTable[slider].widget)) && !XtIsRealized(*(sliderTable[slider].widget))) return; XtVaSetValues(*(sliderTable[slider].widgetValue), XmNlabelString, xstr, NULL); if(slider != BALANCE && value < 0) { XtSetSensitive(*(sliderTable[slider].widget), False); XtVaSetValues(*(sliderTable[slider].widget), XmNvalue, 0, NULL); if(slider == VOLUME) { XtSetSensitive(*(sliderTable[VOL].widget), False); XtVaSetValues(*(sliderTable[VOL].widget), XmNvalue, 0, NULL); } if(slider == VOL) { XtSetSensitive(*(sliderTable[VOLUME].widget), False); XtVaSetValues(*(sliderTable[VOLUME].widget), XmNvalue, 0, NULL); } } else { XtSetSensitive(*(sliderTable[slider].widget), True); XtVaSetValues(*(sliderTable[slider].widget), XmNvalue, value, NULL); if(slider == VOLUME) { XtSetSensitive(*(sliderTable[VOL].widget), True); XtVaSetValues(*(sliderTable[VOL].widget), XmNvalue, value, NULL); } if(slider == VOL) { XtSetSensitive(*(sliderTable[VOLUME].widget), True); XtVaSetValues(*(sliderTable[VOLUME].widget), XmNvalue, value, NULL); } } XmUpdateDisplay(*(sliderTable[slider].widgetValue)); XmUpdateDisplay(*(sliderTable[slider].widget)); XmStringFree(xstr); } int UpdateMixerSlider() { int right, left, ret = True; int new_vol, new_bas, new_trbl; if(mixer == -1) return False; if(ioctl(mixer, MIXER_READ(SOUND_MIXER_LINE), &new_vol) == -1) { fprintf(stderr, "Can't read line volume: %s\n", strerror(errno)); new_vol = -1; ret = False; } if(ioctl(mixer, MIXER_READ(SOUND_MIXER_BASS), &new_bas) == -1) { fprintf(stderr, "Can't read bass value: %s\n", strerror(errno)); new_bas = -1; ret = False; } if(ioctl(mixer, MIXER_READ(SOUND_MIXER_TREBLE), &new_trbl) == -1) { fprintf(stderr, "Can't read treble value: %s\n", strerror(errno)); new_trbl = -1; ret = False; } left = (new_vol & 0xff); right = ((new_vol >> 8) & 0xff); balance = right - left; new_vol = left > right ? left : right; left = (new_bas & 0xff); right = ((new_bas >> 8) & 0xff); if(right == left) new_bas = left; else new_bas = -1; left = (new_trbl & 0xff); right = ((new_trbl >> 8) & 0xff); if(right == left) new_trbl = left; else new_trbl = -1; if(new_vol != volume) { volume = new_vol; UpdateSliderValue(VOLUME, volume, 0); UpdateSliderValue(BALANCE, balance, 0); } if(new_bas != bass) { bass = new_bas; UpdateSliderValue(BASS, bass, 0); } if(new_trbl != treble) { treble = new_trbl; UpdateSliderValue(TREBLE, treble, 0); } return(ret); } void UpdateFieldstrength(int strength) { static int pos, oldvalue; strength++; if(strength == oldvalue) return; // hardware has 3 bits for field strength if(strength < 0 || strength > 7) { fprintf(stderr, "UpdateFieldstrength() value out of range: %d\n", strength); return; } oldvalue = strength; if(wantLiteClue && !pos) { int i; pos = -1; for(i=0; i station.freq[i] - BANDWIDTH) && (frequency < station.freq[i] + BANDWIDTH)) { st = station.name[i]; sprintf(freq, "%d", station.freq[i]); } } if(!st) { if(seeking) st = "*seeking*"; else st = "*unknown station*"; } if(st && strlen(st) && laststation && strlen(laststation)) { if(!strcmp(st, laststation)) return; } laststation = st; global_station_name = st; XChangeProperty(dpy, XtWindow(toplevel), XA_XMRADIO_STATION, XA_STRING, 8, PropModeReplace, st, strlen(st)); if(gui == MINIMAL_GUI) XtVaSetValues(toplevel, XmNtitle, st, NULL); else { strncpy(buffer, title, 1020); strcat(buffer, ": "); strncat(buffer, st, 1023-strlen(buffer)); XtVaSetValues(toplevel, XmNtitle, buffer, NULL); } XtVaSetValues(toplevel, XmNiconName, st, NULL); XmUpdateDisplay(toplevel); } static void quitCB(Widget widget, XtPointer clientData, XtPointer callData) { close(mixer); close(tuner); if(sockfd) lcd_net_stop(); exit(EXIT_SUCCESS); } static void HandleXEvents() { XtInputMask mask; while((mask = XtAppPending(app_con)) != 0) XtAppProcessEvent(app_con, mask); return; } static XmString xmStringFromInt(int value, int decimalPoints) { static char buffer[17]; float fval; fval = value / pow(10, decimalPoints); if(decimalPoints) sprintf(buffer, "%.2f", fval); else sprintf(buffer, "%.0f", fval); return XmStringCreateLtoR(buffer, XmSTRING_DEFAULT_CHARSET); } static int HandleXError(Display *dpy, XErrorEvent *event) { char msg[80]; XGetErrorText(dpy, event->error_code, msg, 80); fprintf(stderr, "Error code %s\n", msg); return 0; } static void aboutCB(Widget widget, XtPointer clientData, XtPointer callData) { static Widget dialog; char buffer[512]; if(dialog == NULL) { Pixel fg, bg; Display *dpy = XtDisplay(toplevel); Pixmap bitmap; XmString string; dialog = XmCreateInformationDialog(toplevel, "AboutDialog", NULL, 0); XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON)); XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON)); XtVaGetValues(dialog, XmNbackground, &bg, NULL); fg = BlackPixel(dpy, DefaultScreen(dpy)); 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); string = XmStringCreateLtoR(buffer, XmSTRING_DEFAULT_CHARSET); #ifdef HAS_XPM if(XpmCreatePixmapFromData(dpy, DefaultRootWindow(dpy), tom_xpm, &bitmap, NULL, NULL) < XpmSuccess) #endif /* HAS_XPM */ bitmap = XCreatePixmapFromBitmapData(dpy, XtWindow(toplevel), (char *) tom_bits, tom_width, tom_height, fg, bg, DefaultDepth (dpy, DefaultScreen(dpy))); XtVaSetValues(dialog, XmNautoUnmanage, True, XmNsymbolPixmap, bitmap, XmNmessageString, string, NULL); XmStringFree(string); #ifdef HAS_XPM if(skin) SkinToWidgets(dialog, skin); #endif } XtManageChild (dialog); } static void sliderCB(Widget widget, XtPointer clientData, XtPointer callData) { int value = ((XmScrollBarCallbackStruct*) callData)->value; sliderTable[(int)clientData].func(value); } static void freqButtonCB(Widget widget, XtPointer clientData, XtPointer callData) { Dimension w; XtVaGetValues(freqDummyW, XmNwidth, &w, NULL); if(slider_mode == FREQ_MODE) { XtUnmanageChild(freqW); if(station_buttons.cnt) { XtVaSetValues(stabtnW, XmNwidth, w, NULL); XtManageChild(stabtnW); } else { XtManageChild(nostationslabelW); } XtVaSetValues(freqButtonW, XmNlabelString, stations_label_string, NULL); slider_mode = STATION_MODE; return; } if(slider_mode == VOLUME_MODE) { XtUnmanageChild(volW); XtManageChild(freqW); XtVaSetValues(freqButtonW, XmNlabelString, frequency_label_string, NULL); slider_mode = FREQ_MODE; return; } if(slider_mode == STATION_MODE) { if(station_buttons.cnt) { XtUnmanageChild(stabtnW); } else { XtUnmanageChild(nostationslabelW); } XtManageChild(volW); XtVaSetValues(freqButtonW, XmNlabelString, volume_label_string, NULL); slider_mode = VOLUME_MODE; return; } return; } static void sampleCB(Widget widget, XtPointer clientData, XtPointer callData) { SampleDialogToggle(); } static void configCB(Widget widget, XtPointer clientData, XtPointer callData) { ConfigDialogToggle(); } void lcdDisconnectCB(const char *reason) { XtSetSensitive(lcdConnectW, True); XtSetSensitive(lcdDisconnectW, False); sockfd = 0; if(reason) XtomShowMessage(toplevel, XmDIALOG_ERROR, "LCD connect", reason); } static void lcdConnectCB(Widget widget, XtPointer clientData, XtPointer callData) { const char *neterror; int what = (int)clientData; if(what == CONNECT) { sockfd = lcd_net_init(lcd_host, lcd_port, &neterror); if(sockfd < 0) { fprintf(stderr, "error initializing connection to lcd server: %s\n", neterror); lcdDisconnectCB(neterror); } else { XtSetSensitive(lcdConnectW, False); XtSetSensitive(lcdDisconnectW, True); lcd_net_start(); } } else { lcd_net_stop(); } } static void versionCB(Widget widget, XtPointer clientData, XtPointer callData) { CheckNewVersion(); } void version_check(int result, char *description) { if(result == -1) { char *buf; char msg[] = "Error asking for new version!"; char err[] = "(no error description available)"; buf = (char*)malloc(strlen(msg) + (description ? strlen(description) : strlen(err)) + 5); sprintf(buf, "%s\n\n%s", msg, description ? description : err); XtomShowMessage(toplevel, XmDIALOG_ERROR, "Versioncheck", buf); free(buf); } else { float old_ver, new_ver; char *t1, *t2; char *buf; t1 = strtok(description, "\\"); t2 = strtok(NULL, "\\"); new_ver = atof(t1); old_ver = atof(APPVERSION); if(debug) { printf("version check returned: %s\n", description ? description : "nothing(?)"); printf("this version: %f\nnew version: %f\nmessage: %s\n", old_ver, new_ver, t2); } if(new_ver > old_ver) { char msg[] = "Newer version available: "; buf = (char*)malloc(strlen(msg) + strlen(t1) + strlen(t2) + 5); sprintf(buf, "%s%s\n\n%s", msg, t1, t2); XtomShowMessage(toplevel, XmDIALOG_INFORMATION, "Versioncheck", buf); free(buf); } else { char msg1[] = "Sorry, no newer version available."; char msg2[] = "Actual version: "; buf = (char*)malloc(strlen(msg1) + strlen(msg2) + strlen(t1) + strlen(t2) + 5); sprintf(buf, "%s\n\n%s%s\n\n%s", msg1, msg2, t1, t2); XtomShowMessage(toplevel, XmDIALOG_MESSAGE, "Versioncheck", buf); } } free(description); } void version_enable_button(int onoff) { XtSetSensitive(versionW, onoff); } static void analyzerCB(Widget widget, XtPointer clientData, XtPointer callData) { AnalyzerToggle(); } static int old_recsrc; static Boolean sampling; static sampleStruct *ss; int SampleStart(sampleStruct *_ss) { int new_recsrc, tmp; ss = _ss; if(mixer == -1) return False; /* getting old rec mask */ if(ioctl(mixer, SOUND_MIXER_READ_RECSRC, &old_recsrc) == -1) { fprintf(stderr, "couldnt get mixer recsrc: %s\n", strerror(errno)); return False; } /* setting rec mask to line channel */ new_recsrc = SOUND_MASK_LINE; if(ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &new_recsrc) == -1) { fprintf(stderr, "couldnt set mixer recsrc: %s\n", strerror(errno)); return False; } /* opening dsp device */ if((dsp = open(DSP_DEVICE, O_RDONLY, 0)) == -1) { fprintf(stderr, "couldnt open %s: %s\n", DSP_DEVICE, strerror(errno)); dsp = -1; return False; } /* selecting sample format */ tmp = ss->format; if(ioctl(dsp, SNDCTL_DSP_SETFMT, &ss->format) == -1) { fprintf(stderr, "couldnt set dsp format: %s\n", strerror(errno)); SampleEnd(); dsp = -1; return False; } /* checking sample format */ if(ss->format != tmp && debug) { fprintf(stderr, "couldnt set dsp to sample format: %s\n", strerror(errno)); fprintf(stderr, " (wanted %d, got %d; trying to handle it)\n", tmp, ss->format); } /* selecting number of channels */ tmp = ss->stereo; if(ioctl(dsp, SNDCTL_DSP_STEREO, &ss->stereo) == -1) { fprintf(stderr, "couldnt set dsp to stereo: %s\n", strerror(errno)); SampleEnd(); dsp = -1; return False; } /* checking sample format */ if((ss->stereo != tmp) && debug) { fprintf(stderr, "couldnt set dsp to stereo/mono: %s\n", strerror(errno)); fprintf(stderr, " (trying to handle it)\n"); } /* selecting sampling rate */ tmp = ss->speed; if(ioctl(dsp, SNDCTL_DSP_SPEED, &ss->speed) == -1) { fprintf(stderr, "couldnt set dsp speed: %s\n", strerror(errno)); SampleEnd(); dsp = -1; return False; } /* checking sample format */ if((ss->speed != tmp) && debug) { fprintf(stderr, "couldnt set dsp to stereo, got mono: %s\n", strerror(errno)); fprintf(stderr, " (trying to handle it)\n"); } sampling = True; XtAppAddWorkProc(app_con, Sample, (XtPointer)NULL); return True; } int SampleEnd() { sampling = False; if(mixer == -1) return True; close(dsp); /* resetting rec mask */ if(ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &old_recsrc) == -1) { fprintf(stderr, "couldnt reset mixer recsrc: %s\n", strerror(errno)); return False; } return True; } Boolean Sample(XtPointer clientData) { if(!sampling || dsp == -1) return True; ss->bufsize = ss->speed * (ss->stereo+1) * ss->bps / REFRESHRATE; ss->bufsize = ss->bufsize > BUF_SIZE ? BUF_SIZE : ss->bufsize; if(read(dsp, audio_buffer, ss->bufsize * ss->bps) == -1) { fprintf(stderr, "couldnt get all data: %s\n", strerror(errno)); } ss->func(); return False; } static void guiExpandCB(Widget widget, XtPointer clientData, XtPointer callData) { if(gui < NORMAL_GUI) return; if(gui == TINY_GUI) { gui = EXPANDED_GUI; XtVaSetValues(widget, XmNarrowDirection, XmARROW_UP, NULL); XtManageChild(soundFormW); } else { gui = TINY_GUI; XtVaSetValues(widget, XmNarrowDirection, XmARROW_DOWN, NULL); XtUnmanageChild(soundFormW); } } static void switchCB(Widget widget, XtPointer clientData, XtPointer callData) { XmPushButtonCallbackStruct *pbcbstr; pbcbstr = (XmPushButtonCallbackStruct*) callData; if(pbcbstr->event->xbutton.state&ShiftMask) { if((int)clientData == DOWN) XtCallActionProc(toplevel, "StationSeekDown", NULL, NULL, 0); else XtCallActionProc(toplevel, "StationSeekUp", NULL, NULL, 0); } else { if((int)clientData == DOWN) XtCallActionProc(toplevel, "StationDown", NULL, NULL, 0); else XtCallActionProc(toplevel, "StationUp", NULL, NULL, 0); } } static void seekCB(Widget widget, XtPointer clientData, XtPointer callData) { if(seeking) seekstop = True; else SeekChannel((int)clientData); } static void afcCB(Widget widget, XtPointer clientData, XtPointer callData) { XmToggleButtonCallbackStruct *tbcs; tbcs = (XmToggleButtonCallbackStruct*) callData; SetAFC(tbcs->set); } static void stereoCB(Widget widget, XtPointer clientData, XtPointer callData) { XmToggleButtonCallbackStruct *tbcs; tbcs = (XmToggleButtonCallbackStruct*) callData; SetStereo(tbcs->set); } static void muteCB(Widget widget, XtPointer clientData, XtPointer callData) { XmToggleButtonCallbackStruct *tbcs; tbcs = (XmToggleButtonCallbackStruct*) callData; SetMute(tbcs->set); } void propertyCB(Widget widget, XtPointer clientData, XEvent* event, Boolean *continueToDispatch) { int error = False; char *cmd; *continueToDispatch = True; if(debug) { if(event->xany.type == PropertyNotify && event->xproperty.window == XtWindow(toplevel) && event->xproperty.atom == XA_XMRADIO_LOCK && event->xproperty.state == PropertyNewValue) { printf("property changed: Got a lock!\n"); } if(event->xany.type == PropertyNotify && event->xproperty.window == XtWindow(toplevel) && event->xproperty.atom == XA_XMRADIO_LOCK && event->xproperty.state == PropertyDelete) { printf("property changed: Lock removed!\n"); if(quit_command) XtCallCallbacks(quitW, XmNactivateCallback, (XtPointer)NULL); } if(event->xany.type == PropertyNotify && event->xproperty.window == XtWindow(toplevel) && event->xproperty.atom == XA_XMRADIO_COMMAND && event->xproperty.state == PropertyDelete) { printf("property changed: command removed!\n"); } } if(event->xany.type == PropertyNotify && event->xproperty.window == XtWindow(toplevel) && event->xproperty.atom == XA_XMRADIO_COMMAND && event->xproperty.state == PropertyNewValue) { Atom actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *prop; char *tmp; if(debug) printf("property changed: Got a command\n"); XGetWindowProperty(dpy, XtWindow(widget), XA_XMRADIO_COMMAND, 0, 8192, True, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &prop); if(debug) printf(" command: %s\n", prop ? (char*)prop : "nothing?!?"); // ack XChangeProperty(dpy, XtWindow(widget), XA_XMRADIO_RESPONSE, XA_STRING, 8, PropModeReplace, "1", 1); XSync(dpy, False); if(!strcmp(prop, "StationUp") || !strcmp(prop, "StationDown") || !strcmp(prop, "StationSeekUp") || !strcmp(prop, "StationSeekDown")) { XtCallActionProc(toplevel, prop, NULL, NULL, 0); goto READY; } cmd = strtok(prop, "="); if(!strcmp(cmd, "Frequency")) { tmp = strtok(NULL, "="); if(tmp) { if(debug) printf("set frequency command. changing to %d\n", (int)(100. * (atof(tmp) + 0.5))); error = !SetFrequency((int)(100. * (atof(tmp) + 0.5))); } else error = True; goto READY; } if(!strcmp(cmd, "Station")) { tmp = strtok(NULL, "="); if(tmp) { if(debug) printf("set station command. changing to %s\n", tmp); error = !SetStation(tmp); } else error = True; goto READY; } if(!strcmp(cmd, "AFC")) { tmp = strtok(NULL, "="); if(tmp) { int res = 0; res = strcmp(tmp, "on") ? 0 : 1; if(debug) printf("set afc command. changing to %s\n", res ? "on" : "off"); error = !SetAFC(res); XtVaSetValues(afcW, XmNset, res, NULL); } else error = True; goto READY; } if(!strcmp(cmd, "Stereo")) { tmp = strtok(NULL, "="); if(tmp) { int res = 0; res = strcmp(tmp, "on") ? 0 : 1; if(debug) printf("set stereo command. changing to %s\n", res ? "on" : "off"); error = !SetStereo(res); XtVaSetValues(stereoW, XmNset, res, NULL); } else error = True; goto READY; } if(!strcmp(cmd, "Mute")) { tmp = strtok(NULL, "="); if(tmp) { int res = 0; res = strcmp(tmp, "on") ? 0 : 1; if(debug) printf("set mute command. changing to %s\n", res ? "on" : "off"); error = (SetMute(res) == -1) ? True : False; XtVaSetValues(muteW, XmNset, res, NULL); } else error = True; goto READY; } if(!strcmp(cmd, "Balance")) { tmp = strtok(NULL, "="); if(tmp) { if(debug) printf("balance command. changing to %d\n", atoi(tmp)); error = !SetBalance(atoi(tmp)); } else error = True; goto READY; } if(!strcmp(cmd, "Treble")) { tmp = strtok(NULL, "="); if(tmp) { if(debug) printf("treble command. changing to %d\n", atoi(tmp)); error = !SetTreble(atoi(tmp)); } else error = True; goto READY; } if(!strcmp(cmd, "Bass")) { tmp = strtok(NULL, "="); if(tmp) { if(debug) printf("bass command. changing to %d\n", atoi(tmp)); error = !SetBass(atoi(tmp)); } else error = True; goto READY; } if(!strcmp(cmd, "Volume")) { tmp = strtok(NULL, "="); if(tmp) { if(debug) printf("volume command. changing to %d\n", atoi(tmp)); error = !SetVolume(atoi(tmp)); } else error = True; goto READY; } if(!strcmp(cmd, "ShowAbout")) { XtCallCallbacks(aboutW, XmNactivateCallback, (XtPointer)NULL); goto READY; } if(!strcmp(cmd, "ShowAnalyzer")) { XtCallCallbacks(analyzerW, XmNactivateCallback, (XtPointer)NULL); goto READY; } if(!strcmp(cmd, "Iconify")) { XIconifyWindow(dpy, XtWindow(toplevel), DefaultScreen(dpy)); goto READY; } if(!strcmp(cmd, "Withdraw")) { XWithdrawWindow(dpy, XtWindow(toplevel), DefaultScreen(dpy)); goto READY; } if(!strcmp(cmd, "Deiconify")) { XMapWindow(dpy, XtWindow(toplevel)); goto READY; } if(!strcmp(cmd, "Raise")) { XRaiseWindow(dpy, XtWindow(toplevel)); goto READY; } if(!strcmp(cmd, "Lower")) { XLowerWindow(dpy, XtWindow(toplevel)); goto READY; } if(!strcmp(cmd, "Quit")) { quit_command = True; goto READY; } // unknown command XChangeProperty(dpy, XtWindow(widget), XA_XMRADIO_RESPONSE, XA_STRING, 8, PropModeReplace, "4", 1); goto DONE; READY: if(error) XChangeProperty(dpy, XtWindow(widget), XA_XMRADIO_RESPONSE, XA_STRING, 8, PropModeReplace, "5", 1); else XChangeProperty(dpy, XtWindow(widget), XA_XMRADIO_RESPONSE, XA_STRING, 8, PropModeReplace, "2", 1); DONE: if(prop) XFree(prop); } } void enterCursorCB(Widget widget, XtPointer clientData, XEvent* event, Boolean *continueToDispatch) { *continueToDispatch = True; UpdateMixerSlider(); } void station_buttonCB(Widget widget, XtPointer clientData, XtPointer callData) { SetFrequency((int)clientData); } void usage(char *progname) { printf(" xmradio, a Motif based tuner for radio cards.\n"); printf(" Version %s\n\n", APPVERSION); printf("Available options:\n\n"); printf(" -station - tell it a specific start station.\n"); printf(" -frequency - tell it a specific start frequency.\n"); printf(" -volume - tell it a specific start volume.\n"); printf(" -remote - execute commands in a running radio.\n\n"); printf(" where command is one of:\n"); printf(" StationUp - tune up one station\n"); printf(" StationDown - tune down one station\n"); printf(" StationSeekUp - seek up one station\n"); printf(" StationSeekDown - seek down one station\n"); printf(" Frequency= - tune to specified frequency\n"); printf(" Station= - tune to specified station\n"); printf(" AFC= - switch automatic frequency control on or off\n"); printf(" Stereo= - switch stereo on or off\n"); printf(" Mute= - mute audio on or off\n"); printf(" Balance= - set balance (value must be between and\n"); printf(" including -100 and +100\n"); printf(" Treble= - set treble (value must be between and\n"); printf(" including 0 and +100\n"); printf(" Bass= - set treble (value must be between and\n"); printf(" including 0 and +100\n"); printf(" Volume= - set treble (value must be between and\n"); printf(" including 0 and +100\n"); printf(" ShowAbout - toggle about box\n"); printf(" ShowAnalyzer - toggle analyzer\n"); printf(" Iconify - iconify window\n"); printf(" Deiconify - deiconify window\n"); printf(" Withdraw - withdraw window\n"); printf(" Raise - raise window\n"); printf(" Lower - lower window\n"); printf(" Quit - quit application\n\n"); printf(" You can add as many commands as you like in one line, say something like this:\n"); printf(" xmradio -remote Station=\"Delta Radio\" -remote Volume=30 -remote Stereo=off\n\n"); printf(" If you send xmradio a SIGUSR1 or SIGUSR2, it will seek or\n"); printf(" switch to the next/previous station depending on the\n"); printf(" configuration.\n"); } void NewInterface() { toplevel = XtVaAppCreateShell(APPNAME, APPCLASS, sessionShellWidgetClass, dpy, XtNallowShellResize, True, XtNtitle, APPTITLE, XtNiconPixmap, icon_pm, XtNiconMask, icon_pm_mask, XmNdeleteResponse, XmDO_NOTHING, NULL); XtAddEventHandler(toplevel, (EventMask)0, True, _XEditResCheckMessages, NULL); XtAddEventHandler(toplevel, EnterWindowMask, False, (XtEventHandler) enterCursorCB, (XtPointer)NULL); XtAddEventHandler(toplevel, PropertyChangeMask, False, (XtEventHandler) propertyCB, (XtPointer)NULL); if(wantLiteClue) liteClue = XtVaCreatePopupShell("lite_clue", xcgLiteClueWidgetClass, toplevel, XgcNwaitPeriod, 1000, XgcNcancelWaitPeriod, 1000, NULL); ParseStationList(); MakeMotifInterface(); AddTooltipsToWidgets(); #ifdef HAS_XPM SetSkin(); #endif GeneratePopupMenu(False); XtRealizeWidget(toplevel); } void CreateMenuWidget(char *widget_name, Widget parent) { moreW = XtVaCreateManagedWidget(widget_name, xmArrowButtonWidgetClass, parent, XmNarrowDirection, XmARROW_RIGHT, NULL); menuW = XmCreatePopupMenu(toplevel, "more_popup", (Arg*)NULL, 0); aboutW = XtVaCreateManagedWidget("about_button", xmPushButtonWidgetClass, menuW, NULL); XtAddCallback(aboutW, XmNactivateCallback, aboutCB, (XtPointer)NULL); analyzerW = XtVaCreateManagedWidget("analyzer_button", xmPushButtonWidgetClass, menuW, NULL); XtAddCallback(analyzerW, XmNactivateCallback, analyzerCB, (XtPointer)NULL); sampleW = XtVaCreateManagedWidget("sample_button", xmPushButtonWidgetClass, menuW, NULL); XtAddCallback(sampleW, XmNactivateCallback, sampleCB, (XtPointer)NULL); configW = XtVaCreateManagedWidget("config_button", xmPushButtonWidgetClass, menuW, NULL); XtAddCallback(configW, XmNactivateCallback, configCB, (XtPointer)NULL); versionW = XtVaCreateManagedWidget("version_check_button", xmPushButtonWidgetClass, menuW, NULL); XtAddCallback(versionW, XmNactivateCallback, versionCB, (XtPointer)NULL); if(useLcdProc) { lcdConnectW = XtVaCreateManagedWidget("lcd_connect_button", xmPushButtonWidgetClass, menuW, NULL); XtAddCallback(lcdConnectW, XmNactivateCallback, lcdConnectCB, (XtPointer)CONNECT); lcdDisconnectW = XtVaCreateManagedWidget("lcd_disconnect_button", xmPushButtonWidgetClass, menuW, NULL); XtAddCallback(lcdDisconnectW, XmNactivateCallback, lcdConnectCB, (XtPointer)DISCONNECT); } } void MakeMotifInterface() { Atom wmDeleteAtom; Widget sep1, sep2, sep3, sep4; wmDeleteAtom = XmInternAtom(dpy, "WM_DELETE_WINDOW", False); XmAddWMProtocolCallback(toplevel, wmDeleteAtom, quitCB, (XtPointer)NULL); stabtnW = NULL; nostationslabelW = NULL; stationPopupW = NULL; mainW = XtVaCreateManagedWidget("radio_main", xmFormWidgetClass, toplevel, NULL); if(gui == MINIMAL_GUI) { fieldstrengthW = XtVaCreateManagedWidget("fieldStrength", xmScrollBarWidgetClass, mainW, XmNeditable, False, XmNprocessingDirection, XmMAX_ON_TOP, XmNvalue, 0, XmNmaximum, 8, XmNminimum, 0, XmNvalue, 0, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 3, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); #ifdef JUHA_DRIVER indicatorW = create_radio_indicator("indicator", mainW); XtVaSetValues(indicatorW, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, fieldstrengthW, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); #endif freqFormW = XtVaCreateManagedWidget("freq_main", xmFormWidgetClass, mainW, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_WIDGET, #ifdef JUHA_DRIVER XmNrightWidget, indicatorW, #else XmNrightWidget, fieldstrengthW, #endif NULL); stationDownW = XtVaCreateManagedWidget("stationDown", xmArrowButtonWidgetClass, freqFormW, XmNarrowDirection, XmARROW_LEFT, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_POSITION, XmNrightPosition, 50, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); XtAddCallback(stationDownW, XmNactivateCallback, switchCB, (XtPointer)DOWN); stationUpW = XtVaCreateManagedWidget("stationUp", xmArrowButtonWidgetClass, freqFormW, XmNarrowDirection, XmARROW_RIGHT, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 3, XmNleftAttachment, XmATTACH_POSITION, XmNleftPosition, 50, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, stationDownW, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, stationDownW, NULL); XtAddCallback(stationUpW, XmNactivateCallback, switchCB, (XtPointer)UP); return; } buttonFormW = XtVaCreateManagedWidget("button_main", xmFormWidgetClass, mainW, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); quitW = XtVaCreateManagedWidget("quit_button", xmPushButtonWidgetClass, buttonFormW, XmNmarginWidth, 10, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 2, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 2, NULL); XtAddCallback(quitW, XmNactivateCallback, quitCB, (XtPointer)NULL); CreateMenuWidget("more", buttonFormW); XtVaSetValues(moreW, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 2, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, quitW, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, quitW, NULL); buttonSepW = XtVaCreateManagedWidget("button_separator", xmSeparatorWidgetClass, buttonFormW, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 3, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 3, XmNtopAttachment, XmATTACH_FORM, NULL); toggleFormW = XtVaCreateManagedWidget("toggle_main", xmFormWidgetClass, buttonFormW, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, moreW, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, quitW, XmNbottomAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, buttonSepW, XmNtopOffset, 3, NULL); afcW = XtVaCreateManagedWidget("afc", xmToggleButtonWidgetClass, toggleFormW, XmNalignment, XmALIGNMENT_BEGINNING, XmNset, False, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 3, XmNrightAttachment, XmATTACH_POSITION, XmNrightPosition, 33, NULL); XtAddCallback(afcW, XmNvalueChangedCallback, afcCB, (XtPointer)NULL); #ifdef JUHA_DRIVER XtSetSensitive(afcW, False); #endif stereoW = XtVaCreateManagedWidget("stereo", xmToggleButtonWidgetClass, toggleFormW, XmNlabelString, stereo_label_string, XmNrecomputeSize, False, XmNalignment, XmALIGNMENT_BEGINNING, XmNset, True, XmNleftAttachment, XmATTACH_POSITION, XmNleftPosition, 33, XmNrightAttachment, XmATTACH_POSITION, XmNrightPosition, 66, NULL); XtAddCallback(stereoW, XmNvalueChangedCallback, stereoCB, (XtPointer)NULL); muteW = XtVaCreateManagedWidget("mute", xmToggleButtonWidgetClass, toggleFormW, XmNalignment, XmALIGNMENT_BEGINNING, XmNset, False, XmNleftAttachment, XmATTACH_POSITION, XmNleftPosition, 66, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 3, NULL); XtAddCallback(muteW, XmNvalueChangedCallback, muteCB, (XtPointer)NULL); sliderFormW = XtVaCreateManagedWidget("slider_main", xmRowColumnWidgetClass, mainW, XmNorientation, XmVERTICAL, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, buttonFormW, NULL); freqFormW = XtVaCreateManagedWidget("freq_main", xmFormWidgetClass, sliderFormW, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, NULL); stationDownW = XtVaCreateManagedWidget("stationDown", xmArrowButtonWidgetClass, freqFormW, XmNarrowDirection, XmARROW_LEFT, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 0, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 3, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 3, NULL); XtAddCallback(stationDownW, XmNactivateCallback, switchCB, (XtPointer)DOWN); #ifdef LESSTIF_VERSION { Dimension x; XtVaGetValues(stationDownW, XmNheight, &x, NULL); #endif fieldstrengthW = XtVaCreateManagedWidget("fieldStrength", xmScrollBarWidgetClass, freqFormW, XmNeditable, False, XmNprocessingDirection, XmMAX_ON_TOP, #ifdef LESSTIF_VERSION XmNheight, x, #endif XmNmaximum, 8, XmNminimum, 0, XmNvalue, 0, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 3, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, stationDownW, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, stationDownW, NULL); #ifdef LESSTIF_VERSION } #endif #ifdef JUHA_DRIVER indicatorW = create_radio_indicator("indicator", freqFormW); XtVaSetValues(indicatorW, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, fieldstrengthW, XmNrightOffset, 1, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, stationDownW, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, stationDownW, NULL); #endif stationUpW = XtVaCreateManagedWidget("stationUp", xmArrowButtonWidgetClass, freqFormW, XmNarrowDirection, XmARROW_RIGHT, XmNrightAttachment, XmATTACH_WIDGET, #ifdef JUHA_DRIVER XmNrightWidget, indicatorW, #else XmNrightWidget, fieldstrengthW, #endif XmNrightOffset, 3, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, stationDownW, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, stationDownW, NULL); XtAddCallback(stationUpW, XmNactivateCallback, switchCB, (XtPointer)UP); freqDummyW = XtVaCreateManagedWidget("freqDummy", xmFormWidgetClass, freqFormW, XmNorientation, XmHORIZONTAL, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, stationUpW, XmNrightOffset, 3, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, stationDownW, XmNleftOffset, 3, XmNtopAttachment, XmATTACH_FORM, NULL); volW = XtVaCreateManagedWidget("volume", xmScaleWidgetClass, freqDummyW, XmNminimum, 0, XmNmaximum, 100, XmNorientation, XmHORIZONTAL, XmNrightAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); XtAddCallback(volW, XmNdragCallback, sliderCB, (XtPointer)VOLUME); XtAddCallback(volW, XmNvalueChangedCallback, sliderCB, (XtPointer)VOLUME); freqW = XtVaCreateWidget("frequency", xmScaleWidgetClass, freqDummyW, XmNminimum, MINFREQ, XmNmaximum, MAXFREQ, XmNscaleMultiple, FREQSTEP, XmNorientation, XmHORIZONTAL, XmNrightAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); XtAddCallback(freqW, XmNdragCallback, sliderCB, (XtPointer)FREQUENCY); XtAddCallback(freqW, XmNvalueChangedCallback, sliderCB, (XtPointer)FREQUENCY); MakeStationButtonWidgets(False); freqButtonW = XtVaCreateManagedWidget("freqButton", xmPushButtonWidgetClass, freqFormW, XmNlabelString, volume_label_string, XmNborderWidth, 0, XmNmarginHeight, 1, XmNmarginWidth, 1, XmNhighlightThickness, 1, XmNshadowThickness, 1, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftWidget, freqDummyW, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, freqDummyW, XmNtopOffset, 1, NULL); XtAddCallback(freqButtonW, XmNactivateCallback, freqButtonCB, (XtPointer)NULL); guiW = XtVaCreateManagedWidget("gui_expand", xmArrowButtonWidgetClass, freqFormW, XmNarrowDirection, XmARROW_DOWN, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, freqButtonW, XmNleftOffset, 2, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, freqButtonW, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, freqButtonW, NULL); XtAddCallback(guiW, XmNactivateCallback, guiExpandCB, (XtPointer)NULL); seekDownW = XtVaCreateManagedWidget("seekDown", xmArrowButtonWidgetClass, freqFormW, XmNarrowDirection, XmARROW_LEFT, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, guiW, XmNleftOffset, 2, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, freqButtonW, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, freqButtonW, NULL); XtAddCallback(seekDownW, XmNactivateCallback, seekCB, (XtPointer)DOWN); seekUpW = XtVaCreateManagedWidget("seekUp", xmArrowButtonWidgetClass, freqFormW, XmNarrowDirection, XmARROW_RIGHT, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, seekDownW, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, seekDownW, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, seekDownW, NULL); XtAddCallback(seekUpW, XmNactivateCallback, seekCB, (XtPointer)UP); freqValueW = XtVaCreateManagedWidget("freqValue", xmLabelWidgetClass, freqFormW, XmNalignment, XmALIGNMENT_END, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, seekUpW, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, stationUpW, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, freqButtonW, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, freqButtonW, NULL); soundFormW = XtVaCreateWidget("sound_main", xmFormWidgetClass, sliderFormW, XmNorientation, XmVERTICAL, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, volFormW, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); sep1 = XtVaCreateManagedWidget("separator1", xmSeparatorWidgetClass, soundFormW, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 3, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 3, XmNtopAttachment, XmATTACH_FORM, NULL); volumeLabelW = XtVaCreateManagedWidget("volumeLabel", xmLabelWidgetClass, soundFormW, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 3, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, sep1, NULL); volumeValueW = XtVaCreateManagedWidget("volumeValue", xmLabelWidgetClass, soundFormW, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 3, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, volumeLabelW, NULL); volumeW = XtVaCreateManagedWidget("volume_mixer", xmScaleWidgetClass, soundFormW, XmNminimum, 0, XmNmaximum, 100, XmNorientation, XmHORIZONTAL, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftWidget, volumeLabelW, XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET, XmNrightWidget, volumeValueW, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, volumeLabelW, NULL); XtAddCallback(volumeW, XmNdragCallback, sliderCB, (XtPointer)VOLUME); XtAddCallback(volumeW, XmNvalueChangedCallback, sliderCB, (XtPointer)VOLUME); sep2 = XtVaCreateManagedWidget("separator2", xmSeparatorWidgetClass, soundFormW, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 3, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 3, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, volumeW, NULL); balanceLabelW = XtVaCreateManagedWidget("balanceLabel", xmLabelWidgetClass, soundFormW, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 3, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, sep2, NULL); balanceValueW = XtVaCreateManagedWidget("balanceValue", xmLabelWidgetClass, soundFormW, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 3, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, balanceLabelW, NULL); balanceW = XtVaCreateManagedWidget("balance", xmScaleWidgetClass, soundFormW, XmNminimum, -100, XmNmaximum, 100, XmNvalue, 0, XmNorientation, XmHORIZONTAL, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftWidget, balanceLabelW, XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET, XmNrightWidget, balanceValueW, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, balanceLabelW, NULL); XtAddCallback(balanceW, XmNdragCallback, sliderCB, (XtPointer)BALANCE); XtAddCallback(balanceW, XmNvalueChangedCallback, sliderCB, (XtPointer)BALANCE); sep3 = XtVaCreateManagedWidget("separator3", xmSeparatorWidgetClass, soundFormW, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 3, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 3, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, balanceW, NULL); trebleLabelW = XtVaCreateManagedWidget("trebleLabel", xmLabelWidgetClass, soundFormW, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 3, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, sep3, NULL); trebleValueW = XtVaCreateManagedWidget("trebleValue", xmLabelWidgetClass, soundFormW, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 3, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, trebleLabelW, NULL); trebleW = XtVaCreateManagedWidget("treble", xmScaleWidgetClass, soundFormW, XmNminimum, 0, XmNmaximum, 100, XmNvalue, 0, XmNorientation, XmHORIZONTAL, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftWidget, trebleLabelW, XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET, XmNrightWidget, trebleValueW, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, trebleLabelW, NULL); XtAddCallback(trebleW, XmNdragCallback, sliderCB, (XtPointer)TREBLE); XtAddCallback(trebleW, XmNvalueChangedCallback, sliderCB, (XtPointer)TREBLE); sep4 = XtVaCreateManagedWidget("separator4", xmSeparatorWidgetClass, soundFormW, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 3, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 3, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, trebleW, NULL); bassLabelW = XtVaCreateManagedWidget("bassLabel", xmLabelWidgetClass, soundFormW, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 3, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, sep4, NULL); bassValueW = XtVaCreateManagedWidget("bassValue", xmLabelWidgetClass, soundFormW, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 3, XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, bassLabelW, NULL); bassW = XtVaCreateManagedWidget("bass", xmScaleWidgetClass, soundFormW, XmNminimum, 0, XmNmaximum, 100, XmNvalue, 0, XmNorientation, XmHORIZONTAL, XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, XmNleftWidget, bassLabelW, XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET, XmNrightWidget, bassValueW, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, bassLabelW, NULL); XtAddCallback(bassW, XmNdragCallback, sliderCB, (XtPointer)BASS); XtAddCallback(bassW, XmNvalueChangedCallback, sliderCB, (XtPointer)BASS); } void MakeStationButtonWidgets(int realize) { if(stabtnW) { XtDestroyWidget(stabtnW); stabtnW = NULL; } if(nostationslabelW) { XtDestroyWidget(nostationslabelW); nostationslabelW = NULL; } if(station_buttons.cnt) { int i, j; float width; XmString xstr; Widget w; char *name, *tmp; stabtnW = XtVaCreateWidget("station_button_form", xmFormWidgetClass, freqDummyW, XmNrightAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_FORM, NULL); width = 100./station_buttons.cnt; for(i=0; i 0.7) { tmp = strtok(NULL, ":"); if(!tmp) { printf("%s: wrong file format (line %d)\n", filepath, line); goto failure; } } n++; } } rewind(fp); station.name = (char**)calloc(n, sizeof(char*)); station.freq = (int*)calloc(n, sizeof(int)); station.def = (int*)calloc(n, sizeof(int)); station.cnt = 0; station_buttons.pos = (int*)calloc(n, sizeof(int)); station_buttons.cnt = 0; while(!feof(fp)) { if(fgets(buf, 512, fp) != NULL) { if((*buf == '#') || (*buf == '\n')) continue; sta = strtok(buf, ":"); if(!sta) { fprintf(stderr, "wrong file format for rc file \"%s\"\n", filepath); continue; } tmp = strtok(NULL, ":"); if(tmp) freq = (int) (100. * atof(tmp) + 0.5); else { fprintf(stderr, "wrong file format for rc file \"%s\"\n", filepath); continue; } tmp = strtok(NULL, ":"); if(tmp && !strcasecmp(tmp, DEFBTNTAG)) def = True; else def = False; if(version > 0.7) { tmp = strtok(NULL, ":"); if(tmp && !strcasecmp(tmp, DEFBTNTAG) && !startStation) startFreq = freq; } station.name[station.cnt] = strdup(sta); station.freq[station.cnt] = freq; station.def[station.cnt] = def; if(startFreq && !startStation) { startStation = station.name[station.cnt]; startStationPos = station.cnt; } station.cnt++; if(debug) printf("found \"%s\" on %d (%s) %s\n", sta, freq, def ? "def" : "no def", startFreq ? "start station" : ""); if(def) station_buttons.pos[station_buttons.cnt++] = station.cnt; startFreq = 0; } } fclose(fp); SortStationList(); return; failure: printf("using application defaults.\n"); ParseStationListAppDef(); ParseStationButtonListAppDef(); SortStationList(); return; } void ParseStationListAppDef() { int i, j, n, m; char *def_string, *string, *tmp, **stations; char *limiter = ")"; station.cnt = 0; def_string = XGetDefault(dpy, APPCLASS, "stationList"); if(!def_string) return; string = strdup(def_string); n = 0; j = strlen(string); for(i=0; i frequency) { SetFrequency(station.freq[i]); return; } } SetFrequency(station.freq[0]); } void station_down(Widget w, XEvent *event, String *params, Cardinal *paramscnt) { int i; if(!station.cnt) return; for(i = station.cnt-1; i >= 0; i--) { if(station.freq[i] < frequency) { SetFrequency(station.freq[i]); return; } } SetFrequency(station.freq[station.cnt-1]); } void station_seek_up(Widget w, XEvent *event, String *params, Cardinal *paramscnt) { SeekChannel(UP); } void station_seek_down(Widget w, XEvent *event, String *params, Cardinal *paramscnt) { SeekChannel(DOWN); } void more_popup(Widget w, XEvent *event, String *params, Cardinal *paramscnt) { Window w1, w2; int i1, i2, i3; XButtonEvent bEvent; if(!menuW) return; if(event->type == ButtonPress || event->type == ButtonRelease) { XmMenuPosition(menuW, &event->xbutton); } else { XQueryPointer(dpy, XtWindow(w), &w1, &w2, &bEvent.x_root, &bEvent.y_root, &i1, &i2, &i3); XmMenuPosition(menuW, &bEvent); } XtManageChild(menuW); } void station_popup(Widget w, XEvent *event, String *params, Cardinal *paramscnt) { Window w1, w2; int i1, i2, i3; XButtonEvent bEvent; if(!stationPopupW) return; if(event->type == ButtonPress || event->type == ButtonRelease) { XmMenuPosition(stationPopupW, &event->xbutton); } else { XQueryPointer(dpy, XtWindow(w), &w1, &w2, &bEvent.x_root, &bEvent.y_root, &i1, &i2, &i3); XmMenuPosition(stationPopupW, &bEvent); } XtManageChild(stationPopupW); } void GeneratePopupMenu(int realize) { int i; XmString xstr; Widget w; if(!station.cnt) return; if(stationPopupW) XtDestroyWidget(stationPopupW); stationPopupW = XmCreatePopupMenu(toplevel, "station_popup", (Arg*)NULL, 0); for(i = 0; i < station.cnt; i++) { xstr = XmStringCreateLtoR(station.name[i], XmSTRING_DEFAULT_CHARSET); w = XtVaCreateManagedWidget("station_button", xmPushButtonWidgetClass, stationPopupW, XmNlabelString, xstr, NULL); XtAddCallback(w, XmNactivateCallback, station_popupCB, (XtPointer)station.freq[i]); XmStringFree(xstr); } #ifdef HAS_XPM if(skin) SkinToWidgets(stationPopupW, skin); #endif if(realize) XtRealizeWidget(stationPopupW); } #ifdef HAS_XPM void SkinToWidgets(Widget w, Pixmap skin) { WidgetList wl; int i, x; if(XtIsComposite(w)) { XtVaGetValues(w, XtNnumChildren, &x, XtNchildren, &wl, NULL); for(i = 0; i < x; i++) SkinToWidgets(wl[i], skin); } XtVaSetValues(w, XmNbackgroundPixmap, skin, NULL); if(XmIsPushButton(w)) XtVaSetValues(w, XmNarmPixmap, skin, NULL); return; } void SetSkin() { int err; char *file = XGetDefault(dpy, APPCLASS, "skinPixmap"); if(!file) return; if((err = XpmReadFileToPixmap(dpy, DefaultRootWindow(dpy), file, &skin, NULL, NULL)) < XpmSuccess) { skin = 0; fprintf(stderr, "couldnt create pixmap: %s (%s)\n", XpmGetErrorString(err), file); fprintf(stderr, "no skin available.\n"); return; } /* parse all widgets and set pixmap */ SkinToWidgets(toplevel, skin); SkinToWidgets(menuW, skin); } #endif int main(int argc, char** argv) { char *tmp; int startFreq=0; int startFreqCmdLine=0; int startVolume=0; int i; char **remote_commands = NULL; int remote_command_count = 0; int remote_command_size = 0; static XtActionsRec actions[] = { { "MorePopup", more_popup }, { "StationPopup", station_popup }, { "StationUp", station_up }, { "StationDown", station_down }, { "StationSeekUp", station_seek_up }, { "StationSeekDown", station_seek_down }, { "ConfScrollUp", conf_scroll_up }, { "ConfScrollDown", conf_scroll_down } }; quit_command = False; stereo = True; seeking = False; afc = False; initHack = False; useLcdProc = False; connectToLCDOnStartup = False; wantLiteClue = True; startStation = NULL; startStationPos = 0; sockfd = 0; XtToolkitInitialize(); app_con = XtCreateApplicationContext(); XtAppAddActions(app_con, (XtActionsRec *) actions, XtNumber(actions)); XtAppSetFallbackResources(app_con, fbres); dpy = XtOpenDisplay(app_con, NULL, APPNAME, APPCLASS, NULL, 0, &argc, argv); if(!dpy) { fprintf(stderr, "can't open display, exiting...\n"); usage(argv[0]); exit(EXIT_FAILURE); } XSetErrorHandler(HandleXError); tmp = XGetDefault(dpy, APPCLASS, "debug"); if(tmp && !strcasecmp("true", tmp)) debug = True; else debug = False; initAtoms(); for(i = 1; i < argc; i++) { if(!strcasecmp(argv[i], "-h") || !strcasecmp(argv[i], "-help")) { usage(argv[0]); exit(EXIT_SUCCESS); } if(!strcasecmp(argv[i], "-volume")) { i++; if(!argv[i] || *argv[i] == '-' || *argv[i] == 0) { fprintf(stderr, "%s: invalid `-volume' option \"%s\"\n", argv[0], argv[i] ? argv[i] : ""); usage(argv[0]); exit(EXIT_FAILURE); } startVolume = atoi(argv[i]); } if(!strcasecmp(argv[i], "-frequency")) { i++; if(!argv[i] || *argv[i] == '-' || *argv[i] == 0) { fprintf(stderr, "%s: invalid `-frequency' option \"%s\"\n", argv[0], argv[i] ? argv[i] : ""); usage(argv[0]); exit(EXIT_FAILURE); } startFreq = (int) (100. * atof(argv[i]) + 0.5); startFreqCmdLine = 1; } if(!strcasecmp(argv[i], "-station")) { i++; if(!argv[i] || *argv[i] == '-' || *argv[i] == 0) { fprintf(stderr, "%s: invalid `-station' option \"%s\"\n", argv[0], argv[i] ? argv[i] : ""); usage(argv[0]); exit(EXIT_FAILURE); } } if(!strcasecmp(argv[i], "-remote")) { if(remote_command_count == remote_command_size) { remote_command_size += 20; remote_commands = remote_commands ? realloc(remote_commands, remote_command_size * sizeof (char*)) : calloc(remote_command_size, sizeof (char*)); } i++; if(!argv[i] || *argv[i] == '-' || *argv[i] == 0) { fprintf(stderr, "%s: invalid `-remote' option \"%s\"\n", argv[0], argv[i] ? argv[i] : ""); usage(argv[0]); exit(EXIT_FAILURE); } remote_commands[remote_command_count++] = argv[i]; } } if(remote_command_count) { sendCommands(remote_commands); free(remote_commands); exit(EXIT_SUCCESS); } #ifdef HAS_XPM if(XpmCreatePixmapFromData(dpy, DefaultRootWindow(dpy), icon_xpm, &icon_pm, NULL, NULL) < XpmSuccess) #endif /* HAS_XPM */ icon_pm = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), icon_bits, icon_width, icon_height); icon_pm_mask = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), icon_mask_bits, icon_mask_width, icon_mask_height); gui = NORMAL_GUI; tmp = XGetDefault(dpy, APPCLASS, "gui"); if(tmp) { if(!strcasecmp("minimal", tmp)) gui = MINIMAL_GUI; } slider_mode = VOLUME_MODE; tmp = XGetDefault(dpy, APPCLASS, "startMode"); if(tmp) { if(!strcasecmp("frequency", tmp)) slider_mode = FREQ_MODE; if(!strcasecmp("stations", tmp)) slider_mode = STATION_MODE; } tmp = XGetDefault(dpy, APPCLASS, "volume.labelString"); if(tmp) volume_label_string = XmStringCreateLtoR(tmp, XmSTRING_DEFAULT_CHARSET); else volume_label_string = XmStringCreateLtoR("Volume", XmSTRING_DEFAULT_CHARSET); tmp = XGetDefault(dpy, APPCLASS, "frequency.labelString"); if(tmp) frequency_label_string = XmStringCreateLtoR(tmp, XmSTRING_DEFAULT_CHARSET); else frequency_label_string = XmStringCreateLtoR("Frequency", XmSTRING_DEFAULT_CHARSET); tmp = XGetDefault(dpy, APPCLASS, "stations.labelString"); if(tmp) stations_label_string = XmStringCreateLtoR(tmp, XmSTRING_DEFAULT_CHARSET); else stations_label_string = XmStringCreateLtoR("Stations", XmSTRING_DEFAULT_CHARSET); tmp = XGetDefault(dpy, APPCLASS, "def.labelString"); if(tmp) def_label_string = XmStringCreateLtoR(tmp, XmSTRING_DEFAULT_CHARSET); else def_label_string = XmStringCreateLtoR("define", XmSTRING_DEFAULT_CHARSET); tmp = XGetDefault(dpy, APPCLASS, "stereo.labelString"); if(tmp) stereo_label_string = XmStringCreateLtoR(tmp, XmSTRING_DEFAULT_CHARSET); else stereo_label_string = XmStringCreateLtoR("stereo", XmSTRING_DEFAULT_CHARSET); tmp = XGetDefault(dpy, APPCLASS, "mono.labelString"); if(tmp) mono_label_string = XmStringCreateLtoR(tmp, XmSTRING_DEFAULT_CHARSET); else mono_label_string = XmStringCreateLtoR("mono", XmSTRING_DEFAULT_CHARSET); tmp = XGetDefault(dpy, APPCLASS, "useLiteClue"); if(tmp && !strcasecmp("false", tmp)) wantLiteClue = False; tmp = XGetDefault(dpy, APPCLASS, "useInitHack"); if(tmp && !strcasecmp("true", tmp)) initHack = True; tmp = XGetDefault(dpy, APPCLASS, "useLcdProc"); if(tmp && !strcasecmp("true", tmp)) useLcdProc = True; tmp = XGetDefault(dpy, APPCLASS, "connectToLCDOnStartup"); if(tmp && !strcasecmp("true", tmp)) connectToLCDOnStartup = True; NewInterface(); if(!startFreq) { tmp = XGetDefault(dpy, APPCLASS, "startFrequency"); if(tmp) startFreq = (int) (100. * atof(tmp) + 0.5); else startFreq = MINFREQ; } TUNER_DEVICE = XGetDefault(dpy, APPCLASS, "tunerDevice"); if(!TUNER_DEVICE) fprintf(stderr, "no tuner device specified!?!\n"); MIXER_DEVICE = XGetDefault(dpy, APPCLASS, "mixerDevice"); if(!MIXER_DEVICE) fprintf(stderr, "no mixer device specified!?!\n"); DSP_DEVICE = XGetDefault(dpy, APPCLASS, "dspDevice"); if(!DSP_DEVICE) fprintf(stderr, "no dsp device specified!?!\n"); tmp = XGetDefault(dpy, APPCLASS, "channelSet"); if(tmp) chnlset = atoi(tmp); if(chnlsetCHNLSET_MAX) { fprintf(stderr, "wrong channels set. using default for weurope.\n"); chnlset = CHNLSET_WEUROPE; } if(!startFreqCmdLine && !startStation) { tmp = XGetDefault(dpy, APPCLASS, "startStation"); if(tmp) startStation = tmp; } if(startStation) { int found = False; for(i = 0; i < station.cnt; i++) if(!strcmp(startStation, station.name[i])) { startStationPos = i; startFreq = station.freq[i]; found = True; } if(!found) { fprintf(stderr, "Couldn't find starting station, using default one.\n"); if(station.cnt) startFreq = station.freq[0]; else startFreq = MINFREQ; } } if(debug) { printf("using startfreq : %d\n", startFreq); printf("using tuner : %s\n", TUNER_DEVICE); printf("using mixer : %s\n", MIXER_DEVICE); printf("using dsp : %s\n", DSP_DEVICE); printf("using channelset: %d\n", chnlset); } if(InitTuner(startFreq) == False) fprintf(stderr, "FAILED TO INIT TUNER!\n"); InitMixer(); if(startVolume) SetVolume(startVolume); gui = TINY_GUI; gc = XCreateGC(dpy, DefaultRootWindow(dpy), 0, NULL); XtVaGetValues(fieldstrengthW, XmNforeground, &fg, NULL); XSetForeground(dpy, gc, fg); workingCursor = XCreateFontCursor(dpy, XC_watch); if(mixer == -1) { XtSetSensitive(volumeW, False); XtSetSensitive(volW, False); XtSetSensitive(balanceW, False); XtSetSensitive(trebleW, False); XtSetSensitive(bassW, False); XtSetSensitive(analyzerW,False); XtSetSensitive(sampleW, False); } if(stereo == False) XtSetSensitive(balanceW, False); if(slider_mode == FREQ_MODE) { slider_mode = VOLUME_MODE; XtCallCallbacks(freqButtonW, XmNactivateCallback, (XtPointer)NULL); } if(slider_mode == STATION_MODE) { slider_mode = VOLUME_MODE; XtCallCallbacks(freqButtonW, XmNactivateCallback, (XtPointer)NULL); XtCallCallbacks(freqButtonW, XmNactivateCallback, (XtPointer)NULL); } tmp = XGetDefault(dpy, APPCLASS, "signalReaction"); if(tmp) { if(!strcmp(tmp, "seek")) { signal(SIGUSR1, (void*) station_seek_up); signal(SIGUSR2, (void*) station_seek_down); } else if(!strcmp(tmp, "switch")) { signal(SIGUSR1, (void*) station_up); signal(SIGUSR2, (void*) station_down); } else { fprintf(stderr, "wrong argument for \"signalReaction\" in app-def file!\n"); signal(SIGUSR1, signal_error); signal(SIGUSR2, signal_error); } } XChangeProperty(dpy, XtWindow(toplevel), XA_XMRADIO_VERSION, XA_STRING, 8, PropModeReplace, APPVERSION, strlen(APPVERSION)); if(useLcdProc && connectToLCDOnStartup) XtCallCallbacks(lcdConnectW, XmNactivateCallback, (XtPointer)CONNECT); else lcdDisconnectCB(NULL); XtAppAddTimeOut(app_con, 250, UpdateStatus, NULL); XtAppMainLoop(app_con); return(True); } #ifdef JUHA_DRIVER static void update_radio_indicator(Widget w) { Window win = XtWindow(w); static GC gc; Colormap cmap; static XColor red, green, blue, black, white; Dimension width, height; Dimension shadow, marginw, marginh; int x, y; int v; struct tuner_status st; static struct tuner_status ost; if(!XtIsRealized(w)) return; v = ioctl(tuner, TUNER_GETSTATUS, &st); if(v) st.lock = 0; if(ost.lock == st.lock && ost.stereo == st.stereo && ost.afc == st.afc && ost.rssi == st.rssi) return; ost = st; XtVaGetValues(w, XtNwidth, &width, XtNheight, &height, XtNcolormap, &cmap, XmNshadowThickness, &shadow, XmNmarginWidth, &marginw, XmNmarginHeight, &marginh, NULL); x = shadow + marginw + 1; y = shadow + marginh + 1; width -= 2 * x; height -= 2 * y; if(!gc) { red.red = ~0; green.green = ~0; blue.blue = ~0; XAllocColor(dpy, cmap, &red); XAllocColor(dpy, cmap, &green); XAllocColor(dpy, cmap, &blue); gc = XCreateGC(dpy, XtWindow(w), 0, NULL); black.pixel = BlackPixelOfScreen(XtScreen(w)); white.pixel = WhitePixelOfScreen(XtScreen(w)); } v = st.rssi * height / 255; if(!st.lock || v != height) { XSetForeground(dpy, gc, st.lock ? black.pixel : red.pixel); XFillRectangle(dpy, win, gc, x, y, width, height); } if(st.lock) { if(v) { XSetForeground(dpy, gc, st.stereo ? green.pixel : blue.pixel); XFillRectangle(dpy, win, gc, x, y + height - v, width, v); } v = st.afc * (width - 2) / 255; XSetForeground(dpy, gc, v ? red.pixel : white.pixel); XFillRectangle(dpy, win, gc, x + width / 2 - 1 + v, y, 2, height); } } static Widget create_radio_indicator(char *name, Widget parent) { Widget w; w = XtVaCreateWidget(name, xmDrawnButtonWidgetClass, parent, XmNshadowType, XmSHADOW_IN, XmNwidth, 24, NULL); XtManageChild(w); update_radio_indicator(w); return (w); } #endif /* JUHA_DRIVER */