Bercut-MMTデバイスで使用するQt4 Embeddedでは、エンコーダーなどの入力デバイスはサポートされていません。 つまり マウスをデバイスに接続すると、移動時に座標が処理されますが、スクロールホイールは処理されません。 linuxinputドライバーは 、エンコーダーが生成するREL_WHEEL型のイベントを処理せず、座標の変更を担当するREL_XおよびREL_Yのみを処理するためです。
誰がこの問題を解決するかを気にします-猫へようこそ。
Linuxカーネルサブシステムの入力からのイベントを処理するlinuxinputドライバーコードの一部を次に示します。
for (int i = 0; i < n; ++i) { struct ::input_event *data = &buffer[i]; bool unknown = false; if (data->type == EV_ABS) { if (data->code == ABS_X) { m_x = data->value; } else if (data->code == ABS_Y) { m_y = data->value; } else { unknown = true; } } else if (data->type == EV_REL) { if (data->code == REL_X) { m_x += data->value; } else if (data->code == REL_Y) { m_y += data->value; } else { unknown = true; } } else if (data->type == EV_KEY && data->code == BTN_TOUCH) { m_buttons = data->value ? Qt::LeftButton : 0; } else if (data->type == EV_KEY) { int button = 0; switch (data->code) { case BTN_LEFT: button = Qt::LeftButton; break; case BTN_MIDDLE: button = Qt::MidButton; break; case BTN_RIGHT: button = Qt::RightButton; break; } if (data->value) m_buttons |= button; else m_buttons &= ~button; } else if (data->type == EV_SYN && data->code == SYN_REPORT) { QPoint pos(m_x, m_y); pos = m_handler->transform(pos); m_handler->limitToScreen(pos); m_handler->mouseChanged(pos, m_buttons); } else if (data->type == EV_MSC && data->code == MSC_SCAN) { // kernel encountered an unmapped key - just ignore it continue; } else { unknown = true; } if (unknown) { qWarning("unknown mouse event type=%x, code=%x, value=%x", data->type, data->code, data->value); } }
問題を解決します
次の3つのオプションがあります。
- linuxinputドライバーの変更
- l inuxinputドライバーが理解できるイベントを生成するようにカーネルドライバーを変更する
- Qt4の入力デバイスドライバーを書く
3番目のオプションは最も正確です。 検討します。
ドライバーを書く
ドライバーを作成するには、 QWSMouseHandlerの子孫とQWSMousePluginの子孫の 2つのクラスを作成する必要があります。 最初のタスクは入力デバイスを直接操作することで、2番目のタスクはQMouseDriverFactoryに %drivername%という名前のドライバーに対してQWSMouseHandler子孫の実装を使用する必要があることを説明することです。
QWSMouseHandlerの下位クラスから始めましょう:
class RotaryEncoderHandler: public QObject, public QWSMouseHandler { Q_OBJECT public: RotaryEncoderHandler( const QString &device = QString("/dev/input/rotary_encoder" ) ); ~RotaryEncoderHandler( ); void suspend( ); void resume ( ); private: QSocketNotifier *m_notify; int deviceFd; int m_wheel; private slots: void readMouseData( ); };
ヘッダーファイルからわかるように、 suspend() 、 resume() 、 readMouseData()の 3つの関数を実装する必要があります。 まあ、デストラクタを持つコンストラクタ。
コンストラクター-引数として、デバイス名が来ます-例えば/ dev / input / event3 。 次に、指定された名前でデバイスのファイル記述子を開き、それをtearにQSocketNotifierに転送します。 QSocketNotifierは、ファイル記述子をリッスンし、その動きのいずれかに対してアクティブ化された(int)シグナルを発信する獣です。
RotaryEncoderHandler::RotaryEncoderHandler( const QString &device ): QWSMouseHandler( device ) ,deviceFd( 0 ) ,m_wheel( 0 ) { setObjectName("Rotary Encoder Handler"); deviceFd = ::open(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY); if( deviceFd > 0 ){ qDebug() << "Opened" << device << "as rotary encoder device"; m_notify = new QSocketNotifier( deviceFd, QSocketNotifier::Read, this); connect( m_notify, SIGNAL( activated(int)), this, SLOT( readMouseData())); } else { qWarning("Cannot open %s: %s", device.toLocal8Bit().constData(), strerror( errno ) ); return; } }
つまり 入力デバイスの記述子を開き、 QSocketNotifierをそのデバイスに添付し、 アクティブ化された(int)シグナルでハンドラーを切断しました。
このクラスのデストラクタは非常に単純です。そのタスクは、入力デバイスの記述子が開いているかどうかを確認し、開いている場合は閉じます。
suspend()/ resume()メソッドは、入力デバイスからのデータ処理を停止/開始する必要があります。 これは、 QSocketNotifierで setEnabled(bool)メソッドを呼び出すだけで実行されます。
そこで、データプロセッサに直接アクセスしました。
void RotaryEncoderHandler::readMouseData( ) { struct ::input_event buffer[32]; int n = 0; forever { n = ::read(deviceFd, reinterpret_cast(buffer) + n, sizeof(buffer) - n); if (n == 0) { qWarning("Got EOF from the input device."); return; } else if (n < 0 && (errno != EINTR && errno != EAGAIN)) { qWarning("Could not read from input device: %s", strerror(errno)); return; } else if (n % sizeof(buffer[0]) == 0) { break; } } n /= sizeof(buffer[0]); for (int i = 0; i < n; ++i) { struct ::input_event *data = &buffer[i]; bool unknown = false; if (data->type == EV_REL) { if (data->code == REL_WHEEL) { m_wheel = data->value; } else { unknown = true; } } else if (data->type == EV_SYN && data->code == SYN_REPORT) { mouseChanged(pos(), Qt::NoButton, m_wheel); } else if (data->type == EV_MSC && data->code == MSC_SCAN) { // kernel encountered an unmapped key - just ignore it continue; } else { unknown = true; } if (unknown) { qWarning("unknown mouse event type=%x, code=%x, value=%x", data->type, data->code, data->value); } } }
linuxinputドライバーの同様のメソッドに非常に似ていますが、それとは異なり、エンコーダーの状態が変化したイベントのみを送信します。 つまり このドライバは、マウス自体の座標の変更に対する処理が不足しているため、そのままではマウスに使用できません。スクロールホイール以外は機能しません。
次に、ドライバークラスが何であるかを見てみましょう。
class RotaryEncoderDriverPlugin : public QMouseDriverPlugin { Q_OBJECT public: RotaryEncoderDriverPlugin( QObject *parent = 0 ); ~RotaryEncoderDriverPlugin(); QWSMouseHandler* create(const QString& driver); QWSMouseHandler* create(const QString& driver, const QString& device); QStringList keys()const; };
それほど大きくないでしょう? その実装は次のとおりです。
Q_EXPORT_PLUGIN2(rotaryencoderdriver, RotaryEncoderDriverPlugin) RotaryEncoderDriverPlugin::RotaryEncoderDriverPlugin( QObject *parent ): QMouseDriverPlugin( parent ) { } RotaryEncoderDriverPlugin::~RotaryEncoderDriverPlugin() { } QStringList RotaryEncoderDriverPlugin::keys() const { return QStringList() <<"rotaryencoderdriver"; } QWSMouseHandler* RotaryEncoderDriverPlugin::create( const QString& driver, const QString& device ) { if( driver.toLower() == "rotaryencoderdriver" ){ return new RotaryEncoderHandler( device ); } return 0; } QWSMouseHandler* RotaryEncoderDriverPlugin::create( const QString& driver ) { if( driver.toLower() == "rotaryencoderdriver" ){ return new RotaryEncoderHandler( ); } return 0; }
コードからわかるように、ドライバーのタスク全体はQMouseDriverFactoryクラスに、それがRotaryencoderdriverと呼ばれるドライバーであることを伝えることです。 もちろん、 ()メソッドを作成します 。
バトルチェック
ドライバーができたので、特定のデバイスに使用する必要があるものをQt4ライブラリに何らかの形で説明する必要があります。 このための特別な環境変数-QWS_MOUSE_PROTOがあります。 Qt4は、マウスの動きに関するデータを取得するドライバーとデバイスを示します。 エンコーダーが/ dev / input / Rotary0であると仮定すると、それを機能させるには、変数をQWS_MOUSE_PROTO = "rotaryencoderdriver:/ dev / input / Rotary0"として設定する必要があります。
エンコーダーからイベントをキャッチします
エンコーダーイベントを操作するには、アプリケーションにイベントフィルターを実装する必要があります。
bool ClassName::eventFilter(QObject *o, QEvent *e) { if ( o ) { if ( e->type() == QEvent::Wheel) { QWheelEvent* we = static_cast< QWheelEvent* >( e ); /* */ return true; } /* Object*/ return QObject::eventFilter( o, e ); }
便利なリンク
更新:わかりやすくするためにビデオが追加されました