web-dev-qa-db-ja.com

golangにグローバルホットキーを実装しますか?

誰かがGo(golang)でクロスプラットフォーム(Mac、Linux、Windows)のグローバルホットキーを作成したいとします。OSの任意の場所でホットキーの組み合わせを押して、ターミナルで何かが印刷されたとしましょう。

現在(2016年7月)それを行うライブラリが見つからないので、一緒に方法を見つけることができるかもしれません。

もちろん、OSごとにいくつかのネイティブOSバインディングを呼び出す必要がありますが、その方法に関する情報は非常にまばらです。

マック

グーグルから判断すると、addGlobalMonitorForEventsMatchingMaskを使用する必要があります

編集:役に立たない例が削除されました

Linux

容疑者はXGrabKeyのようですが、近くにサンプルコードはありません https://github.com/search?utf8=%E2%9C%93&q=language%3Ago+XGrabKey&type=リポジトリ&ref =検索結果

ウィンドウズ

RegisterHotKeyを使用する必要があるようですが、サンプルコードを見つけようとしても、どこにもつながりません: https://github.com/search?utf8=%E2%9C%93&q=language%3Ago + RegisterHotKey

(Javaで)調査するいくつかの興味深いクロスプラットフォームプロジェクトは https://github.com/tulskiy/jkeymaster

どんな助けでも大歓迎です!

20
Slava V

これは、単純なシステムコールを使用するほとんどのオペレーティングシステムで可能です。Goでは、パッケージ syscall を使用して、追加のCコードやcgoコンパイラなしでシステムコールを実行できます。

syscallパッケージのオンライン公式ドキュメントには、Linuxインターフェースのみが示されていることに注意してください。他のオペレーティングシステムのインターフェースはわずかに異なります。 Windowsでは、syscallパッケージには、_syscall.DLL_型、syscall.LoadDLL()およびsyscall.MustLoadDLL()関数なども含まれています。これらを表示するには、godocツールをローカルで実行します。

_godoc -http=:6060
_

これにより、_godoc.org_に類似したWebページをホストするWebサーバーが起動し、_http://localhost:6060/pkg/syscall/_に移動します。

ここでは、純粋なGoで実行可能な完全なWindowsソリューションを紹介します。完全なサンプルアプリケーションは、 Go Playground で入手できます。遊び場では実行されません。ダウンロードしてローカルで実行します(Windowsの場合)。


使用するホットキーを説明するタイプを定義しましょう。これはWindows固有のものではなく、コードをより適切にするためです。 Hotkey.String()メソッドは、_"Hotkey[Id: 1, Alt+Ctrl+O]"_などのわかりやすいホットキーの表示名を提供します。

_const (
    ModAlt = 1 << iota
    ModCtrl
    ModShift
    ModWin
)

type Hotkey struct {
    Id        int // Unique id
    Modifiers int // Mask of modifiers
    KeyCode   int // Key code, e.g. 'A'
}

// String returns a human-friendly display name of the hotkey
// such as "Hotkey[Id: 1, Alt+Ctrl+O]"
func (h *Hotkey) String() string {
    mod := &bytes.Buffer{}
    if h.Modifiers&ModAlt != 0 {
        mod.WriteString("Alt+")
    }
    if h.Modifiers&ModCtrl != 0 {
        mod.WriteString("Ctrl+")
    }
    if h.Modifiers&ModShift != 0 {
        mod.WriteString("Shift+")
    }
    if h.Modifiers&ModWin != 0 {
        mod.WriteString("Win+")
    }
    return fmt.Sprintf("Hotkey[Id: %d, %s%c]", h.Id, mod, h.KeyCode)
}
_

ウィンドウ_user32.dll_には、グローバルホットキー管理用の関数が含まれています。それをロードしましょう:

_user32 := syscall.MustLoadDLL("user32")
defer user32.Release()
_

グローバルホットキーを登録するための RegisterHotkey() 関数があります。

