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 > > Plasma-devel@kde.org > > https://mail.kde.org/mailman/listinfo/plasma-devel > > _______________________________________________ > Plasma-devel mailing list > Plasma-devel@kde.org > 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 Plasma-devel@kde.org https://mail.kde.org/mailman/listinfo/plasma-devel