Windows、Linux、およびMac OS Xでのマルチメディアキーストロークエミュレーション







Qt :: Key列挙は、QKeyEventイベントの15種類のマルチメディアコントロールキーを定義します(記事の最後の表を参照)。 これらはすべて、イベントフィルタ(installEventFilter)で使用して、マルチメディアキーボードのキーストロークを処理できます(オーディオデバイスと再生を制御できます)。

逆の問題は記事で検討されています-Windows、Linux、およびMacOSXで対応するキーを押すことをエミュレートしてシステムにマルチメディア制御コマンドを送信します(オペレーティングシステムはソリューションの検索に費やされた時間でソートされます)。 この記事に記載されている資料は、システムメッセージ処理サイクルに送信されるクロスプラットフォームイベントの問題をさらに調査するための出発点となります。



ソースコードの説明に直接進む前に、マルチメディアキーのエミュレーションを一般的に適用できる場所を見てみましょう。たとえば、次のとおりです。



QEventでは、アプリケーションの「内部」にある特定のオブジェクトにのみメッセージを送信できるため、標準のQtツールを使用してキーストロークをエミュレートすることはできません。 これを行うには、Apiシステムコール(Windowsの場合)または対応するライブラリ(LinuxではX Window System、Mac OS Xでは多くのフレームワーク)を使用します。

説明の便宜上、送信メッセージの実装全体をsendKeyEventToSystem(Qt :: Key qtKey)関数に配置します 。この関数はQt :: Key列挙からキーコードを受け取ります。 この関数は、たとえば次のようにスロットから呼び出されます。

