ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Qt/E 터치스크린 강좌
    정리필요2 2007. 11. 23. 05:04
    [QT/Embedded] Qt/E 터치스크린 강좌]

    이 문서의 저작권은 korone.net의 korone에게 있습니다. 다른 곳으로 불펌을 금합니다. 감사합니다.

    이 글은 여러가지 플랫폼에 Qt/E를 포팅하시면서 가장 많이 하시는 질문이 터치스크린에 대한 설정방법입니다.
    여기 질문게시판에도 요즘들어 부쩍많이 글을 써주고 계시는데 일일히 다 답변달기가 힘들어서 강좌형태의 글로써 대신하려고 합니다.

    일단, 터치스크린에 대한 질문을 많이 해주시는데, 터치스크린이라는것이 해당 보드가 어떤지에 따라서 달라지는 것이기 때문에 해당 보드에 대한 경험이 없는 사람들이 답변을 해드리기가 쉽지가 않습니다.
    제가 여기서 말씀드리고 싶은것은 터치스크린을 잡기위해서 수정해야할 부분과 원리르 설명해 주시면 이를 이용해서 각자 자기 보드에 맞는 설정을 하시면 됩니다.

    1. Qt/E 내에서 터치스크린 지원구조
    1.1 Qt/E 2.x 버전
    src/kernel/qwsmouse_qws.cpp 파일을 열어보시면
    QWSMouseHandler* QWSServer::newMouseHandler(const QString& spec)
    함수가 있습니다.
    이 함수는 Qt/e가 시작시 QWS_MOUSE_PROTO환경변수에 설정된 값이 이 함수의 spec
    인자값으로 넘어가게 되어 있습니다.

    이 함수내부를 보시면
    spec값을 : 구분자로 구분을 시키는데
    가령 QWS_MOUSE_PROTO 값을 Qt/E 로 만든 App를 실행시키기 전에
    export QWS_MOUSE_PROTO=TPanel:/dev/ts 라고 설정하였다면
    내부적으로 TPanel 과 /dev/ts라는 형태로 분리가 됩니다. 이 분리된 내용을 가지고
    mouseProtocol이라는 변수에 저장해 놓고 조금더 아래에 다음과 같은 라인에서 처리를 하도록 되어 있습니다.
    switch(mouseProtocol) {
    case Auto:
    .... 중략
    case TPanel:
    #if defined(QWS_CUSTOMTOUCHPANEL)
            handler = new QCustomTPanelHandlerPrivate(mouseProtocol,mouseDev);
    #elif defined(QT_QWS_TSLIB)
            handler = new QTSLibHandlerPrivate();
    #elif defined(QT_QWS_YOPY)
            handler = new QYopyTPanelHandlerPrivate(mouseProtocol,mouseDev);
    #elif defined(QT_QWS_IPAQ) || defined(QT_QWS_SL5XXX) || defined(QT_QWS_K2)
            handler = new QTPanelHandlerPrivate(mouseProtocol,mouseDev);
    #elif defined(QT_QWS_CASSIOPEIA)
            handler = new QVrTPanelHandlerPrivate( mouseProtocol, mouseDev );
    #endif
            break;
    default:
    qDebug( "Mouse type %s unsupported", spec.latin1() );
        }

    그렇습니다. TPanel의 case를 보시면 Qt/E를 처음 컴파일할때 Configure의 옵션에 따라서
    #if define값에 의해서 해당 터치스크린 핸들러 클래스가 동작되게 됩니다.

    여기서는 QT_QWS_TSLIB를 지원하도록 컴파일 했다고 가정한다면
    handler = new QTSLibHandlerPrivate(); 가 실행되어 내부 member 함수에서 실행되도록 되어 있습니다.

    이제 QTSLibHandlerPrivate 클래스를 따라가 보겠습니다.
    이 클래스를 보시면 openTs()함수에서 해당 터치스크린을 동작시키도록 되어 있습니다.

    void QTSLibHandlerPrivate::openTs()
    {
    #ifdef QT_QWS_TSLIB
       char *tsdevice;
       if((tsdevice = getenv("TSLIB_TSDEVICE")) != NULL) {
          m_ts = ts_open( tsdevice, 1 ); //1 = nonblocking, 0 = blocking mode
       } else {
          m_ts = ts_open( "/dev/ts", 1 );
       }

       if (!m_ts) {
          qWarning( "Cannot open touchscreen (%s)", strerror( errno));
          return;
       }

       if (ts_config( m_ts)) {
          qWarning( "Cannot configure touchscreen (%s)", strerror( errno));
          return;
       }
       m_notify = new QSocketNotifier( ts_fd(m_ts), QSocketNotifier::Read, this );
       connect( m_notify, SIGNAL(activated(int)),this, SLOT(readMouseData()));
    #endif
    }

    위를 보시면 TSLIB_TSDEVICE 환경변수가 설정되어 있으면 변수의 내용을 터치스크린 device이름으로
    정하여 open을 하고 지정되지 않는경우 무조건 /dev/ts를 open합니다.

    성공적으로 오픈하였다면 열려진 디바이스 핸들에 대해서 QSocketNotifier로 해당 입력값이 있을때마다
    readMouseData()함수를 호출하도록 되어 있습니다.

    void QTSLibHandlerPrivate::readMouseData()
    {
    #ifdef QT_QWS_TSLIB
        if(!qt_screen)
            return;

        /*
         * After clear Calibration
         * we're in raw mode and do some easy median
         * search.
         */

        if ( m_raw )
            return interpolateSample();

        static struct ts_sample sample;
        static int ret;

        /*
         * Ok. We need to see if we can read more than one event
         * We do this not to lose an update.
         */

        while ( true ) {
            if ((ret = ts_read(m_ts, &sample, 1)) != 1 )
                return;


            QPoint pos( sample.x, sample.y );
            mouseChanged( pos, sample.pressure != 0 ? 1 : 0 );
        }
    #endif
    }

    readMouseData()함수를 보시면 ts_read()함수를 통해 좌표값을 읽어서 mouseChanged함수를 호출해서
    qt내부로 전달하는 아주 간단한 mechanism을 가지고 있습니다.

    참고로 TSLIB는 http://cvs.arm.linux.org.uk/에서 제공하는 터치스크린 라이브러리 입니다.
    따라서 Qt 컴파일시에 반드시 존재해야 합니다.

    제가 터치스크린에 대해서 구조를 따라가면서 설명을 드렸습니다.
    이렇게 설명드린 이유는 만약 좌표값이 이상하게 설정된다면 readMouseData()함수내에서 읽어낸좌표값을
    직접 찍어보시면서 디버깅을 하실 수 있습니다.

    1.2 Qt/E 3.x 버전
    Qt/E 3.x는 방식이 2.x와 약간 다르게 되어 있습니다.
    src/kernel/qwindowsystem_qws.cpp 파일내의 void QWSServer::openMouse() 함수내에서 QWS_MOUSE_PROTO환경변수 값을 읽습니다.
    이 환경변수를 이용해서 QWSMouseHandler* QWSServer::newMouseHandler(const QString& spec)를 호출하고
    newMouseHandler함수내에서 다음과 값이 객체를 생성해 냅니다.

    handler = QMouseDriverFactory::create(mouseProto, mouseDev);

    src/embedded/qmousedriverfactory_qws.cpp 파일의 다음의 함수에서
    QWSMouseHandler *QMouseDriverFactory::create( const QString& key, const QString &device )

    #ifndef QT_NO_QWS_MOUSE_LINUXTP
        if ( driver == "linuxtp" || driver.isEmpty() )
        return new QWSLinuxTPMouseHandler( key, device );
    #endif
    라는 부분을 통해서 QWSlinxTPMouseHandler를 생성합니다.

    즉 Qt/E 2.x에서는 QWS_MOUSE_PROTO=TPanel:/dev/ts를 하면 되었지만
    QT/E 3.x에서는 QWS_MOUSE_PROTO=linuxtp:/dev/ts 라고 하셔야 합니다.

    src/embedded/qmouselinuxtp_qws.cpp 파일내의 QWSLinuxTPMouseHandler 클래스는 내부적으로
    QWSLinuxTPMouseHandlerPrivate 객체를 생성한다음 해당 객체내에서 모든 처리를 하도록 되어 있습니다.

    QWSLinuxTPMouseHandlerPrivate 생성자에서는

    QWSLinuxTPMouseHandlerPrivate::QWSLinuxTPMouseHandlerPrivate( QWSLinuxTPMouseHandler *h )
        : samples(QT_QWS_TP_SAMPLE_SIZE), currSample(0), lastSample(0),
        numSamples(0), skipCount(0), handler(h)
    {
    #if defined(QT_QWS_IPAQ)
    # ifdef QT_QWS_IPAQ_RAW
        if ((mouseFD = open( "/dev/h3600_tsraw", O_RDONLY | O_NDELAY)) < 0) {
    # else
        if ((mouseFD = open( "/dev/h3600_ts", O_RDONLY | O_NDELAY)) < 0) {
    # endif
        qWarning( "Cannot open /dev/h3600_ts (%s)", strerror(errno));
        return;
        }
    #elif defined(QT_QWS_EBX)
    //# ifdef QT_QWS_EBX_TSRAW
    # if 0
        if ((mouseFD = open( "/dev/tsraw", O_RDONLY | O_NDELAY)) < 0) {
            qWarning( "Cannot open /dev/tsraw (%s)", strerror(errno));
           return;
        }
    # else
        if ((mouseFD = open( "/dev/ts", O_RDONLY | O_NDELAY)) < 0) {
            qWarning( "Cannot open /dev/ts (%s)", strerror(errno));
            return;
         }
    # endif
    #endif

    IPAQ PDA일 경우 /dev/h3600_ts를 오픈하고 그외의 경우에는 /dev/ts를 오픈합니다.
    이 역시 Qt/E 2.x와 마찬가지로 QSocketNotifier에 의해서 데이타가 발생시 readMouseData()함수를
    호출하도록 합니다.

    void QWSLinuxTPMouseHandlerPrivate::readMouseData()
    {
        if(!qt_screen)
        return;

        int n;
        do {
        n = read(mouseFD, mouseBuf+mouseIdx, mouseBufSize-mouseIdx );
        if ( n > 0 )
            mouseIdx += n;
        } while ( n > 0 && mouseIdx < mouseBufSize );
        중략...
    }

    위 readMouseData함수를 보시면 read함수를 이용해서 mouseBuf에 좌표값을 불러오도록 합니다.

    1.3 Qtopia Core 4.x
    Qtopia Core 4는 Qt/E 3.x와 비슷한 구조로 되어 있습니다.
    약간 다른것은 directory구조가 변경된것입니다.

    src/gui/embedded/qwindowsystem_qws.cpp 파일의 openMouse()함수에서 QWS_MOUSE_PROTO환경변수를 읽어서
    QWSMouseHandler* QWSServerPrivate::newMouseHandler(const QString& spec) 함수를 호출합니다.

    QWSMouseHandler* QWSServerPrivate::newMouseHandler(const QString& spec)
    {
        static int init=0;
        if (!init && qt_screen) {
            init = 1;
        }

        int c = spec.indexOf(':');
        QString mouseProto;
        QString mouseDev;
        if (c >= 0) {
            mouseProto = spec.left(c);
            mouseDev = spec.mid(c+1);
        } else {
            mouseProto = spec;
        }

        QWSMouseHandler *handler = 0;
        handler = QMouseDriverFactory::create(mouseProto, mouseDev);
        return handler;
    }

    위 함수에서 QMouseDriverFactory::create함수를 호출하여 다음과 같이 처리합니다.
    나머지는 Qt/E 3.x와 모두 동일합니다.
    QWSMouseHandler *QMouseDriverFactory::create(const QString& key, const QString &device)
    {
        QString driver = key.toLower();
    #ifndef QT_NO_QWS_MOUSE_LINUXTP
        if (driver == "linuxtp" || driver.isEmpty())
            return new QWSLinuxTPMouseHandler(key, device);
    #endif
    #ifndef QT_NO_QWS_MOUSE_YOPY
        if (driver == "yopy" || driver.isEmpty())
            return new QWSYopyMouseHandler(key, device);
    #endif
    #ifndef QT_NO_QWS_MOUSE_VR41XX
        if (driver == "vr41xx" || driver.isEmpty())
            return new QWSVr41xxMouseHandler(key, device);
    #endif
    #ifndef QT_NO_QWS_MOUSE_PC
        if (driver == "auto" || driver == "intellimouse" ||
             driver == "microsoft" || driver == "mousesystems" ||
             driver == "mouseman" || driver.isEmpty()) {
            qDebug() << "Creating mouse" << key;
            return new QWSPcMouseHandler(key, device);
        }
    #endif
    #ifndef QT_NO_QWS_MOUSE_BUS
        if (driver == "bus")
            return new QWSBusMouseHandler(key, device);
    #endif
    #ifndef QT_NO_QWS_MOUSE_TSLIB
        if (driver == "tslib" || driver.isEmpty())
            return new QWSTslibMouseHandler(key, device);
    #endif
    #ifndef QT_NO_QWS_MOUSE_QVFB
        if (driver == "qvfbmouse" || driver == "qvfb")
            return new QVFbMouseHandler(key, device);
    #endif

    #if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL)
    #ifndef QT_NO_LIBRARY
        if (QWSMouseHandlerFactoryInterface *factory = qobject_cast<QWSMouseHandlerFactoryInterface*>(loader()->instance(driver)))
            return factory->create(driver);
    #endif
    #endif
        return 0;
    }

    2. custom touchscreen 지원하기
    위 1에서본것은 TSLIB등을 지원하거나 IPAQ, YOPY등의 PDA를 위한 기본코드만 들어 있습니다.
    만약 Qt/E내에 내장되지 않은 방식의 터치스크린을 지원하려며 새롭게 클래스를 추가해 넣어야 합니다.
    이렇게 추가해 넣는것은 1에서 따라기가 형식으로 보았던 경로를 따라가면서 새로운 클래스를 추가해
    넣으면 됩니다.
    이에 대해서는 추후 다시 강좌를 작성하도록 하겠습니다.

    감사합니다.

    이 문서의 저작권은 korone.net의 korone에게 있습니다. 다른 곳으로 불펌을 금합니다. 감사합니다.
Designed by Tistory.