dockapps/wmmixer/wmmixer.cc
2017-08-29 09:02:43 +02:00

593 lines
16 KiB
C++

// wmmixer.cc - A mixer designed for WindowMaker
//
// Release 1.5
// Copyright (C) 1998 Sam Hawker <shawkie@geocities.com>
// Copyright (C) 2002 Gordon Fraser <gordon@debian.org>
// Patch added by Rodolfo (kix) Garcia <kix@kix.es> 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; count<mixctl_->getNrDevices(); 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; i<argc; i++)
{
if(strcmp("-position", argv[i]) == 0)
{
sprintf(argv[i], "%s", "-g");
}
else if(strcmp("-help", argv[i]) == 0)
{
sprintf(argv[i], "%s", "-h");
}
else if(strcmp("-display", argv[i]) == 0)
{
sprintf(argv[i], "%s", "-d");
}
}
while ((i = getopt_long(argc, argv, "hvd:g:wasl:L:b:m:c:x:r:", long_opts, &opt_index)) != -1)
{
switch (i)
{
case 'h':
case ':':
case '?':
displayUsage(argv[0]);
break;
case 'v':
displayVersion();
break;
case 'd':
xhandler_->setDisplay(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", &current);
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", &current);
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 <string> 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 <string> use the specified color for led display" << std::endl;
std::cout << " -L, --led-highcolor <string> use the specified color for led shading" << std::endl;
std::cout << " -b, --back-color <string> 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 <string> use specified class (rather than WMMmixer)" << std::endl;
std::cout << " -r, --scrollwheel <number> 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 " PACKAGE_VERSION << 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();
}