void playPauseToogle() { //      postKeyEventToSystem(Qt::Key_MediaTogglePlayPause); }
      
      



プラットフォーム依存コードを#ifdef OS_TYPEディレクティブと#endifディレクティブで分離します Objective-Cコードの別の部分を別のmacx.mmファイルに取り出しますが、これについては後で詳しく説明します)。



Windowsのキーボードエミュレーション



このオペレーティングシステムでは、 SendInput関数がメッセージを送信します。 コードを使用してメッセージを送信できます。コードの完全なリストは、MSDN Virtual-Key Codesページに表示されます。

この関数を使用するには、<Windows.h>ヘッダーファイルを含める必要があります。

 #ifdef Q_OS_WIN32 #define WINVER 0x0500 #include <Windows.h> #endif
      
      





インターネットでこの関数を使用する例は数多くあり、そのアプリケーションは問題を引き起こさないはずなので、すぐにコードを提供します(Windowsの部分)。

 sendKeyEventToSystem(Qt::Key qtKey) { //   . qtKey -   #ifdef Q_OS_WIN32 INPUT ip; //  ip.type = INPUT_KEYBOARD; ip.ki.wScan = 0; ip.ki.time = 0; ip.ki.dwExtraInfo = 0; //     switch (qtKey) { case Qt::Key_MediaPrevious: ip.ki.wVk = VK_MEDIA_PREV_TRACK; //  break; case Qt::Key_MediaTogglePlayPause: ip.ki.wVk = VK_MEDIA_PLAY_PAUSE; //    break; case Qt::Key_MediaNext: ip.ki.wVk = VK_MEDIA_NEXT_TRACK; //  break; default: return; break; } //    ip.ki.dwFlags = 0; SendInput(1, &ip, sizeof(INPUT)); //    ip.ki.dwFlags = KEYEVENTF_KEYUP; SendInput(1, &ip, sizeof(INPUT)); #endif }
      
      



以下の例では、3つのキーのみが使用されます。 記事の最後に、コードマッチングの表があります。



Linuxでのキーボードエミュレーション



Linuxでキーをエミュレートするための最も簡単な方法は、libXtst開発者ライブラリ( X11 Record拡張ライブラリ )を使用することです。

パッケージから取得するには、次のコマンドを実行する必要があります。
 sudo apt-get install libxtst-dev
      
      



また、プロジェクトファイルにライブラリを含める必要があります。

 unix:!macx:LIBS += -lXtst -lX11
      
      





ファイルの先頭で、必要なヘッダーファイルを接続し、マルチメディアキーのコードに対応する多数の定数を定義します(実際には、X11 / keysymdef.hファイルにはマルチメディアキーのコードはありません)。

 #ifdef Q_OS_LINUX #include <X11/Xlib.h> #include <X11/extensions/XTest.h> #define XF86AudioLowerVolume 0x1008ff11 #define XF86AudioMute 0x1008ff12 #define XF86AudioRaiseVolume 0x1008ff13 #define XF86AudioPlay 0x1008ff14 #define XF86AudioStop 0x1008ff15 #define XF86AudioPrev 0x1008ff16 #define XF86AudioNext 0x1008ff17 #define XF86AudioPause 0x1008ff31 #endif
      
      





Linuxエミュレーションコード:

 #ifdef Q_OS_LINUX unsigned int key; unsigned int keycode; switch (qtKey) { case Qt::Key_MediaPrevious: key = XF86AudioPrev; break; case Qt::Key_MediaTogglePlayPause: key = XF86AudioPlay; break; case Qt::Key_MediaNext: key = XF86AudioNext; break; default: return; break; } //   X Display *display; display = XOpenDisplay(NULL); //    keycode = XKeysymToKeycode(display, key); //    XTestFakeKeyEvent(display, keycode, 1, 0); //    XTestFakeKeyEvent(display, keycode, 0, 0); //   X XFlush(display); //   X XCloseDisplay(display); #endif
      
      





Mac OS Xでのキーボードエミュレーション



MacOS Xのソリューションを見つけようとしても、GoogleがHacoのQtアプリケーションをMac OS Xに統合する(CocoaとObjective-C ++を使用) 。 すでにObjective-Cアプリケーションで使用するC ++コードを分離する方法を説明した英語の記事を見つけました。 Objective-Cコードを分離するために、正反対が必要でした(これは必要な機能を実行しましたが、同時にコンパイラーは誓いました)。 すべてが非常にシンプルであることが判明しました。

1. macx.mmファイルを作成し、Objective-Cコードをその中に配置しました(プロジェクトファイルに行が自動的に表示されました
 OBJECTIVE_SOURCES += macx.mm
      
      





2. macx.hファイルを作成し、その中にmacx.mmから関数宣言を配置しました(#include“ macx.h”をmacx.mmに追加)。

3.プロジェクトファイルに、特に次の必要なフレームワークの接続を追加しました。

 macx:LIBS += -framework ApplicationServices -framework IOKit
      
      





4. Mac OS Xのマクロ条件付きコンパイル内に、必要なヘッダーとmacx.hが追加されました。

5.既におなじみのスイッチケース構造に、新しく作成した関数への呼び出しを挿入しました。



したがって、ファイルの冒頭で、Mac OS Xの設計を取得しました。

 #ifdef Q_OS_MAC #include <ApplicationServices/ApplicationServices.h> //   UInt8 #include <IOKit/hidsystem/ev_keymap.h> //  #include "mac.h" //    Objective-C  #endif
      
      





次のコードがsendKeyEventToSystem関数に追加されます。

 #ifdef Q_OS_MAC switch (qtKey) { case Qt::Key_MediaPrevious: HIDPostAuxKey( NX_KEYTYPE_PREVIOUS ); break; case Qt::Key_MediaTogglePlayPause: HIDPostAuxKey( NX_KEYTYPE_PLAY ); break; case Qt::Key_MediaNext: HIDPostAuxKey( NX_KEYTYPE_NEXT ); break; default: return; break; } #endif
      
      







mac.mmファイルの内容:

 #import <Cocoa/Cocoa.h> #import <IOKit/hidsystem/IOHIDLib.h> #import <IOKit/hidsystem/ev_keymap.h> #include "macx.h" static io_connect_t get_event_driver(void) { static mach_port_t sEventDrvrRef = 0; mach_port_t masterPort, service, iter; kern_return_t kr; if (!sEventDrvrRef) { // Get master device port kr = IOMasterPort( bootstrap_port, &masterPort ); check( KERN_SUCCESS == kr); kr = IOServiceGetMatchingServices( masterPort, IOServiceMatching( kIOHIDSystemClass ), &iter ); check( KERN_SUCCESS == kr); service = IOIteratorNext( iter ); check( service ); kr = IOServiceOpen( service, mach_task_self(), kIOHIDParamConnectType, &sEventDrvrRef ); check( KERN_SUCCESS == kr ); IOObjectRelease( service ); IOObjectRelease( iter ); } return sEventDrvrRef; } void HIDPostAuxKey(const UInt8 auxKeyCode ) { NXEventData event; kern_return_t kr; IOGPoint loc = { 0, 0 }; //     UInt32 evtInfo = auxKeyCode << 16 | NX_KEYDOWN << 8; bzero(&event, sizeof(NXEventData)); event.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS; event.compound.misc.L[0] = evtInfo; kr = IOHIDPostEvent( get_event_driver(), NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE ); check( KERN_SUCCESS == kr ); //     evtInfo = auxKeyCode << 16 | NX_KEYUP << 8; bzero(&event, sizeof(NXEventData)); event.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS; event.compound.misc.L[0] = evtInfo; kr = IOHIDPostEvent( get_event_driver(), NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE ); check( KERN_SUCCESS == kr ); }
      
      





結論:


メッセージ(キーボード、マウス、その他のイベントを含む)を送信できる、オープンにアクセス可能なクロスプラットフォームライブラリがこれまで存在しなかったことは、私にとって少し奇妙でした。 いずれにせよ、そのようなライブラリは見つかりませんでした。 提示されたコードは完全にはほど遠いです(新しいキーを追加するとスイッチケースシーケンスがどのように成長するか想像できます)。 それでも、これは、クロスプラットフォームアプリケーションの作成に関する知識ベースの一般的な貯金箱への小さな貢献としましょう。

記事の作業中に、VirtualBox がマルチメディアキーのキーストロークをインターセプトすることに気付きました(Ubuntuでテストされました-すべてハードウェアで動作しました)。 WMWareにはこの欠点はありません(Mac OS Xでテスト済み)。



付録:マルチメディアキーとその定義のリスト(#defineを使用)。



Qt ::キー Linux Mac OS X
Qt :: Key_VolumeDown VK_VOLUME_DOWN XF86AudioLowerVolume NX_KEYTYPE_SOUND_DOWN
Qt :: Key_VolumeMute VK_VOLUME_MUTE XF86AudioMute NX_KEYTYPE_MUTE
Qt :: Key_VolumeUp VK_VOLUME_UP XF86AudioRaiseVolume NX_KEYTYPE_SOUND_UP
Qt :: Key_BassBoost
Qt :: Key_BassUp
Qt :: Key_BassDown
Qt :: Key_TrebleUp
Qt :: Key_TrebleDown
Qt :: Key_MediaPlay VK_MEDIA_PLAY_PAUSE XF86AudioPlay NX_KEYTYPE_PLAY
Qt :: Key_MediaStop VK_MEDIA_STOP XF86AudioStop
Qt :: Key_MediaPrevious VK_MEDIA_PREV_TRACK XF86AudioPrev NX_KEYTYPE_PREVIOUS
Qt :: Key_MediaNext VK_MEDIA_NEXT_TRACK XF86AudioNext NX_KEYTYPE_NEXT
Qt :: Key_MediaRecord
Qt :: Key_MediaPause XF86AudioPause
Qt :: Key_MediaTogglePlayPause VK_MEDIA_PLAY_PAUSE XF86AudioPlay NX_KEYTYPE_PLAY


関連リンク:

1.問題を解決するための既存のアプローチ: キーボード入力、sendkeys、send kestrokesなどをシミュレートするためのC ++(Qt)クロスプラットフォームライブラリ

2. MSDN WebサイトのSendInput関数の説明とキーボードコードのリスト

3. CおよびX11でのMediakey Pressのシミュレーション -この記事では、Linuxで「xev」ユーティリティ(Ubuntuでは「sudo apt-get install x11-utils」)を使用してキーコードを検索する方法と、archlinuxの記事を説明しています.org

4. Pythonプログラミング言語でのMacOS Xのマルチメディアキーのエミュレーション。



All Articles