OK. Here it is if anyone is interested in taking a look.
> You could attach the code to an email.
>
> On Sun, Dec 7, 2008 at 8:58 AM, David Baron <[EMAIL PROTECTED]> wrote:
> > On Sunday 07 December 2008 15:04:34 Sebastian Kügler wrote:
> >> On Sunday 07 December 2008 13:08:49 David Baron wrote:
> >> > I am building an applet to control a speakerphone modem. I have
> >> > encountered the following problems:
> >>
> >> Knowing your version of KDE would be useful. Also, maybe you can post
> >> code so people can point out things that might be wrong.
> >>
> >> And welcome to Plasma :-)
> >
> > I am using kde4.1.3 from debian experimental and backports.
> > While the code is not voluminous, it is longer than appropriate for such
> > a posting. If the list permits inclusions, I can do so.
> >
> > Basically, the paint method places an image or alternating by a timer,
> > two images based on program state. A "daemon" thread, code lifted from
> > xringd, monitors the modem for dial. Clicking the applet can answer the
> > phone, hang up when talking or put up a widget from which one can dial a
> > call.
> >
> > All of the io which can be time-disrupting is in threads so the UI should
> > remain responsive and even.
> >
> > Nothing very complicated once one can figures out how to read the modem
> > effectively.
> > _______________________________________________
> > Plasma-devel mailing list
> > [email protected]
> > https://mail.kde.org/mailman/listinfo/plasma-devel
>
> _______________________________________________
> Plasma-devel mailing list
> [email protected]
> https://mail.kde.org/mailman/listinfo/plasma-devel
/* phoneapplet.cpp */
#include <QPainter>
#include <QFontMetrics>
#include <QSize>
#include <QtCore>
#include <KIcon>
#include <plasma/svg.h>
#include <plasma/theme.h>
// includes for linux std c++, ioctl, etc
#include <errno.h>
#include <stdio.h>
#include <sys/file.h>
#include <stdlib.h>
#include <unistd.h>
#include <paths.h>
#include <fcntl.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <signal.h>
#include <termios.h>
#include <getopt.h>
#include <syslog.h>
#include <pwd.h>
#include <ctype.h>
#include <linux/version.h>
#include <linux/serial.h>
#include <printf.h>
#include "phoneapplet.h"
#include "ui_phonecall.h"
static char* new_string(char*, const char*);
static bool fork_cmd( char* cmd );
static int modem_talk(int, const char*, const char*);
static int modem_volume( int, int );
static int modem_dial(int, const char*);
class RingDaemonThread : public QThread {
public:
RingDaemonThread( PhoneApplet* parent = 0 ) { m_Parent = parent; m_iRet = 0; }
~RingDaemonThread() {}
void run() { m_iRet = m_Parent->ring_daemon(); }
void pause( long ms , QThread* qt ) { qt->wait( ms ); }
void pause( QThread* qt ) { qt->wait(); }
bool result() { return isRunning(); }
int error() { return m_iRet; }
private:
PhoneApplet* m_Parent;
int m_iRet;
};
class ModemAnswerThread : public QThread {
public:
ModemAnswerThread( PhoneApplet* parent ) { m_Parent = parent; m_iRet = 0; }
~ModemAnswerThread() {}
void run() { m_iRet = m_Parent->modem_answer_sequence( m_fn ); }
bool result() { return m_iRet == 0; }
int error() { return m_iRet; }
void setFn( int fn ) { m_fn = fn; }
void sleepu( unsigned long msec ) { usleep( msec ); }
private:
PhoneApplet* m_Parent;
int m_fn;
int m_iRet;
};
class ModemDialThread : public QThread {
public:
ModemDialThread( PhoneApplet* parent ) { m_Parent = parent; m_iRet = 0; }
~ModemDialThread() {}
void run() { m_iRet = m_Parent->modem_dial_sequence( m_fn, m_Number ); }
bool result() { return m_iRet == 0; }
int error() { return m_iRet; }
void setFn( int fn ) { m_fn = fn; }
void setNumber( QString n ) { m_Number = n; }
void sleepu( unsigned long msec ) { usleep( msec ); }
private:
PhoneApplet* m_Parent;
QString m_Number;
int m_fn;
int m_iRet;
};
class ModemHangupThread : public QThread {
public:
ModemHangupThread( PhoneApplet* parent ) { m_Parent = parent; m_iRet = 0; }
~ModemHangupThread() {}
void run() { m_iRet = m_Parent->modem_hangup_sequence( m_fn ); }
bool result() { return m_iRet == 0; }
int error() { return m_iRet; }
void setFn( int fn ) { m_fn = fn; }
void sleepu( unsigned long msec ) { usleep( msec ); }
private:
PhoneApplet* m_Parent;
int m_fn;
int m_iRet;
};
class MyPhoneNumber : public QDialog {
public:
MyPhoneNumber(PhoneApplet* parent) {
m_Parent = parent; // Caller, simplest UI:
setVisible(FALSE);
setWindowIcon( KIcon( "internet-telephony" ) );
//m_Ui.toolButton_Close->setIcon( KIcon( "system-shutdown" ));
//m_Ui.toolButton_Bell->setIcon( KIcon( "preferences-desktop-notification-bell" ) );
move( m_Parent->popupPosition( size() ) );
m_Ui.setupUi( this );
/* set this all up in the parent because:
* 1. Cannot find the slot in this context for some reason
* 2. This might enable tone-control on live call
*
connect( m_Ui.pushButton_Call, SIGNAL( clicked() ), m_Parent, SLOT( CallNumber()) );
connect( m_Ui.checkBox_Bell, SIGNAL( clicked()), m_Parent, SLOT( toggle_sound()));
connect( m_Ui.pushButton_Back, SIGNAL( clicked()), this, SLOT(deleteText()));
connect( m_Ui.pushButton_1, SIGNAL( clicked()), this, SLOT( digitClicked()) );
connect( m_Ui.pushButton_2, SIGNAL( clicked()), this, SLOT( digitClicked()) );
connect( m_Ui.pushButton_3, SIGNAL( clicked()), this, SLOT( digitClicked()) );
connect( m_Ui.pushButton_4, SIGNAL( clicked()), this, SLOT( digitClicked()) );
connect( m_Ui.pushButton_5, SIGNAL( clicked()), this, SLOT( digitClicked()) );
connect( m_Ui.pushButton_6, SIGNAL( clicked()), this, SLOT( digitClicked()) );
connect( m_Ui.pushButton_7, SIGNAL( clicked()), this, SLOT( digitClicked()) );
connect( m_Ui.pushButton_8, SIGNAL( clicked()), this, SLOT( digitClicked()) );
connect( m_Ui.pushButton_9, SIGNAL( clicked()), this, SLOT( digitClicked()) );
connect( m_Ui.pushButton_0, SIGNAL( clicked()), this, SLOT( digitClicked()) );
*/
}
QString text() {
return m_Ui.lineEdit_Number->text();
}
QObject* Sender() { return sender(); }
Ui::PhoneDialog* UI() { return &m_Ui; }
private:
PhoneApplet* m_Parent;
Ui::PhoneDialog m_Ui;
};
PhoneApplet::PhoneApplet(QObject *parent, const QVariantList &args)
: Plasma::Applet(parent, args),
m_svgMain(this), m_svgRing(this), m_svgTalk(this)
{
m_eState = eIDLE;
m_svgMain.setImagePath( ":/icons/internet-telephony.svgz" );
m_svgRing.setImagePath( ":/icons/preferences-desktop-notification-bell.svgz" );
m_svgRing2.setImagePath( ":/icons/internet-telephony.svgz" );
m_svgConnect.setImagePath( ":/icons/media-seek-forward.svgz" );
m_svgConnect2.setImagePath( ":/icons/internet-telephony.svgz" );
m_svgDial.setImagePath( ":/icons/preferences-desktop-user.svgz" );
m_svgDial2.setImagePath( ":/icons/internet-telephony.svgz" );
m_svgTalk.setImagePath( ":/icons/user-identity.svgz" );
m_svgTalk2.setImagePath( ":/icons/list-remove-user.svgz" );
// this will get us the standard applet background, for free!
setBackgroundHints(DefaultBackground);
resize(48, 48);
}
PhoneApplet::~PhoneApplet() {
if (hasFailedToLaunch()) {
// Do something
} else {
// Save settingsome cleanup here
AbortAllThreads();
delete m_QTimerBlink;
delete m_QTimerEndRings;
delete m_DaemonThread;
delete m_AnswerThread;
delete m_HangupThread;
delete m_DialThread;
delete m_PhoneNumber;
delete m_strModemFilename;
delete m_strPlaySound;
}
}
void PhoneApplet::closeEvent( QCloseEvent* ev ) {
// cannot wait until the destructor, do it now!
// End the thread, just in case :-)
//AbortAllThreads();
m_PhoneNumber->setVisible(FALSE);
ev->ignore();
Applet::closeEvent( ev );
}
void PhoneApplet::AbortAllThreads() {
m_DaemonThread->terminate();
m_QTimerBlink->stop();
m_QTimerEndRings->stop();
m_AnswerThread->terminate();
m_HangupThread->terminate();
m_DialThread->terminate();
m_PhoneNumber->setVisible(FALSE);
}
void PhoneApplet::init() {
// set up defaults, enable configuration later on
m_strModemFilename = new_string(m_strModemFilename, MODEM_FILENAME);
m_strPlaySound = new_string(m_strPlaySound, CMD_PLAY_SOUND);
m_bWantSound = TRUE; // for TRUE, need some plasma/threadsafe exec()
m_iVolume = 2; // medium
m_eState = eIDLE; // decent startup state, but must be reset to use
m_bAlternate = FALSE;
// set up timer
m_QTimerBlink = new QTimer( this );
m_QTimerBlink->setInterval( MSEC_BLINK );
m_QTimerBlink->setSingleShot(FALSE);
connect( m_QTimerBlink, SIGNAL(timeout()), this, SLOT(blinkit()) );
m_QTimerEndRings = new QTimer( this );
m_QTimerEndRings->setInterval( MSEC_AFTER_RINGS );
m_QTimerEndRings->setSingleShot(TRUE);
connect( m_QTimerEndRings, SIGNAL(timeout()), this, SLOT(proc_endringseq()) );
// Connect up modem UI threads
m_AnswerThread = new ModemAnswerThread( this );
connect( m_AnswerThread, SIGNAL( finished()), this, SLOT( CallAnswered() ) );
m_DialThread = new ModemDialThread( this );
connect( m_DialThread, SIGNAL( finished()), this, SLOT( CallAnswered() ) );
m_HangupThread = new ModemHangupThread( this );
connect( m_HangupThread, SIGNAL( finished()), this, SLOT( CallHungUp() ) );
m_PhoneNumber = new MyPhoneNumber( this );
{ /* set up the UI here as some of it did not work in the class itself
*
*/
m_PhoneNumber->UI()->toolButton_Bell->setChecked( m_bWantSound );
connect( m_PhoneNumber->UI()->pushButton_Call, SIGNAL( clicked() ), this, SLOT( CallNumber()) );
connect( m_PhoneNumber->UI()->toolButton_Bell, SIGNAL( clicked()), this, SLOT( toggle_sound()));
connect( m_PhoneNumber->UI()->toolButton_Volume, SIGNAL( clicked()), this, SLOT( setVolume()));
connect( m_PhoneNumber->UI()->pushButton_Back, SIGNAL( clicked()), this, SLOT(deleteText()));
connect( m_PhoneNumber->UI()->pushButton_1, SIGNAL( clicked()), this, SLOT( digitClicked1()) );
connect( m_PhoneNumber->UI()->pushButton_2, SIGNAL( clicked()), this, SLOT( digitClicked2()) );
connect( m_PhoneNumber->UI()->pushButton_3, SIGNAL( clicked()), this, SLOT( digitClicked3()) );
connect( m_PhoneNumber->UI()->pushButton_4, SIGNAL( clicked()), this, SLOT( digitClicked4()) );
connect( m_PhoneNumber->UI()->pushButton_5, SIGNAL( clicked()), this, SLOT( digitClicked5()) );
connect( m_PhoneNumber->UI()->pushButton_6, SIGNAL( clicked()), this, SLOT( digitClicked6()) );
connect( m_PhoneNumber->UI()->pushButton_7, SIGNAL( clicked()), this, SLOT( digitClicked7()) );
connect( m_PhoneNumber->UI()->pushButton_8, SIGNAL( clicked()), this, SLOT( digitClicked8()) );
connect( m_PhoneNumber->UI()->pushButton_9, SIGNAL( clicked()), this, SLOT( digitClicked9()) );
connect( m_PhoneNumber->UI()->pushButton_0, SIGNAL( clicked()), this, SLOT( digitClicked0()) );
}
//m_QTimerBlink->start();
m_DaemonThread = new RingDaemonThread( this );
StartDaemon();
}
void PhoneApplet::digitClicked( char digit) {
QString number = m_PhoneNumber->UI()->lineEdit_Number->text();
number.append( digit );
m_PhoneNumber->UI()->lineEdit_Number->setText( number);
if ( m_eState == eTALK ) {
// try to send the tone!
QString tone;
tone =+ MODEM_TONE;
tone += '{';
tone += digit;
tone += ",2}"; // 2 x 100 ms
int ret = modem_talk( m_iModemFileno, tone.toAscii(), MODEM_WAS_OK );
if ( ret != 0 )
ErrMessage( ret ); //my not want this but diagnostic for now
}
}
void PhoneApplet::deleteText() {
QString number = m_PhoneNumber->UI()->lineEdit_Number->text();
number.truncate(number.length() - 1);
m_PhoneNumber->UI()->lineEdit_Number->setText( number);
}
void PhoneApplet::paintInterface(QPainter *p,
const QStyleOptionGraphicsItem *option, const QRect &contentsRect) {
p->setRenderHint(QPainter::SmoothPixmapTransform);
p->setRenderHint(QPainter::Antialiasing );
// Now we draw the applet, starting with our svg for appropriate program state
// The icons do not look good filling the whole rectangle so:
QRect myRect;
QPoint myOffset;
myOffset.setX( contentsRect.width() / 10 );
myOffset.setY( contentsRect.height() / 10 );
myRect = contentsRect;
myRect.setX( contentsRect.x() + myOffset.x() );
myRect.setY( contentsRect.y() + myOffset.y() );
myRect.setWidth( contentsRect.width() - 2 * myOffset.x() );
myRect.setHeight( contentsRect.height() - 2 * myOffset.y() );
switch ( m_eState ) {
case eTALK:
paintInterfaceState( p, option, myRect,
m_bAlternate ? m_svgTalk2 : m_svgTalk );
break;
case eRING:
paintInterfaceState( p, option, myRect,
m_bAlternate ? m_svgRing2 : m_svgRing );
break;
case eCONNECT:
paintInterfaceState( p, option, myRect,
m_bAlternate ? m_svgConnect2 : m_svgConnect );
break;
case eDIAL:
paintInterfaceState( p, option, myRect,
m_bAlternate ? m_svgDial2 : m_svgDial );
break;
default:
paintInterfaceState( p, option, myRect, m_svgMain);
break;
}
// We place the icon and text
//p->drawPixmap(7, 0, m_icon.pixmap((int) contentsRect.width(), (int) contentsRect.width() - 14));
/*p->save();
p->setPen(Qt::white);
p->drawText(contentsRect,
Qt::AlignBottom | Qt::AlignHCenter,
"Phone Home!");
p->restore();*/
}
void PhoneApplet::paintInterfaceState(QPainter *p,
const QStyleOptionGraphicsItem *option, const QRect &contentsRect,
Plasma::Svg& svg) {
svg.resize((int) contentsRect.width(), (int) contentsRect.height());
svg.paint(p, (int) contentsRect.left(), (int) contentsRect.top());
}
void PhoneApplet::StartDaemon() {
if ( m_DaemonThread->isRunning() )
return;
// do this stuff here to TEST modem access!
if ( access(m_strModemFilename, F_OK | R_OK | W_OK ) ) {
ErrMessage( ERRNO_NOMODEM );
set_state( eNOGOOD );
return;
}
set_state( eIDLE );
m_DaemonThread->start();
if ( !m_DaemonThread->result() ) {
set_state( eNOGOOD );
ErrMessage( m_DaemonThread->error() );
}
}
void PhoneApplet::set_state( eMODEM_STATE newstate ) {
m_bAlternate = FALSE;
m_eState = newstate;
if ( m_eState == eIDLE )
m_QTimerBlink->stop();
else
m_QTimerBlink->start();
update();
}
void PhoneApplet::blinkit() {
m_bAlternate = !m_bAlternate;
update();
}
void PhoneApplet::AbortDaemon() {
if ( !m_DaemonThread->isRunning() )
return;
m_DaemonThread->quit();
}
int PhoneApplet::ring_daemon() {
int ModemFileno = modem_open(m_strModemFilename, TRUE ); /* first open */
// this should be tested beforehand!
if (ModemFileno < 0) {
return ERRNO_NOMODEM;
}
// code from xringd.c
int c;
if (ioctl(ModemFileno, TIOCGICOUNT, &c) != 0) {
return ERRNO_BADLINUX;
}
// code from xringg.d
long msec;
struct timeval tmBef, tm;
gettimeofday( &tmBef, NULL );
/* main daemon loop */
while (TRUE) {
/* wait on RI line */
if (ioctl(ModemFileno, TIOCMIWAIT, TIOCM_RNG) != 0) {
if (EINTR == errno) {
continue;
}
/*
* XXX EIO may occur: port hung up by other process!
* Reopen it - danger: reopening failing continously
*/
::close(ModemFileno);
usleep(3 * 1000000); // apparently necessary on repopen
ModemFileno = modem_open(m_strModemFilename, TRUE);
continue;
}
gettimeofday(&tm, NULL);
msec = (tm.tv_sec - tmBef.tv_sec) * 1000L +
(tm.tv_usec - tmBef.tv_usec) / 1000L;
tmBef = tm; // update the before time!
if (msec > MSEC_IGNORE_RING)
proc_ring();
}
if ( ModemFileno >= 0 )
::close(ModemFileno);
return 0;
}
void PhoneApplet::serial_setup( int gPortFd )
{
int i;
struct termios attr;
if (tcgetattr(gPortFd, &attr) < 0) {
MessageBox( "Call to tcgetattr failed" );
return;
}
attr.c_iflag = 0;
attr.c_oflag = 0;
attr.c_cflag = CLOCAL | CREAD | CS8;
attr.c_lflag = 0;
attr.c_cc[VTIME] = 0; /* timeout in tenths of a second */
attr.c_cc[VMIN] = 1; /* Only wait for a single char */
cfsetispeed(&attr, MODEM_BAUD);
cfsetospeed(&attr, MODEM_BAUD);
if (tcsetattr(gPortFd, TCSAFLUSH, &attr) < 0) {
MessageBox( "Call to tcsetattr failed" );
}
}
int PhoneApplet::modem_open(char *file, bool readonly ) {
int fd = -1;
int options;
if (readonly ) {
options = O_NOCTTY | O_RDONLY | O_NDELAY ;
}
else {
options = O_NOCTTY | O_RDWR | O_NDELAY ;
}
if ((fd = ::open(file, options)) < 0) {
return -1;
}
// DISABLE ECHO ALWAYS
struct termios tios;
ioctl(fd, TCGETS, &tios);
tios.c_lflag &= ~(ECHO); /*|ECHOE|ECHOK|ECHOKE|ECHOCTL*/
ioctl(fd, TCSETS, &tios);
if (!readonly )
serial_setup( fd );
return fd;
}
// FOR m_iModemFileno ONLY
void PhoneApplet::modem_close() {
::close( m_iModemFileno );
m_iModemFileno = -1;
}
void PhoneApplet::proc_ring() {
if ( m_eState == eDIAL ) {
m_DialThread->quit();
modem_close();
ErrMessage( ERRNO_TIMEOUT );
set_state( eRING );
}
else if ( m_eState == eIDLE ) {
set_state( eRING);
}
else {}
m_QTimerEndRings->start();
if (m_eState == eRING && m_bWantSound) {
// play the sound, do not care about restult
QtConcurrent::run( fork_cmd, m_strPlaySound );
//QtConcurrent::run( QSound::play, PLAY_SOUND_FILE );
}
}
void PhoneApplet::proc_endringseq() {
if ( m_eState == eCONNECT && !m_AnswerThread->isFinished() ) {
toggle_daemon();
set_state( eIDLE );
m_AnswerThread->quit();
modem_close();
ErrMessage( ERRNO_TIMEOUT );
return;
}
if ( m_eState == eRING )
set_state( eIDLE );
}
void PhoneApplet::toggle_sound() {
//m_bWantSound = !m_bWantSound;
m_bWantSound = m_PhoneNumber->UI()->toolButton_Bell->isChecked();
}
void PhoneApplet::setVolume() {
++m_iVolume;
if ( m_iVolume > 3 )
m_iVolume = 0;
}
void PhoneApplet::toggle_daemon() {
if ( m_DaemonThread->isRunning() )
AbortDaemon();
else
StartDaemon();
}
// I do not know why this works but ... it does :-)
// TODO, do this using phonon
static bool fork_cmd(char *cmd)
{
int pid;
int fdnull;
if ((pid = fork()) < 0) {
return FALSE;
}
else if (0 == pid) {
/* child */
/* unblock signals we use - may be blocked here */
//sigprocmask(SIG_UNBLOCK, &sig_mask, NULL);
setsid();
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
/* connect /dev/null to std{in/out/err} */
fdnull = open(_PATH_DEVNULL, O_RDWR);
if (fdnull >= 0) {
if (fdnull != 0) {
dup2(fdnull, 0);
close(fdnull);
}
dup2(0, 1);
dup2(0, 2);
}
setuid(getuid());
setgid(getgid());
execl("/bin/sh", "sh", "-c", cmd, NULL);
return TRUE;
//exit(1);
}
else {
return TRUE; // ever get here?
}
/* parent */
}
// This is the UI:
void PhoneApplet::mousePressEvent ( QGraphicsSceneMouseEvent *ev ) {
if ( signalsBlocked() ) {
Plasma::Applet::mousePressEvent( ev );
return;
}
if ( ev->button() == Qt::LeftButton )
proc_click();
else
Plasma::Applet::mousePressEvent( ev );
}
void PhoneApplet::proc_click() {
switch ( m_eState ) {
case eTALK: HangUp(); break;
case eRING: AnswerCall(); break;
case eIDLE: ToggleGUI(); break;
case eDIAL: m_DialThread->quit();
HangUp();
break;
default: break;
}
}
#define FMT "%s%s "
void PhoneApplet::HangUp() {
// should not get here:
if ( m_iModemFileno < 0 )
return;
m_HangupThread->setFn( m_iModemFileno );
m_HangupThread->start();
}
void PhoneApplet::CallHungUp() {
modem_close();
//m_PhoneNumber->hide();
set_state( eIDLE );
}
void PhoneApplet::AnswerCall() {
m_iModemFileno = modem_open( m_strModemFilename, FALSE );
if ( m_iModemFileno < 1 ) {
ErrMessage( ERRNO_NOMODEM );
return;
}
//toggle_daemon();
set_state( eCONNECT );
m_AnswerThread->setFn( m_iModemFileno );
m_AnswerThread->start();
m_DaemonThread->pause( MSEC_WAIT_THREAD, m_AnswerThread );
m_QTimerEndRings->stop();
}
void PhoneApplet::CallAnswered() {
bool stat;
int err;
//toggle_daemon();
switch ( m_eState ) {
case eCONNECT:
stat = m_AnswerThread->result();
err = m_AnswerThread->error();
break;
case eDIAL:
stat = m_DialThread->result();
err = m_DialThread->error();
break;
default:
set_state( eIDLE );
return;
}
if ( !stat ) {
//m_PhoneNumber->setVisible( FALSE );
set_state( eIDLE );
ErrMessage( err );
modem_close();
return;
}
set_state( eTALK );
}
void PhoneApplet::ToggleGUI() {
//set_status( eDIAL);
m_PhoneNumber->setVisible( !m_PhoneNumber->isVisible() );
}
void PhoneApplet::CallNumber() {
// actions like main click
if (m_eState == eTALK) {
HangUp();
return;
} else if (m_eState == eDIAL) {
m_DialThread->quit();
HangUp();
return;
} else if (m_eState == eIDLE) {
// prevent recursion
if (m_DialThread->isRunning())
return;
m_iModemFileno = modem_open(m_strModemFilename, FALSE);
if (m_iModemFileno < 1) {
ErrMessage(ERRNO_NOMODEM);
return;
}
//toggle_daemon();
set_state(eDIAL);
//MessageBox ( m_PhoneNumber->UI()->lineEdit_Number->text().toAscii() );
m_DialThread->setFn(m_iModemFileno);
m_DialThread->setNumber(m_PhoneNumber->UI()->lineEdit_Number->text());
m_DialThread->start();
m_DaemonThread->pause(MSEC_WAIT_THREAD, m_DialThread);
//m_QTimerEndRings->stop();
}
else ;
}
void PhoneApplet::NotImplemented() {
MessageBox("Not Implemented");
}
void PhoneApplet::MessageBox( const char* cmd ) {
QMessageBox(QMessageBox::Information, name(), QString(cmd) ).exec();
}
int PhoneApplet::modem_answer_sequence( int fn ) {
int ret;
if ( ( ret = modem_talk( fn, MODEM_INIT, MODEM_WAS_OK ) ) != 0 )
return ERRNO_INITMODEM;
//if ( ( ret = modem_talk( fn, MODEM_RESET, MODEM_WAS_OK ) ) != 0 )
// return ERRNO_RESETMODEM;
if ( ( ret = modem_volume( fn, m_iVolume )) != 0 )
return ERRNO_MODEMVOLUME;
if ( ( ret = modem_talk( fn, MODEM_SET_SPEAKERPHONE, MODEM_WAS_OK ) ) != 0 )
return ERRNO_SETSPEAKERPHONE;
if ( ( ret = modem_talk( fn, MODEM_ANSWER, MODEM_VOICE_COM ) ) != 0 )
return ERRNO_ANSWERMODEM;
return 0;
}
int PhoneApplet::modem_dial_sequence( int fn, QString& number ) {
int ret;
if ( number == "" )
return ERRNO_NONUMBER;
QString numbercmd;
numbercmd = MODEM_TONE_DIAL_PRE;
numbercmd += number;
numbercmd += MODEM_TONE_DIAL_SUF;
numbercmd.remove( '-'); //carefull!
if ( ( ret = modem_talk( fn, MODEM_INIT, MODEM_WAS_OK ) ) != 0 )
return ERRNO_INITMODEM;
//if ( ( ret = modem_talk( fn, MODEM_RESET, MODEM_WAS_OK ) ) != 0 )
// return ERRNO_RESETMODEM;
if ( ( ret = modem_volume( fn, m_iVolume )) != 0 )
return ERRNO_MODEMVOLUME;
if ( ( ret = modem_talk( fn, MODEM_SET_SPEAKERPHONE, MODEM_WAS_OK ) ) != 0 )
return ERRNO_SETSPEAKERPHONE;
if ( ( ret = modem_dial( fn, numbercmd.toAscii() ) ) != 0 )
return ret; // may contain other status flags!
return 0;
}
int PhoneApplet::modem_hangup_sequence(int fn ) {
int ret1 = 0, ret2 = 0;
ret1 = modem_talk( fn, MODEM_HANGUP, MODEM_WAS_OK );
ret2 = modem_talk( fn, MODEM_RESET, MODEM_WAS_OK );
return ret1 != 0 ? ret1 : ret2;
}
bool testresp( char* test, const char* resp ) {
return strcasestr( test, resp ) != NULL;
}
/*
* Tne next two function were written by Nelson Castillo.
* You should not blame Dave for what he wrote here :-)
*/
int serial_read(int fd, char *buf, int len, struct timeval* tv) {
fd_set rfds;
int retval;
int nread = 0;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
select_next:
/* We rely on the value of tv after the select call.
* This is a Linux-only thing.
* TODO: Fix. */
errno = 0;
retval = select(fd + 1, &rfds, NULL, NULL, tv);
if (retval == -1) {
if (errno == EINTR)
goto select_next;
return -1;
} else if (retval) {
if (len - nread > 0) {
int r;
r = read(fd, buf + nread, len - nread);
if (r > 0) {
nread += r;
if (len - nread > 0)
goto select_next;
}
if ( r < 0 )
return nread;
}
}
return nread;
}
void setup_timeout(long ms_timeout, struct timeval* tv) {
tv->tv_sec = ms_timeout / 1000;
tv->tv_usec = (ms_timeout % 1000) * 1000;
};
int modem_talk(int fn, const char* cmd, const char* resp) {
char buffer[64];
int ret;
for (int t = 0; t < RETRIES; ++t) {
while ( read( fn, buffer, 1 ) == 1 ) ; // FLUSH before command!
ret = write(fn, cmd, strlen(cmd));
if ( ret < 0 )
break;
write( fn, "\r", 1 ); // general terminator ^M
if (ret != strlen(cmd))
continue;
/*switch ( m_eState ) {
case eCONNECT: m_AnswerThread->sleepu(150000l); break;
case eDIAL: m_DialThread->sleepu( 150000l); break;
case eTALK: m_HangupThread->sleepu( 150000l ); break;
default: break;
*/
/*char* bufPtr = buffer;
char ch;
do {
if (read(fn, &ch, 1) < 0)
break;
*bufPtr = ch;
++bufPtr;
*bufPtr = '\0';
// if I interecepted a RING, start over :-)
if ( testresp( buffer, MODEM_RING ) )
ch = 0, bufPtr = buffer;
} while (ch != '\n' && ch != '\r' && bufPtr - buffer < 63);
//ret = read( fn, buffer, 64 ); // flush is apparently needed!
//ret = ioctl( fn, _IOR( 4, 1, char*), buffer );*/
struct timeval tv;
setup_timeout( MSEC_READ_TIMEOUT, &tv );
serial_read( fn, buffer, 64, &tv );
if (testresp( buffer, resp) )
return 0;
// need valid test
}
return ERRNO_GENERAL;
}
int modem_dial(int fn, const char *cmd) {
char buffer[64];
int ret;
while ( read( fn, buffer, 1 ) == 1 ) ; // FLUSH before command!
ret = write(fn, cmd, strlen(cmd));
if (ret < 0)
return ERRNO_DIAL;
write(fn, "\r", 1); // general terminator ^M
if (ret != strlen(cmd))
return ERRNO_DIAL;
//m_DialThread->sleepu( 350000l );
//long lret;
//while ( ioctl(fn, FIONREAD, &lret), lret == 0l) ;
/*char* bufPtr = buffer;
char ch;
do {
ret = read(fn, &ch, 1);
if ( ret < 0)
break; //!!
if ( ret == 0 )
continue; // ??
*bufPtr = ch;
++bufPtr;
*bufPtr = '\0';
// if I interecepted a RING, start over :-)
if (testresp(buffer, MODEM_RING))
ch = 0, bufPtr = buffer;
} while (ch != '\n' && ch != '\r' && bufPtr - buffer < 63);*/
struct timeval tv;
setup_timeout(MSEC_DIAL_TIMEOUT, &tv);
ret = serial_read(fn, buffer, 64, &tv);
if ( ret < 0 )
return ERRNO_DIAL;
// return any specific responses
if (testresp(buffer, MODEM_BUSY))
return ERRNO_BUSY;
if (testresp(buffer, MODEM_CARRIER))
return ERRNO_NOCARRIER;
if (testresp(buffer, MODEM_DIALTONE))
return ERRNO_NODIALTONE;
if (testresp(buffer, MODEM_NOANSWER))
return ERRNO_NOANSWER;
if (testresp(buffer, MODEM_WAS_ERROR))
return ERRNO_GENERAL;
return 0;
}
int modem_volume( int fn, int vol ) {
switch (vol) {
case 0: return modem_talk( fn, MODEM_ZERO_VOLUME, MODEM_WAS_OK );
case 1: return modem_talk( fn, MODEM_LOW_VOLUME, MODEM_WAS_OK );
case 2: return modem_talk( fn, MODEM_MED_VOLUME, MODEM_WAS_OK );
default:return modem_talk( fn, MODEM_HI_VOLUME, MODEM_WAS_OK );
}
}
void PhoneApplet::ErrMessage( int err_no ) {
switch ( err_no ) {
case ERRNO_NOMODEM: MessageBox( ERRMSG_NOMODEM ); break;
case ERRNO_BADLINUX: MessageBox( ERRMSG_BADLINUX ); break;
case ERRNO_INITMODEM: MessageBox( ERRMSG_INITMODEM ); break;
case ERRNO_RESETMODEM: MessageBox( ERRMSG_RESETMODEM ); break;
case ERRNO_SETSPEAKERPHONE: MessageBox( ERRMSG_SETSPEAKERPHONE ); break;
case ERRNO_HANGUPMODEM: MessageBox( ERRMSG_HANGUPMODEM ); break;
case ERRNO_ANSWERMODEM: MessageBox( ERRMSG_ANSWERMODEM ); break;
case ERRNO_BUSY: MessageBox( ERRMSG_BUSY ); break;
case ERRNO_NOCARRIER: MessageBox( ERRMSG_NOCARRIER ); break;
case ERRNO_NONUMBER: MessageBox( ERRMSG_NONUMBER ); break;
case ERRNO_DIAL: MessageBox( ERRMSG_DIAL ); break;
case ERRNO_TIMEOUT: MessageBox( ERRMSG_TIMEOUT ); break;
case ERRNO_NODIALTONE: MessageBox( ERRMSG_NODIALTONE ); break;
case ERRNO_NOANSWER: MessageBox( ERRMSG_NOANSWER ); break;
case ERRNO_MODEMVOLUME: MessageBox( ERRMSG_MODEMVOLUME ); break;
default: MessageBox( ERRMSG_GENERAL ); break;
}
}
static char* new_string(char *s, const char *ns) {
int newsize = strlen(ns) + 1;
char *p = new char[ newsize ];
strcpy(p, ns);
return p;
}
#include "phoneapplet.moc"
// phoneapplet.h definitions
//
// David Baron, November 2008
//
// Here we avoid loading the header multiple times
#ifndef PhoneApplet_HEADER
#define PhoneApplet_HEADER
// We need the Plasma Applet headers
#include <Plasma/Applet>
#include <Plasma/Svg>
#include <QIcon>
#include <QtGui>
#include <QtConcurrentRun>
#include <QFuture>
#include <QTimer>
// These are the program states:
typedef enum {
eIDLE, eRING, eCONNECT, eDIAL, eTALK, eBUSY, eNOGOOD
} eMODEM_STATE;
// Edit this for your modem device!
const char* MODEM_FILENAME = "/dev/ttyS2";
const speed_t MODEM_BAUD = B57600;
// These are for Rockwell chip (336, etc) speakerphone modems
// Edit these for your modem if necessary (\n or ^M ??)
// Standar Hayes compatable
const char* MODEM_INIT = "~AT S7=45 S0=0 M2 V1 X4 &c1 E0 Q0"; // pref ~\r~
// S7 remote carrier wait secs
// S0 rings before auto-answer
// M2 speaker always on (0-off, 1-till answer 3-during answer)
// V1 textual responses
// X4 all of the above
// &c1 DCD follows carrier
// E0 echo off
// Q0 DTR set
const char* MODEM_RESET = "ATZ";
const char* MODEM_ANSWER = "ATA";
const char* MODEM_HANGUP = "ATH";
const char* MODEM_TONE_DIAL_PRE = "ATDT";
const char* MODEM_TONE_DIAL_SUF = "";
const char* MODEM_ZERO_VOLUME = "ATL0";
const char* MODEM_LOW_VOLUME = "ATL1";
const char* MODEM_MED_VOLUME = "ATL2";
const char* MODEM_HI_VOLUME = "ATL3";
const char* MODEM_TONE = "AT#VTS=";
// should precede dialing or answer:
// may change for other models
const char* MODEM_SET_SPEAKERPHONE = "AT#CLS=8 #VRN=0 #VLS=6";
// standard? modem status returns
const char* MODEM_WAS_OK = "OK";
const char* MODEM_WAS_ERROR = "ERROR";
const char* MODEM_VOICE_COM = "VCON";
const char* MODEM_RING = "RING";
const char* MODEM_BUSY = "BUSY";
const char* MODEM_CARRIER = "CARRIER";
const char* MODEM_DIALTONE = "DIALTONE";
const char* MODEM_NOANSWER = "ANSWER";
const char* CMD_PLAY_SOUND = "/usr/bin/ogg123 -d alsa /usr/share/sounds/KDE-Im-Nudge.ogg";
const char* PLAY_SOUND_FILE = "/usr/share/sounds/KDE-Im-Nudge.ogg";
const long MSEC_IGNORE_RING = 500l; // because rings can be irregular
const long MSEC_BLINK = 667l; // blink time
const long MSEC_AFTER_RINGS = 3500l; // if I do not answer the phone
const long MSEC_WAIT_THREAD = 5000l; // pasue the thread while procesing commands from me
const long MSEC_READ_TIMEOUT = 250l;
const long MSEC_DIAL_TIMEOUT = 15000l;
const int RETRIES = 3;
// ERROR STATUSES AND MESSAGES
const int ERRNO_NOMODEM = -1;
const char* ERRMSG_NOMODEM = "Could not open modem";
const int ERRNO_BADLINUX = -2;
const char* ERRMSG_BADLINUX = "Incompatable Linux version";
const int ERRNO_INITMODEM = -3;
const char* ERRMSG_INITMODEM = "Could not initialize modem";
const int ERRNO_RESETMODEM = -4;
const char* ERRMSG_RESETMODEM = "Could not reset modem";
const int ERRNO_SETSPEAKERPHONE = -5;
const char* ERRMSG_SETSPEAKERPHONE = "Could not set modem to speakerphone";
const int ERRNO_HANGUPMODEM = -6;
const char* ERRMSG_HANGUPMODEM = "Could not hang up modem";
const int ERRNO_BUSY = -7;
const char* ERRMSG_BUSY = "Line busy";
const int ERRNO_NOCARRIER = -8;
const char* ERRMSG_NOCARRIER = "No carrier";
const int ERRNO_TIMEOUT = -9;
const char* ERRMSG_TIMEOUT = "Could not succeed in time";
const int ERRNO_ANSWERMODEM = -10;
const char* ERRMSG_ANSWERMODEM = "Could not answer";
const int ERRNO_NONUMBER = -11;
const char* ERRMSG_NONUMBER = "No phone number entered";
const int ERRNO_DIAL = -12;
const char* ERRMSG_DIAL = "Cannot dial";
const int ERRNO_NODIALTONE = -13;
const char* ERRMSG_NODIALTONE = "No dial tone";
const int ERRNO_NOANSWER = -14;
const char* ERRMSG_NOANSWER = "No answer";
const int ERRNO_MODEMVOLUME = -15;
const char* ERRMSG_MODEMVOLUME = "Cannot set volume";
const int ERRNO_GENERAL = -100;
const char* ERRMSG_GENERAL = "Could not do it";
class QSizeF;
class RingDaemonThread;
class ModemAnswerThread;
class ModemHangupThread;
class ModemDialThread;
class MyPhoneNumber;
// Define our plasma Applet
namespace Plasma {
class Svg;
}
class PhoneApplet : public Plasma::Applet {
Q_OBJECT
public:
// public functions
// Basic Create/Destroy
PhoneApplet(QObject *parent, const QVariantList &args);
~PhoneApplet();
// The paintInterface procedure paints the applet to screen
void paintInterface(QPainter *painter,
const QStyleOptionGraphicsItem *option,
const QRect& contentsRect);
void init();
int ring_daemon();
void proc_click();
int modem_answer_sequence( int fn );
int modem_hangup_sequence( int fn );
int modem_dial_sequence( int fn, QString& numb );
private:
void closeEvent( QCloseEvent* ev );
void paintInterfaceState(QPainter *painter,
const QStyleOptionGraphicsItem *option,
const QRect& contentsRect,
Plasma::Svg&);
// functions from xringd.c
int modem_open(char *file, bool readonly );
//bool modem_talk( char* cmd, char* resp ); // for member file number
void modem_close(); // for member file number
bool is_ring_near();
//bool is_ring_far();
void proc_ring();
void set_state( eMODEM_STATE newstate );
void MessageBox(const char*);
void ErrMessage(int);
void NotImplemented();
void mousePressEvent(QGraphicsSceneMouseEvent *);
void AbortAllThreads();
void digitClicked(char digit);
public slots:
void digitClicked1() { digitClicked( '1' ); }
void digitClicked2() { digitClicked( '2' ); }
void digitClicked3() { digitClicked( '3' ); }
void digitClicked4() { digitClicked( '4' ); }
void digitClicked5() { digitClicked( '5' ); }
void digitClicked6() { digitClicked( '6' ); }
void digitClicked7() { digitClicked( '7' ); }
void digitClicked8() { digitClicked( '8' ); }
void digitClicked9() { digitClicked( '9' ); }
void digitClicked0() { digitClicked( '0' ); }
void deleteText();
void CallNumber();
void toggle_sound();
void setVolume();
void serial_setup(int);
private slots:
void blinkit();
void proc_endringseq();
void toggle_daemon();
void StartDaemon();
void AbortDaemon();
void HangUp();
void CallHungUp();
void AnswerCall();
void CallAnswered();
void ToggleGUI();
private:
Plasma::Svg m_svgMain;
Plasma::Svg m_svgRing;
Plasma::Svg m_svgRing2;
Plasma::Svg m_svgConnect;
Plasma::Svg m_svgConnect2;
Plasma::Svg m_svgDial;
Plasma::Svg m_svgDial2;
Plasma::Svg m_svgTalk;
Plasma::Svg m_svgTalk2;
eMODEM_STATE m_eState;
int m_iModemFileno;
bool m_bWantSound;
bool m_bAlternate;
int m_iVolume;
//struct timeval m_tmBef;
QTimer* m_QTimerBlink;
QTimer* m_QTimerEndRings;
RingDaemonThread* m_DaemonThread;
ModemAnswerThread* m_AnswerThread;
ModemHangupThread* m_HangupThread;
ModemDialThread* m_DialThread;
MyPhoneNumber* m_PhoneNumber;
// program configuration copied to these
char* m_strModemFilename;
char* m_strPlaySound;
};
// This is the command that links your applet to the .desktop file
K_EXPORT_PLASMA_APPLET(phoneapplet, PhoneApplet)
#endif
_______________________________________________
Plasma-devel mailing list
[email protected]
https://mail.kde.org/mailman/listinfo/plasma-devel