Skip to content

Commit

Permalink
all: support keyup event (darwin)
Browse files Browse the repository at this point in the history
Fixes #9
  • Loading branch information
changkun committed Jan 4, 2022
1 parent b4e6fbb commit b950a6e
Show file tree
Hide file tree
Showing 14 changed files with 752 additions and 95 deletions.
45 changes: 43 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,51 @@ import "golang.design/x/hotkey"

## API Usage

Package hotkey provides the primary facility to register a system-level global hotkey shortcut to notify an application if a user triggers the desired hotkey. A hotkey must be a combination of modifiers and a single key.
Package hotkey provides the basic facility to register a system-level
global hotkey shortcut so that an application can be notified if a user
triggers the desired hotkey. A hotkey must be a combination of modifiers
and a single key.

Note a platform-specific detail on `macOS` due to the OS restriction (other platforms do not have this restriction): hotkey events must be handled on the "main thread". Therefore, to use this package properly, one must call the `(*Hotkey).Register` method on the main thread, and an OS app main event loop must be established. One can use the provided `golang.design/x/hotkey/mainthread` for self-contained applications. For applications based on other GUI frameworks, one has to use their provided ability to run the `(*Hotkey).Register` on the main thread. See the [examples](./examples) folder for more examples.
```go
func main() { mainthread.Init(fn) } // not necessary when use in Fyne, Ebiten or Gio.
func fn() {
hk := hotkey.New([]hotkey.Modifier{hotkey.ModCtrl, hotkey.ModShift}, hotkey.KeyS)
err := hk.Register()
if err != nil {
return
}
fmt.Printf("hotkey: %v is registered\n", hk)
<-hk.Keydown()
fmt.Printf("hotkey: %v is down\n", hk)
<-hk.Keyup()
fmt.Printf("hotkey: %v is up\n", hk)
hk.Unregister()
fmt.Printf("hotkey: %v is unregistered\n", hk)
}
```

Note platform specific details:

