diff --git a/.github/workflows/check-go-task.yml b/.github/workflows/check-go-task.yml new file mode 100644 index 0000000..b2a6771 --- /dev/null +++ b/.github/workflows/check-go-task.yml @@ -0,0 +1,223 @@ +# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-go-task.md +name: Check Go + +env: + # See: https://github.com/actions/setup-go/tree/main#supported-version-syntax + GO_VERSION: "1.19" + +# See: https://docs.github.com/actions/using-workflows/events-that-trigger-workflows +on: + create: + push: + paths: + - ".github/workflows/check-go-task.ya?ml" + - "Taskfile.ya?ml" + - "**/go.mod" + - "**/go.sum" + - "**.go" + pull_request: + paths: + - ".github/workflows/check-go-task.ya?ml" + - "Taskfile.ya?ml" + - "**/go.mod" + - "**/go.sum" + - "**.go" + schedule: + # Run periodically to catch breakage caused by external changes. + - cron: "0 7 * * WED" + workflow_dispatch: + repository_dispatch: + +jobs: + run-determination: + runs-on: ubuntu-latest + outputs: + result: ${{ steps.determination.outputs.result }} + steps: + - name: Determine if the rest of the workflow should run + id: determination + run: | + RELEASE_BRANCH_REGEX="refs/heads/[0-9]+.[0-9]+.x" + # The `create` event trigger doesn't support `branches` filters, so it's necessary to use Bash instead. + if [[ + "${{ github.event_name }}" != "create" || + "${{ github.ref }}" =~ $RELEASE_BRANCH_REGEX + ]]; then + # Run the other jobs. + RESULT="true" + else + # There is no need to run the other jobs. + RESULT="false" + fi + + echo "result=$RESULT" >> $GITHUB_OUTPUT + + check-errors: + name: check-errors (${{ matrix.module.path }}) + needs: run-determination + if: needs.run-determination.outputs.result == 'true' + runs-on: windows-latest + + strategy: + fail-fast: false + + matrix: + module: + - path: ./ + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Install Task + uses: arduino/setup-task@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + version: 3.x + + - name: Check for errors + env: + GO_MODULE_PATH: ${{ matrix.module.path }} + run: task go:vet + + check-outdated: + name: check-outdated (${{ matrix.module.path }}) + needs: run-determination + if: needs.run-determination.outputs.result == 'true' + runs-on: windows-latest + + strategy: + fail-fast: false + + matrix: + module: + - path: ./ + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Install Task + uses: arduino/setup-task@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + version: 3.x + + - name: Modernize usages of outdated APIs + env: + GO_MODULE_PATH: ${{ matrix.module.path }} + run: task go:fix + + - name: Check if any fixes were needed + run: git diff --color --exit-code + + check-style: + name: check-style (${{ matrix.module.path }}) + needs: run-determination + if: needs.run-determination.outputs.result == 'true' + runs-on: windows-latest + + strategy: + fail-fast: false + + matrix: + module: + - path: ./ + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Install Task + uses: arduino/setup-task@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + version: 3.x + + - name: Install golint + run: go install golang.org/x/lint/golint@latest + + - name: Check style + env: + GO_MODULE_PATH: ${{ matrix.module.path }} + run: task --silent go:lint + + check-formatting: + name: check-formatting (${{ matrix.module.path }}) + needs: run-determination + if: needs.run-determination.outputs.result == 'true' + runs-on: windows-latest + + strategy: + fail-fast: false + + matrix: + module: + - path: ./ + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Install Task + uses: arduino/setup-task@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + version: 3.x + + - name: Format code + env: + GO_MODULE_PATH: ${{ matrix.module.path }} + run: task go:format + + - name: Check formatting + run: git diff --color --exit-code + + check-config: + name: check-config (${{ matrix.module.path }}) + needs: run-determination + if: needs.run-determination.outputs.result == 'true' + runs-on: windows-latest + + strategy: + fail-fast: false + + matrix: + module: + - path: ./ + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Run go mod tidy + working-directory: ${{ matrix.module.path }} + run: go mod tidy + + - name: Check whether any tidying was needed + run: git diff --color --exit-code diff --git a/README.md b/README.md index 3de689e..533ac0c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Check Taskfiles status](https://github.com/arduino/go-win32-utils/actions/workflows/check-taskfiles.yml/badge.svg)](https://github.com/arduino/go-win32-utils/actions/workflows/check-taskfiles.yml) [![Test Go status](https://github.com/arduino/go-win32-utils/actions/workflows/test-go-task.yml/badge.svg)](https://github.com/arduino/go-win32-utils/actions/workflows/test-go-task.yml) [![Codecov](https://codecov.io/gh/arduino/go-win32-utils/branch/main/graph/badge.svg)](https://codecov.io/gh/arduino/go-win32-utils) +[![Check Go status](https://github.com/arduino/go-win32-utils/actions/workflows/check-go-task.yml/badge.svg)](https://github.com/arduino/go-win32-utils/actions/workflows/check-go-task.yml) This library contains some useful calls to win32 API that are not available on the standard golang library. diff --git a/Taskfile.yml b/Taskfile.yml index 147002a..336ce9f 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -117,3 +117,46 @@ tasks: -coverprofile=coverage_unit.txt \ {{.TEST_LDFLAGS}} \ {{default .DEFAULT_GO_PACKAGES .GO_PACKAGES}} + + # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-go-task/Taskfile.yml + go:fix: + desc: Modernize usages of outdated APIs + dir: "{{default .DEFAULT_GO_MODULE_PATH .GO_MODULE_PATH}}" + cmds: + - go fix {{default .DEFAULT_GO_PACKAGES .GO_PACKAGES}} + + # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-go-task/Taskfile.yml + go:format: + desc: Format Go code + dir: "{{default .DEFAULT_GO_MODULE_PATH .GO_MODULE_PATH}}" + cmds: + - go fmt {{default .DEFAULT_GO_PACKAGES .GO_PACKAGES}} + + # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-go-task/Taskfile.yml + go:lint: + desc: Lint Go code + dir: "{{default .DEFAULT_GO_MODULE_PATH .GO_MODULE_PATH}}" + cmds: + - | + if ! which golint &>/dev/null; then + echo "golint not installed or not in PATH. Please install: https://github.com/golang/lint#installation" + exit 1 + fi + - | + golint \ + {{default "-min_confidence 0.8 -set_exit_status" .GO_LINT_FLAGS}} \ + {{default .DEFAULT_GO_PACKAGES .GO_PACKAGES}} + + # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-go-task/Taskfile.yml + go:vet: + desc: Check for errors in Go code + dir: "{{default .DEFAULT_GO_MODULE_PATH .GO_MODULE_PATH}}" + cmds: + - go vet {{default .DEFAULT_GO_PACKAGES .GO_PACKAGES}} + + # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/go-task/Taskfile.yml + go:build: + desc: Build the Go code + dir: "{{.DEFAULT_GO_MODULE_PATH}}" + cmds: + - go build -v {{.LDFLAGS}} diff --git a/devicenotification/devicenotification.go b/devicenotification/devicenotification.go index ccb3e98..48c3cf1 100644 --- a/devicenotification/devicenotification.go +++ b/devicenotification/devicenotification.go @@ -42,7 +42,7 @@ import ( "golang.org/x/sys/windows" ) -var osThreadId atomic.Uint32 +var osThreadID atomic.Uint32 // Start the device add/remove notification process, at every event a call to eventCB will be performed. // This function will block until interrupted by the given context. Errors will be passed to errorCB. @@ -50,7 +50,7 @@ var osThreadId atomic.Uint32 func Start(ctx context.Context, eventCB func(), errorCB func(msg string)) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - osThreadId.Store(windows.GetCurrentThreadId()) + osThreadID.Store(windows.GetCurrentThreadId()) eventsChan := make(chan bool, 1) var eventsChanLock sync.Mutex @@ -108,24 +108,24 @@ func Start(ctx context.Context, eventCB func(), errorCB func(msg string)) error go func() { <-ctx.Done() - _ = win32.PostMessage(windowHandle, win32.WM_Quit, 0, 0) + _ = win32.PostMessage(windowHandle, win32.WMQuit, 0, 0) }() for { // Verify running thread prerequisites - if currThreadId := windows.GetCurrentThreadId(); currThreadId != osThreadId.Load() { - panic(fmt.Sprintf("this function must run on the main OS Thread: currThread=%d, osThread=%d", currThreadId, osThreadId)) + if currThreadID := windows.GetCurrentThreadId(); currThreadID != osThreadID.Load() { + panic(fmt.Sprintf("this function must run on the main OS Thread: currThread=%d, osThread=%d", currThreadID, osThreadID.Load())) } var m win32.TagMSG - if res := win32.GetMessage(&m, windowHandle, win32.WM_Quit, win32.WM_Quit); res == 0 { // 0 means we got a WM_QUIT + if res := win32.GetMessage(&m, windowHandle, win32.WMQuit, win32.WMQuit); res == 0 { // 0 means we got a WMQUIT break } else if res == -1 { // -1 means that an error occurred err := windows.GetLastError() errorCB("error consuming messages: " + err.Error()) break } else { - // we got a message != WM_Quit, it should not happen but, just in case... + // we got a message != WMQuit, it should not happen but, just in case... win32.TranslateMessage(&m) win32.DispatchMessage(&m) } @@ -136,8 +136,8 @@ func Start(ctx context.Context, eventCB func(), errorCB func(msg string)) error func createWindow(windowCallback win32.WindowProcCallback) (syscall.Handle, *byte, error) { // Verify running thread prerequisites - if currThreadId := windows.GetCurrentThreadId(); currThreadId != osThreadId.Load() { - panic(fmt.Sprintf("this function must run on the main OS Thread: currThread=%d, osThread=%d", currThreadId, osThreadId.Load())) + if currThreadID := windows.GetCurrentThreadId(); currThreadID != osThreadID.Load() { + panic(fmt.Sprintf("this function must run on the main OS Thread: currThread=%d, osThread=%d", currThreadID, osThreadID.Load())) } moduleHandle, err := win32.GetModuleHandle(nil) @@ -167,8 +167,8 @@ func createWindow(windowCallback win32.WindowProcCallback) (syscall.Handle, *byt func destroyWindow(windowHandle syscall.Handle, className *byte) error { // Verify running thread prerequisites - if currThreadId := windows.GetCurrentThreadId(); currThreadId != osThreadId.Load() { - panic(fmt.Sprintf("this function must run on the main OS Thread: currThread=%d, osThread=%d", currThreadId, osThreadId.Load())) + if currThreadID := windows.GetCurrentThreadId(); currThreadID != osThreadID.Load() { + panic(fmt.Sprintf("this function must run on the main OS Thread: currThread=%d, osThread=%d", currThreadID, osThreadID.Load())) } if err := win32.DestroyWindowEx(windowHandle); err != nil { @@ -182,8 +182,8 @@ func destroyWindow(windowHandle syscall.Handle, className *byte) error { func registerNotifications(windowHandle syscall.Handle) (syscall.Handle, error) { // Verify running thread prerequisites - if currThreadId := windows.GetCurrentThreadId(); currThreadId != osThreadId.Load() { - panic(fmt.Sprintf("this function must run on the main OS Thread: currThread=%d, osThread=%d", currThreadId, osThreadId.Load())) + if currThreadID := windows.GetCurrentThreadId(); currThreadID != osThreadID.Load() { + panic(fmt.Sprintf("this function must run on the main OS Thread: currThread=%d, osThread=%d", currThreadID, osThreadID.Load())) } notificationFilter := win32.DevBroadcastDeviceInterface{ @@ -203,8 +203,8 @@ func registerNotifications(windowHandle syscall.Handle) (syscall.Handle, error) func unregisterNotifications(notificationsDevHandle syscall.Handle) error { // Verify running thread prerequisites - if currThreadId := windows.GetCurrentThreadId(); currThreadId != osThreadId.Load() { - panic(fmt.Sprintf("this function must run on the main OS Thread: currThread=%d, osThread=%d", currThreadId, osThreadId.Load())) + if currThreadID := windows.GetCurrentThreadId(); currThreadID != osThreadID.Load() { + panic(fmt.Sprintf("this function must run on the main OS Thread: currThread=%d, osThread=%d", currThreadID, osThreadID.Load())) } if err := win32.UnregisterDeviceNotification(notificationsDevHandle); err != nil { diff --git a/doc.go b/doc.go index 3f6a07e..a94fe09 100644 --- a/doc.go +++ b/doc.go @@ -27,6 +27,6 @@ * the GNU General Public License. */ -// win32 is a collection of useful bindings to Win32 API that are not available in the standard +// Package win32 is a collection of useful bindings to Win32 API that are not available in the standard // golang windows/syscall package. package win32 diff --git a/syscall_windows.go b/syscall_windows.go index b1b4bd5..41b85a9 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -234,6 +234,7 @@ const csidlSystemX86 = 41 const csidlTemplates = 21 const csidlWindows = 36 +// WndClass FIXMEDOCS type WndClass struct { Style uint32 WndProc uintptr @@ -247,11 +248,13 @@ type WndClass struct { ClassName *byte } +// Point FIXMEDOCS type Point struct { X int32 Y int32 } +// TagMSG FIXMEDOCS type TagMSG struct { Hwnd syscall.Handle Message uint32 @@ -262,47 +265,71 @@ type TagMSG struct { LPrivate int32 } -const WM_Quit = 0x0012 +// WMQuit FIXMEDOCS +const WMQuit = 0x0012 -const WsExDlgModalFrame = 0x00000001 -const WsExTopmost = 0x00000008 -const WsExTransparent = 0x00000020 -const WsExMDIChild = 0x00000040 -const WsExToolWindow = 0x00000080 -const WsExAppWindow = 0x00040000 -const WsExLayered = 0x00080000 +const ( + // WsExDlgModalFrame FIXMEDOCS + WsExDlgModalFrame = 0x00000001 + // WsExTopmost FIXMEDOCS + WsExTopmost = 0x00000008 + // WsExTransparent FIXMEDOCS + WsExTransparent = 0x00000020 + // WsExMDIChild FIXMEDOCS + WsExMDIChild = 0x00000040 + // WsExToolWindow FIXMEDOCS + WsExToolWindow = 0x00000080 + // WsExAppWindow FIXMEDOCS + WsExAppWindow = 0x00040000 + // WsExLayered FIXMEDOCS + WsExLayered = 0x00080000 +) -type Guid struct { +// GUID FIXEMEDOCS +type GUID struct { Data1 uint32 Data2 uint16 Data3 uint16 Data4 [8]byte } +// DevBroadcastDeviceInterface FIXMEDOCS type DevBroadcastDeviceInterface struct { DwSize uint32 DwDeviceType uint32 DwReserved uint32 - ClassGUID Guid + ClassGUID GUID SzName uint16 } -// USB devices GUID used to filter notifications -var UsbEventGUID Guid = Guid{ +// UsbEventGUID is USB devices GUID used to filter notifications +var UsbEventGUID GUID = GUID{ Data1: 0x10bfdca5, Data2: 0x3065, Data3: 0xd211, Data4: [8]byte{0x90, 0x1f, 0x00, 0xc0, 0x4f, 0xb9, 0x51, 0xed}, } -const DeviceNotifyWindowHandle = 0 -const DeviceNotifySserviceHandle = 1 -const DeviceNotifyAllInterfaceClasses = 4 +const ( + // DeviceNotifyWindowHandle FIXMEDOCS + DeviceNotifyWindowHandle = 0 + // DeviceNotifySserviceHandle FIXMEDOCS + DeviceNotifySserviceHandle = 1 + // DeviceNotifyAllInterfaceClasses FIXMEDOCS + DeviceNotifyAllInterfaceClasses = 4 +) +// DbtDevtypeDeviceInterface FIXMEDOCS const DbtDevtypeDeviceInterface = 5 -const PM_NoRemove = 0x0000 -const PM_Remove = 0x0001 -const PM_NoYield = 0x0002 +const ( + // PMNoRemove FIXMEDOCS + PMNoRemove = 0x0000 + // PMRemove FIXMEDOCS + PMRemove = 0x0001 + // PMNoYield FIXMEDOCS + PMNoYield = 0x0002 +) +// WindowProcCallback FIXMEDOCS type WindowProcCallback func(hwnd syscall.Handle, msg uint32, wParam uintptr, lParam uintptr) uintptr