// wmmixer.cc - A mixer designed for WindowMaker // // Release 1.5 // Copyright (C) 1998 Sam Hawker // Copyright (C) 2002 Gordon Fraser // Patch added by Rodolfo (kix) Garcia to select the config file // This software comes with ABSOLUTELY NO WARRANTY // This software is free software, and you are welcome to redistribute it // under certain conditions // See the COPYING file for details. #include "wmmixer.h" //-------------------------------------------------------------------- WMMixer::WMMixer() { // Initialize member variables current_channel_ = 0; num_channels_ = 0; current_channel_left_ = 0; current_channel_right_ = 0; repeat_timer_ = 0; wheel_scroll_ = 2; current_recording_ = false; current_show_recording_ = false; dragging_ = false; strcpy(mixer_device_, MIXERDEV); xhandler_ = new XHandler(); } //-------------------------------------------------------------------- WMMixer::~WMMixer() { delete[] channel_list_; delete mixctl_; delete xhandler_; } //-------------------------------------------------------------------- void WMMixer::loop() { XEvent xev; bool done=false; while(!done) { while(XPending(xhandler_->getDisplay())) { XNextEvent(xhandler_->getDisplay(), &xev); switch(xev.type) { case Expose: xhandler_->repaint(); break; case ButtonPress: pressEvent(&xev.xbutton); break; case ButtonRelease: releaseEvent(&xev.xbutton); break; case MotionNotify: motionEvent(&xev.xmotion); break; case ClientMessage: if(xev.xclient.data.l[0] == (int)xhandler_->getDeleteWin()) done=true; break; } } // keep a button pressed causes scrolling throught the channels if(xhandler_->getButtonState() & (BTNPREV | BTNNEXT)) { repeat_timer_++; if(repeat_timer_ >= RPTINTERVAL) { if(xhandler_->getButtonState() & BTNNEXT) { current_channel_++; if(current_channel_ >= num_channels_) current_channel_ = 0; } else { if(current_channel_ < 1) current_channel_ = num_channels_-1; else current_channel_--; } checkVol(true); repeat_timer_ = 0; } } else { checkVol(false); } XFlush(xhandler_->getDisplay()); usleep(100000); } } //-------------------------------------------------------------------- void WMMixer::init(int argc, char **argv) { parseArgs(argc, argv); initMixer(); readConfigurationFile(); xhandler_->init(argc, argv, mixctl_->getNrDevices()); if(num_channels_ == 0) { std::cerr << NAME << " : Sorry, no supported channels found." << std::endl; } else { checkVol(true); } } //-------------------------------------------------------------------- void WMMixer::initMixer() { // Initialize Mixer try { mixctl_ = new MixCtl(mixer_device_); } catch(MixerDeviceException &exc) { std::cerr << NAME << " : " << exc.getErrorMessage() << "'." << std::endl; exit(1); } channel_list_ = new unsigned[mixctl_->getNrDevices()]; for(unsigned count=0; countgetNrDevices(); count++) { if(mixctl_->getSupport(count)){ channel_list_[num_channels_]=count; num_channels_++; } } } //-------------------------------------------------------------------- void WMMixer::checkVol(bool forced = true) { if(!forced && !mixctl_->hasChanged()) return; if(mixctl_->isMuted(channel_list_[current_channel_])) xhandler_->setButtonState(xhandler_->getButtonState() | BTNMUTE); else xhandler_->setButtonState(xhandler_->getButtonState() & ~BTNMUTE); mixctl_->readVol(channel_list_[current_channel_], true); unsigned nl = mixctl_->readLeft(channel_list_[current_channel_]); unsigned nr = mixctl_->readRight(channel_list_[current_channel_]); bool nrec = mixctl_->readRec(channel_list_[current_channel_], true); if(forced) { current_channel_left_ = nl; current_channel_right_ = nr; current_recording_ = nrec; if(nrec) xhandler_->setButtonState(xhandler_->getButtonState() | BTNREC); else xhandler_->setButtonState(xhandler_->getButtonState() & ~BTNREC); current_show_recording_=mixctl_->getRecords(channel_list_[current_channel_]); updateDisplay(); } else { if(nl != current_channel_left_ || nr != current_channel_right_ || nrec != current_recording_) { if(nl!=current_channel_left_) { current_channel_left_=nl; if(mixctl_->getStereo(channel_list_[current_channel_])) xhandler_->drawLeft(current_channel_left_); else xhandler_->drawMono(current_channel_left_); } if(nr!=current_channel_right_) { current_channel_right_=nr; if(mixctl_->getStereo(channel_list_[current_channel_])) xhandler_->drawRight(current_channel_right_); else xhandler_->drawMono(current_channel_left_); } if(nrec!=current_recording_) { current_recording_=nrec; if(nrec) xhandler_->setButtonState(xhandler_->getButtonState() | BTNREC); else xhandler_->setButtonState(xhandler_->getButtonState() & ~BTNREC); xhandler_->drawBtns(BTNREC, current_show_recording_); } updateDisplay(); } } } //-------------------------------------------------------------------- void WMMixer::parseArgs(int argc, char **argv) { static struct option long_opts[] = { {"help", 0, NULL, 'h'}, {"version", 0, NULL, 'v'}, {"display", 1, NULL, 'd'}, {"geometry", 1, NULL, 'g'}, {"withdrawn", 0, NULL, 'w'}, {"afterstep", 0, NULL, 'a'}, {"shaped", 0, NULL, 's'}, {"led-color", 1, NULL, 'l'}, {"led-highcolor", 1, NULL, 'L'}, {"back-color", 1, NULL, 'b'}, {"mix-device", 1, NULL, 'm'}, {"config-file", 1, NULL, 'c'}, {"x-class", 1, NULL, 'x'}, {"scrollwheel",1, NULL, 'r'}, {NULL, 0, NULL, 0 }}; int i, opt_index = 0; // init the config file name snprintf(config_file_, CONFIGFILELEN -1, "%s/.wmmixer", getenv("HOME")); // For backward compatibility for(i=1; isetDisplay(optarg); break; case 'g': xhandler_->setPosition(optarg); break; case 'w': xhandler_->setWindowMaker(); break; case 'a': xhandler_->setAfterStep(); break; case 's': xhandler_->setUnshaped(); break; case 'l': xhandler_->setLedColor(optarg); break; case 'L': xhandler_->setLedHighColor(optarg); break; case 'b': xhandler_->setBackColor(optarg); break; case 'm': sprintf(mixer_device_, "%s", optarg); break; case 'c': snprintf(config_file_, CONFIGFILELEN -1, "%s", optarg); break; case 'x': xhandler_->setWindowClass(optarg); break; case 'r': if(atoi(optarg)>0) wheel_scroll_ = atoi(optarg); break; } } } //-------------------------------------------------------------------- void WMMixer::readConfigurationFile() { FILE *rcfile; char buf[256]; int done; // int current=-1; unsigned current = mixctl_->getNrDevices() + 1; if((rcfile=fopen(config_file_, "r"))!=NULL) { num_channels_=0; do { fgets(buf, 250, rcfile); if((done=feof(rcfile))==0) { buf[strlen(buf)-1]=0; if(strncmp(buf, "addchannel ", strlen("addchannel "))==0) { sscanf(buf, "addchannel %i", ¤t); if(current >= mixctl_->getNrDevices() || mixctl_->getSupport(current) == false) { fprintf(stderr,"%s : Sorry, this channel (%i) is not supported.\n", NAME, current); current = mixctl_->getNrDevices() + 1; } else { channel_list_[num_channels_] = current; num_channels_++; } } if(strncmp(buf, "setchannel ", strlen("setchannel "))==0) { sscanf(buf, "setchannel %i", ¤t); if(current >= mixctl_->getNrDevices() || mixctl_->getSupport(current)==false) { fprintf(stderr,"%s : Sorry, this channel (%i) is not supported.\n", NAME, current); current = mixctl_->getNrDevices() + 1; } } if(strncmp(buf, "setmono ", strlen("setmono "))==0) { if(current== mixctl_->getNrDevices() + 1) fprintf(stderr,"%s : Sorry, no current channel.\n", NAME); else{ int value; sscanf(buf, "setmono %i", &value); mixctl_->setLeft(current, value); mixctl_->setRight(current, value); mixctl_->writeVol(current); } } if(strncmp(buf, "setleft ", strlen("setleft "))==0) { if(current== mixctl_->getNrDevices() + 1) fprintf(stderr, "%s : Sorry, no current channel.\n", NAME); else{ int value; sscanf(buf, "setleft %i", &value); mixctl_->setLeft(current, value); mixctl_->writeVol(current); } } if(strncmp(buf, "setright ", strlen("setright "))==0) { if(current== mixctl_->getNrDevices() + 1) fprintf(stderr, "%s : Sorry, no current channel.\n", NAME); else { int value; sscanf(buf, "setleft %i", &value); mixctl_->setRight(current, value); mixctl_->writeVol(current); } } if(strncmp(buf, "setrecsrc ", strlen("setrecsrc "))==0) { if(current== mixctl_->getNrDevices() + 1) fprintf(stderr, "%s : Sorry, no current channel.\n", NAME); else mixctl_->setRec(current, (strncmp(buf+strlen("setrecsrc "), "true", strlen("true"))==0)); } } } while(done==0); fclose(rcfile); mixctl_->writeRec(); } } //-------------------------------------------------------------------- void WMMixer::displayUsage(const char* name) { std::cout << "Usage: " << name << "[options]" << std::endl; std::cout << " -h, --help display this help screen" << std::endl; std::cout << " -v, --version display program version" << std::endl; std::cout << " -d, --display display to use (see X manual pages)" << std::endl; std::cout << " -g, --geometry +XPOS+YPOS geometry to use (see X manual pages)" << std::endl; std::cout << " -w, --withdrawn run the application in withdrawn mode" << std::endl; std::cout << " (for WindowMaker, etc)" << std::endl; std::cout << " -a, --afterstep use smaller window (for AfterStep Wharf)" << std::endl; std::cout << " -s, --shaped shaped window" << std::endl; std::cout << " -l, --led-color use the specified color for led display" << std::endl; std::cout << " -L, --led-highcolor use the specified color for led shading" << std::endl; std::cout << " -b, --back-color use the specified color for backgrounds" << std::endl; std::cout << " -m, --mix-device use specified device (rather than /dev/mixer)" << std::endl; std::cout << " -c, --config-file use specified config file (rather than $HOME/.wmmixer)" << std::endl; std::cout << " -x, --x-class use specified class (rather than WMMmixer)" << std::endl; std::cout << " -r, --scrollwheel volume increase/decrease with mouse wheel (default: 2)" << std::endl; std::cout << "\nFor backward compatibility the following obsolete options are still supported:" << std::endl; std::cout << " -help display this help screen" << std::endl; std::cout << " -position geometry to use (see X manual pages)" << std::endl; std::cout << " -display display to use (see X manual pages)" << std::endl; exit(0); } //-------------------------------------------------------------------- void WMMixer::displayVersion() { std::cout << "wmmixer version 1.5" << std::endl; exit(0); } //-------------------------------------------------------------------- void WMMixer::pressEvent(XButtonEvent *xev) { bool forced_update = true; int x = xev->x-(xhandler_->getWindowSize()/2-32); int y = xev->y-(xhandler_->getWindowSize()/2-32); if(xhandler_->isLeftButton(x, y)) { if(current_channel_ < 1) current_channel_=num_channels_-1; else current_channel_--; xhandler_->setButtonState(xhandler_->getButtonState() | BTNPREV); repeat_timer_ = 0; xhandler_->drawBtns(BTNPREV, current_show_recording_); } if(xhandler_->isRightButton(x, y)) { current_channel_++; if(current_channel_ >= num_channels_) current_channel_=0; xhandler_->setButtonState(xhandler_->getButtonState() | BTNNEXT); repeat_timer_ = 0; xhandler_->drawBtns(BTNNEXT, current_show_recording_); } // Volume settings if(xhandler_->isVolumeBar(x, y)) { int vl = 0, vr = 0; if(xev->button < 4) { vl = ((60-y)*100)/(2*25); vr = vl; dragging_ = true; } else if(xev->button == 4) { vr = mixctl_->readRight(channel_list_[current_channel_]) + wheel_scroll_; vl = mixctl_->readLeft(channel_list_[current_channel_]) + wheel_scroll_; } else if(xev->button == 5) { vr = mixctl_->readRight(channel_list_[current_channel_]) - wheel_scroll_; vl = mixctl_->readLeft(channel_list_[current_channel_]) - wheel_scroll_; } if(vl <= 0) vl = 0; if(vr <= 0) vr = 0; if(x <= 50) mixctl_->setLeft(channel_list_[current_channel_], vl); if(x >= 45) mixctl_->setRight(channel_list_[current_channel_], vr); mixctl_->writeVol(channel_list_[current_channel_]); forced_update = false; } // Toggle record if(xhandler_->isRecButton(x, y)) { mixctl_->setRec(channel_list_[current_channel_], !mixctl_->readRec(channel_list_[current_channel_], false)); mixctl_->writeRec(); forced_update = false; } // Toggle mute if(xhandler_->isMuteButton(x, y)) { if(mixctl_->isMuted(channel_list_[current_channel_])) { xhandler_->setButtonState(xhandler_->getButtonState() & ~BTNMUTE); mixctl_->unmute(channel_list_[current_channel_]); } else { mixctl_->mute(channel_list_[current_channel_]); xhandler_->setButtonState(xhandler_->getButtonState() | BTNMUTE); } xhandler_->drawBtns(BTNMUTE, current_show_recording_); } // Update volume display checkVol(forced_update); } //-------------------------------------------------------------------- void WMMixer::releaseEvent(XButtonEvent *xev) { dragging_ = false; xhandler_->setButtonState(xhandler_->getButtonState() & ~(BTNPREV | BTNNEXT)); xhandler_->drawBtns(BTNPREV | BTNNEXT, current_show_recording_); xhandler_->repaint(); } //-------------------------------------------------------------------- void WMMixer::motionEvent(XMotionEvent *xev) { int x=xev->x-(xhandler_->getWindowSize()/2-32); int y=xev->y-(xhandler_->getWindowSize()/2-32); // if(x>=37 && x<=56 && y>=8 && dragging_){ if(xhandler_->isVolumeBar(x, y) && dragging_){ int v=((60-y)*100)/(2*25); if(v<0) v=0; if(x<=50) mixctl_->setLeft(channel_list_[current_channel_], v); if(x>=45) mixctl_->setRight(channel_list_[current_channel_], v); mixctl_->writeVol(channel_list_[current_channel_]); checkVol(false); } } //-------------------------------------------------------------------- void WMMixer::updateDisplay() { xhandler_->update(channel_list_[current_channel_]); if(mixctl_->getStereo(channel_list_[current_channel_])) { xhandler_->drawLeft(current_channel_left_); xhandler_->drawRight(current_channel_right_); } else { xhandler_->drawMono(current_channel_right_); } xhandler_->drawBtns(BTNREC | BTNNEXT | BTNPREV | BTNMUTE, current_show_recording_); xhandler_->repaint(); } //==================================================================== int main(int argc, char** argv) { WMMixer mixer = WMMixer(); mixer.init(argc, argv); mixer.loop(); }