【问题标题】:Read media keys from Go program从 Go 程序中读取媒体密钥
【发布时间】:2017-08-05 20:23:59
【问题描述】:

我正在编写一个媒体跨平台分布式媒体播放器,用于我自己的网络。

当前版本有三/四部分:

  1. 保存音频文件的 NAS。
  2. 元数据服务器保存有关文件的信息。
  3. 一个 HTML/JS 客户端,允许操作元数据服务器和排队媒体:
  4. 玩家恶魔。

我的问题在于第 4 部分。播放器没有用户界面,也不需要用户界面。它将通过来自客户端的网络命令和侦听其当前主机上的媒体密钥来控制。

播放器守护进程需要同时在 Windows 和 Linux 上运行,但我似乎无法找到一种方法(任何方法)在任一操作系统上读取这些密钥。我所知道的大多数读取键盘的方法根本不会读取这些键。

【问题讨论】:

  • 您可能希望从键盘记录器之类的东西开始搜索。这是一个适用于linux的包。类似的逻辑可以用于macsgithub.com/gearmover/keylogger。 Windows超出了我的知识范围,但我确信在堆栈上进行一些谷歌搜索和搜索会产生结果。 play.golang.org/p/wHJ-8D2nKT
  • 谢谢,我从没想过要检查键盘记录器......你链接的那个有足够的信息我可以让 Linux 工作,现在我只需要一些适用于 Windows 的东西......(有趣的是,链接的键盘记录器产生媒体密钥的未知扫描码错误,但这只是因为它的映射不完整。)
  • 是的,可能是映射问题。您可能已经在 windows 上遇到过这个,但如果没有,这里是 windows 上的一个。 github.com/SaturnsVoid/Windows-KeyLogger
  • 我现在感觉自己像个白痴。我真正知道的一个 Windows 键盘轮询功能会做我想做的事......出于某种原因,我认为它更受限制......哦,好吧。无论如何,如果在我完成这项工作时没有人回答,我会写一个自我回答,归功于某些有用的评论者:)

标签: linux windows go


【解决方案1】:

在几位评论者的帮助下,我现在已经弄清楚了。

Linux版本如下:

package main

import (
    “bytes”
    “encoding/binary”
    “fmt”
    “os”
    “os/exec”
    “syscall”
)

// parses through the /proc/bus/input/devices file for keyboard devices.
// Copied from `github.com/gearmover/keylogger` with trivial modification.
func dumpDevices() ([]string, error) {
    cmd := exec.Command(“/bin/sh”, “-c”, “/bin/grep -E ‘Handlers|EV=’ /proc/bus/input/devices | /bin/grep -B1 ‘EV=120013’ | /bin/grep -Eo ‘event[0-9]+’”)

    output, err := cmd.Output()
    if err != nil {
        return nil, err
    }

    buf := bytes.NewBuffer(output)

    var devices []string

    for line, err := buf.ReadString(‘\n’); err == nil; {
        devices = append(devices, “/dev/input/”+line[:len(line)-1])

        line, err = buf.ReadString(‘\n’)
    }

    return devices, nil
}

// Using MS names, just because I don’t feel like looking up the Linux versions.
var keys = map[uint16]string{
    0xa3: “VK_MEDIA_NEXT_TRACK”,
    0xa5: “VK_MEDIA_PREV_TRACK”,
    0xa6: “VK_MEDIA_STOP”,
    0xa4: “VK_MEDIA_PLAY_PAUSE”,
}

// Most of the code here comes from `github.com/gearmover/keylogger`.
func main() {
    // drop privileges when executing other programs
    syscall.Setgid(65534)
    syscall.Setuid(65534)

    // dump our keyboard devices from /proc/bus/input/devices
    devices, err := dumpDevices()
    if err != nil {
        fmt.Println(err)
    }
    if len(devices) == 0 {
        fmt.Println(“No input devices found”)
        return
    }

    // bring back our root privs
    syscall.Setgid(0)
    syscall.Setuid(0)

    // Open the first keyboard device.
    input, err := os.OpenFile(devices[0], os.O_RDONLY, 0600)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer input.Close()

    // Log media keys
    var buffer = make([]byte, 24)
    for {
        // read the input events as they come in
        n, err := input.Read(buffer)
        if err != nil {
            return
        }

        if n != 24 {
            fmt.Println(“Weird Input Event Size: “, n)
            continue
        }

        // parse the input event according to the <linux/input.h> header struct
        binary.LittleEndian.Uint64(buffer[0:8]) // Time stamp stuff I could care less about
        binary.LittleEndian.Uint64(buffer[8:16])
        etype := binary.LittleEndian.Uint16(buffer[16:18])        // Event Type. Always 1 for keyboard events
        code := binary.LittleEndian.Uint16(buffer[18:20])         // Key scan code
        value := int32(binary.LittleEndian.Uint32(buffer[20:24])) // press(1), release(0), or repeat(2)

        if etype == 1 && value == 1 && keys[code] != “” {
            // In a real application I would send a message here.
            fmt.Println(keys[code])
        }
    }
}

Windows 版本:

package main

import (
    “fmt”
    “syscall”
    “time”
)

var user32 = syscall.NewLazyDLL(“user32.dll”)
var procGAKS = user32.NewProc(“GetAsyncKeyState”)

// Key codes from MSDN
var keys = [4]uint{
    0xb0, // VK_MEDIA_NEXT_TRACK
    0xb1, // VK_MEDIA_PREV_TRACK
    0xb2, // VK_MEDIA_STOP
    0xb3, // VK_MEDIA_PLAY_PAUSE
}

var names = [4]string{
    “VK_MEDIA_NEXT_TRACK”,
    “VK_MEDIA_PREV_TRACK”,
    “VK_MEDIA_STOP”,
    “VK_MEDIA_PLAY_PAUSE”,
}

func main() {
    fmt.Println(“Running…”)

    // Since I don’t want to trigger dozens of times for each key I need to track state.
    // I could check the bits of GAKS’ return value, but that is not reliable.
    down := [4]bool{false, false, false, false}

    for {
        time.Sleep(1 * time.Millisecond)
        for i, key := range keys {
            // val is not a simple boolean!
            // 0 means “not pressed” (also certain errors)
            // If LSB is set the key was just pressed (this may not be reliable)
            // If MSB is set the key is currently down.
            val, _, _ := procGAKS.Call(uintptr(key))

            // Turn a press into a transition and track key state.
            goingdown := false
            if int(val) != 0 && !down[i] {
                goingdown = true
                down[i] = true
            }
            if int(val) == 0 && down[i] {
                down[i] = false
            }
            if goingdown {
                // In a real application I would send a message here.
                fmt.Println(names[i])
            }
        }
    }
}

唯一的“问题”是 Linux 版本必须以 root 身份运行。对我来说,这不是问题。如果以root身份运行是一个问题,我认为有一种涉及X11的方法......

【讨论】:

    猜你喜欢
    • 2023-03-19
    • 2014-11-15
    • 2010-10-05
    • 2011-11-04
    • 2021-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多