- On macOS, due to the OS restriction (other
platforms does not have this restriction), hotkey events must be handled
on the "main thread". Therefore, in order to use this package properly,
one must start an OS main event loop on the main thread, For self-contained
applications, using [golang.design/x/hotkey/mainthread](https://pkg.go.dev/golang.design/x/hotkey/mainthread) is possible.
For applications based on other GUI frameworks, such as fyne, ebiten, or Gio.
This is not necessary. See the "[./examples](./examples)" folder for more examples.
- On Linux (X11), when AutoRepeat is enabled in the X server, the Keyup
is triggered automatically and continuously as Keydown continues.

## Examples

| Description | Folder |
|:------------|:------:|
| A minimum example | [minimum](./examples/minimum/main.go) |
| Register multiple hotkeys | [multiple](./examples/multiple/main.go) |
| A example to use in GLFW | [glfw](./examples/glfw/main.go) |
| A example to use in Fyne | [fyne](./examples/fyne/main.go) |
| A example to use in Ebiten | [ebiten](./examples/ebiten/main.go) |
| A example to use in Gio | [gio](./examples/gio/main.go) |
## Who is using this package?

The main purpose of building this package is to support the
Expand Down
4 changes: 0 additions & 4 deletions examples/README.md

This file was deleted.

3 changes: 1 addition & 2 deletions examples/ebiten/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ func reghk() {
// Register a desired hotkey.
hk := hotkey.New([]hotkey.Modifier{hotkey.ModCtrl, hotkey.ModShift}, hotkey.KeyS)

var err error
if ebiten.RunOnMainThread(func() { err = hk.Register() }); err != nil {
if err := hk.Register(); err != nil {
log.Println("failed to register hotkey: %v", err)
return
}
Expand Down
34 changes: 34 additions & 0 deletions examples/fyne/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2022 The golang.design Initiative Authors.
// All rights reserved. Use of this source code is governed
// by a MIT license that can be found in the LICENSE file.

package main

import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"golang.design/x/hotkey"
)

func main() {
w := app.New().NewWindow("golang.design/x/hotkey")
label := widget.NewLabel("Hello golang.design!")
button := widget.NewButton("Hi!", func() { label.SetText("Welcome :)") })
w.SetContent(container.NewVBox(label, button))

go func() {
// Register a desired hotkey.
hk := hotkey.New([]hotkey.Modifier{hotkey.ModCtrl, hotkey.ModShift}, hotkey.KeyS)
if err := hk.Register(); err != nil {
panic("hotkey registration failed")
}
// Start listen hotkey event whenever it is ready.
for range hk.Keydown() {
button.Tapped(&fyne.PointEvent{})
}
}()

w.ShowAndRun()
}
43 changes: 43 additions & 0 deletions examples/gio/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2022 The golang.design Initiative Authors.
// All rights reserved. Use of this source code is governed
// by a MIT license that can be found in the LICENSE file.

package main

import (
"os"

"gioui.org/app"
"gioui.org/unit"
"golang.design/x/hotkey"
)

func main() {
go fn()
app.Main()
}

func fn() {
w := app.NewWindow(app.Title("golang.design/x/hotkey"), app.Size(unit.Dp(200), unit.Dp(200)))

go reghk()

for range w.Events() {
}
}

func reghk() {
// Register a desired hotkey.
hk := hotkey.New([]hotkey.Modifier{hotkey.ModCtrl, hotkey.ModShift}, hotkey.KeyS)
if err := hk.Register(); err != nil {
panic("hotkey registration failed")
}

// Unregister the hotkey when keydown event is triggered
for range hk.Keydown() {
hk.Unregister()
}

// Exist the app.
os.Exit(0)
}
47 changes: 47 additions & 0 deletions examples/glfw/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2022 The golang.design Initiative Authors.
// All rights reserved. Use of this source code is governed
// by a MIT license that can be found in the LICENSE file.

package main

import (
"fmt"
"runtime"

"github.com/go-gl/glfw/v3.3/glfw"
"golang.design/x/hotkey"
)

func init() {
runtime.LockOSThread()
}

func main() {
err := glfw.Init()
if err != nil {
panic(err)
}
defer glfw.Terminate()
window, err := glfw.CreateWindow(640, 480, "golang.design/x/hotkey", nil, nil)
if err != nil {
panic(err)
}

go func() {
// Register a desired hotkey.
hk := hotkey.New([]hotkey.Modifier{hotkey.ModCtrl, hotkey.ModShift}, hotkey.KeyS)
if err := hk.Register(); err != nil {
panic("hotkey registration failed")
}
// Start listen hotkey event whenever it is ready.
for range hk.Keydown() {
fmt.Println("keydown!")
}
}()

window.MakeContextCurrent()
for !window.ShouldClose() {
window.SwapBuffers()
glfw.PollEvents()
}
}
22 changes: 20 additions & 2 deletions examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,37 @@ module golang.design/x/hotkey/examples
go 1.17

require (
fyne.io/fyne/v2 v2.1.2
gioui.org v0.0.0-20220104172405-7751d737403e
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec
github.com/hajimehoshi/ebiten/v2 v2.3.0-alpha.4.0.20220103082943-8418c4a65551
golang.design/x/hotkey v0.2.1
)

require (
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec // indirect
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 // indirect
gioui.org/shader v1.0.6 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f // indirect
github.com/godbus/dbus/v5 v5.0.4 // indirect
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect
github.com/stretchr/testify v1.7.0 // indirect
github.com/yuin/goldmark v1.4.0 // indirect
golang.design/x/mainthread v0.3.0 // indirect
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
golang.org/x/exp v0.0.0-20210722180016-6781d3edade3 // indirect
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5 // indirect
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 // indirect
golang.org/x/text v0.3.6 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)

replace golang.design/x/hotkey => ../
Loading

0 comments on commit b950a6e

Please sign in to comment.