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 ::キーLinuxMac OS X
Qt :: Key_VolumeDownVK_VOLUME_DOWNXF86AudioLowerVolumeNX_KEYTYPE_SOUND_DOWN
Qt :: Key_VolumeMuteVK_VOLUME_MUTEXF86AudioMuteNX_KEYTYPE_MUTE
Qt :: Key_VolumeUpVK_VOLUME_UPXF86AudioRaiseVolumeNX_KEYTYPE_SOUND_UP
Qt :: Key_BassBoost
Qt :: Key_BassUp
Qt :: Key_BassDown
Qt :: Key_TrebleUp
Qt :: Key_TrebleDown
Qt :: Key_MediaPlayVK_MEDIA_PLAY_PAUSEXF86AudioPlayNX_KEYTYPE_PLAY
Qt :: Key_MediaStopVK_MEDIA_STOPXF86AudioStop
Qt :: Key_MediaPreviousVK_MEDIA_PREV_TRACKXF86AudioPrevNX_KEYTYPE_PREVIOUS
Qt :: Key_MediaNextVK_MEDIA_NEXT_TRACKXF86AudioNextNX_KEYTYPE_NEXT
Qt :: Key_MediaRecord
Qt :: Key_MediaPauseXF86AudioPause
Qt :: Key_MediaTogglePlayPauseVK_MEDIA_PLAY_PAUSEXF86AudioPlayNX_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のマルチメディアキーのエミュレーション。

Source: https://habr.com/ru/post/J217779/


All Articles