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