_reghotkey := user32.MustFindProc("RegisterHotKey")
_

これを使用して、いくつかのホットキーを登録しましょう。 ALT+CTRL+O、 ALT+SHIFT+M、 ALT+CTRL+X (アプリを終了するために使用されます):

_// Hotkeys to listen to:
keys := map[int16]*Hotkey{
    1: &Hotkey{1, ModAlt + ModCtrl, 'O'},  // ALT+CTRL+O
    2: &Hotkey{2, ModAlt + ModShift, 'M'}, // ALT+SHIFT+M
    3: &Hotkey{3, ModAlt + ModCtrl, 'X'},  // ALT+CTRL+X
}

// Register hotkeys:
for _, v := range keys {
    r1, _, err := reghotkey.Call(
        0, uintptr(v.Id), uintptr(v.Modifiers), uintptr(v.KeyCode))
    if r1 == 1 {
        fmt.Println("Registered", v)
    } else {
        fmt.Println("Failed to register", v, ", error:", err)
    }
}
_

これらのホットキーを押すイベントを「聞く」方法が必要です。このため、_user32.dll_には PeekMessage() 関数が含まれています。

_peekmsg := user32.MustFindProc("PeekMessageW")
_

PeekMessage()はメッセージを MSG 構造体に格納します。定義しましょう:

_type MSG struct {
    HWND   uintptr
    UINT   uintptr
    WPARAM int16
    LPARAM int64
    DWORD  int32
    POINT  struct{ X, Y int64 }
}
_

これが、グローバルキーの押下をリッスンして処理するリスニングループです(ここでは、押されたホットキーをコンソールに出力するだけです。 CTRL+ALT+X が押されたら、アプリを終了します):

_for {
    var msg = &MSG{}
    peekmsg.Call(uintptr(unsafe.Pointer(msg)), 0, 0, 0, 1)

    // Registered id is in the WPARAM field:
    if id := msg.WPARAM; id != 0 {
        fmt.Println("Hotkey pressed:", keys[id])
        if id == 3 { // CTRL+ALT+X = Exit
            fmt.Println("CTRL+ALT+X pressed, goodbye...")
            return
        }
    }

    time.Sleep(time.Millisecond * 50)
}
_

これで完了です。

上記のアプリケーションを起動すると、次のように出力されます。

_Registered Hotkey[Id: 1, Alt+Ctrl+O]
Registered Hotkey[Id: 2, Alt+Shift+M]
Registered Hotkey[Id: 3, Alt+Ctrl+X]
_

次に、登録済みのホットキーのいくつかを押してみましょう(アプリにフォーカスがあり、必ずしもアプリである必要はありません)。コンソールに次のように表示されます。

_Hotkey pressed: Hotkey[Id: 1, Alt+Ctrl+O]
Hotkey pressed: Hotkey[Id: 1, Alt+Ctrl+O]
Hotkey pressed: Hotkey[Id: 2, Alt+Shift+M]
Hotkey pressed: Hotkey[Id: 3, Alt+Ctrl+X]
CTRL+ALT+X pressed, goodbye...
_
14
icza

それはできます。たとえば、ゲームエンジンAzul3dはそれを行うことができます https://github.com/azul3d/engine/tree/master/keyboard 。 AppDelegate.hファイルがないため、OSXスニペットはコンパイルされません。手作業で作成するか、最初に簿記を行うXCodeを使用してプロジェクトをコンパイルしてから、AppDelegate.hとAppDelegate.mを確認することができます。 Linuxでキーストロークストリームをログに記録するには、ここで実行される/ dev/input/event $ IDファイルを読み取ることができます https://github.com/MarinX/keylogger 。 Windowsでは、このコードのようないくつかのシステムコールが必要になります https://github.com/eiannone/keyboard 。 nixとwinの両方で、cgoは必要なく、syscallと純粋なGoに満足できます。

2
Uvelichitel