Skip to content

feat: implement keyboard device management#134

Merged
fly602 merged 1 commit into
linuxdeepin:masterfrom
fly602:master
Jun 3, 2025
Merged

feat: implement keyboard device management#134
fly602 merged 1 commit into
linuxdeepin:masterfrom
fly602:master

Conversation

@fly602
Copy link
Copy Markdown
Contributor

@fly602 fly602 commented Jun 3, 2025

  • Added Keyboard struct and associated methods for creating and managing keyboard devices.
  • Introduced error handling for device creation and validation.
  • Implemented functions to enable/disable keyboards and check their status.
  • Added utility function to identify keyboard devices in the X11 environment.

Log: This enhancement improves keyboard device handling within the dxinput package.
pms: BUG-315763

Summary by Sourcery

Implement keyboard device management in the dxinput package, including device creation, validation, enable/disable controls, and X11 detection.

New Features:

  • Add Keyboard struct with constructors to create and validate keyboard devices
  • Implement methods to enable, disable, and check status of keyboards on X11 and Wayland
  • Add native is_keyboard_device function to detect keyboard devices in the X11 environment

- Added Keyboard struct and associated methods for creating and managing keyboard devices.
- Introduced error handling for device creation and validation.
- Implemented functions to enable/disable keyboards and check their status.
- Added utility function to identify keyboard devices in the X11 environment.

Log: This enhancement improves keyboard device handling within the dxinput package.
pms: BUG-315763
@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

代码审查意见:

  1. 错误处理

    • NewKeyboard函数中,当infosnil时,返回的错误信息是"no device",但这个错误信息可能不够具体,建议返回更详细的错误信息,比如"设备列表获取失败"。
    • NewKeyboardDevInfo函数中,当devnildev.Type != DevTypeKeyboard时,返回的错误信息是"not a keyboard device",但这个错误信息可能不够具体,建议返回更详细的错误信息,比如"设备类型不匹配"。
  2. 代码重复

    • NewKeyboardNewKeyboardDevInfo函数中都有类似的设备信息获取和检查逻辑,可以考虑将这部分逻辑提取到一个单独的函数中,以减少代码重复。
  3. 线程安全

    • is_keyboard_device函数中,使用了全局的互斥锁mutex来保护对X11显示的访问,这是一个好的做法。但是,建议在函数开始时立即检查display是否为NULL,并在函数结束时释放资源,以避免潜在的内存泄漏。
  4. 错误信息格式化

    • NewKeyboardDevInfo函数中,使用fmt.Errorf格式化错误信息时,建议使用%d来格式化dev.Id,而不是%v,以提高代码的可读性。
  5. 全局变量

    • globalWayland是一个全局变量,建议将其改为局部变量,以减少对全局状态的依赖,提高代码的可测试性和可维护性。
  6. 代码注释

    • is_keyboard_device函数中,有一些注释说明了代码的功能,但是缺少对函数整体功能的描述,建议添加更详细的注释,以便其他开发者更好地理解代码。
  7. 错误处理

    • is_keyboard_device函数中,当XOpenDisplay失败时,应该返回一个具体的错误信息,而不是仅仅打印错误信息并返回0。

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Jun 3, 2025

Reviewer's Guide

This PR extends the dxinput package with a dedicated Keyboard type—complete with creation, validation, enable/disable, and status-check methods—and enhances the underlying C utilities to accurately detect keyboard devices in an X11 environment.

Sequence Diagram for NewKeyboard function logic

sequenceDiagram
    participant Caller
    participant NK as "dxinput.NewKeyboard()"
    participant Utils as "utils"
    participant DevInfoList as "DeviceInfoList"
    participant NKDI as "dxinput.NewKeyboardDevInfo()"
    participant KeyboardStruct as "Keyboard (struct)"

    Caller->>NK: NewKeyboard(id)
    NK->>Utils: ListDevice()
    Utils-->>NK: infos (*DeviceInfoList)
    alt infos == nil
        NK-->>Caller: (nil, error "no device")
    end
    NK->>DevInfoList: infos.Get(id)
    DevInfoList-->>NK: info (*DeviceInfo)
    alt info == nil
        NK-->>Caller: (nil, error "invalid device id")
    end
    NK->>NKDI: NewKeyboardDevInfo(info)
    alt info == nil OR info.Type != DevTypeKeyboard
        NKDI-->>NK: (nil, error "not a keyboard device")
    else Valid DeviceInfo
        NKDI-->>KeyboardStruct: &Keyboard{Id: info.Id, Name: info.Name}
        KeyboardStruct-->>NKDI: keyboardInstance
        NKDI-->>NK: (keyboardInstance, nil)
    end
    NK-->>Caller: (*Keyboard, error)
Loading

Sequence Diagram for Keyboard.Enable() method logic

sequenceDiagram
    participant Caller
    participant K as "Keyboard instance"
    participant kwayland_mod as "kwayland module"
    participant dxinput_utils_go as "utils.enableDevice (Go)"

    Caller->>K: Enable(enabled)
    alt globalWayland is true
        K->>kwayland_mod: Enable(m.Id_formatted_for_Wayland, enabled)
        kwayland_mod-->>K: error_or_nil
    else globalWayland is false
        K->>dxinput_utils_go: enableDevice(m.Id, enabled)
        dxinput_utils_go-->>K: error_or_nil
    end
    K-->>Caller: error_or_nil
Loading

Sequence Diagram for C function is_keyboard_device()

sequenceDiagram
    participant CallingCFunction as "Calling C Code (e.g., query_device_type)"
    participant is_keyboard_device_c as "is_keyboard_device() (C function)"
    participant X11Server as "X11 Server"

    CallingCFunction->>is_keyboard_device_c: is_keyboard_device(deviceid)
    is_keyboard_device_c->>X11Server: XOpenDisplay(NULL)
    X11Server-->>is_keyboard_device_c: display*
    alt display == NULL
        is_keyboard_device_c-->>CallingCFunction: 0 (false)
    end
    is_keyboard_device_c->>X11Server: XIQueryDevice(display, deviceid, &num_devices)
    X11Server-->>is_keyboard_device_c: devices* (XIDeviceInfo)
    alt devices == NULL OR num_devices != 1
        is_keyboard_device_c->>X11Server: XIFreeDeviceInfo(devices) %% Potentially if devices not null
        is_keyboard_device_c->>X11Server: XCloseDisplay(display)
        is_keyboard_device_c-->>CallingCFunction: 0 (false)
    end
    is_keyboard_device_c->>is_keyboard_device_c: Check devices[0].use == XISlaveKeyboard
    alt devices[0].use == XISlaveKeyboard
        is_keyboard_device_c->>X11Server: XIFreeDeviceInfo(devices)
        is_keyboard_device_c->>X11Server: XCloseDisplay(display)
        is_keyboard_device_c-->>CallingCFunction: 1 (true)
    else devices[0].use != XISlaveKeyboard
        is_keyboard_device_c->>X11Server: XIFreeDeviceInfo(devices)
        is_keyboard_device_c->>X11Server: XCloseDisplay(display)
        is_keyboard_device_c-->>CallingCFunction: 0 (false)
    end
Loading

Class Diagram for the new Keyboard struct and related components

classDiagram
    class Keyboard {
      +Id: int32
      +Name: string
      +Enable(enabled bool) error
      +IsEnabled() bool
    }

    class dxinputPackage {
      <<Go Package>>
      +NewKeyboard(id int32) (*Keyboard, error)
      +NewKeyboardDevInfo(dev *DeviceInfo) (*Keyboard, error)
    }
    dxinputPackage --o Keyboard : creates

    class DeviceInfo {
      +Id: int32
      +Name: string
      +Type: DevType
    }

    class utilsPackage {
      <<Go Package>>
      +ListDevice() *DeviceInfoList
      +enableDevice(id int32, enabled bool) error
      +isDeviceEnabled(id int32) bool
    }

    class kwaylandPackage {
      <<Go Package>>
      +Enable(deviceId string, enabled bool) error
      +CanEnabled(deviceId string) bool
    }

    dxinputPackage ..> utilsPackage : uses ListDevice()
    dxinputPackage ..> DeviceInfo : uses for NewKeyboardDevInfo()
    Keyboard ..> utilsPackage : uses enableDevice(), isDeviceEnabled() (X11 path)
    Keyboard ..> kwaylandPackage : uses Enable(), CanEnabled() (Wayland path)
Loading

File-Level Changes

Change Details Files
Implement keyboard device creation and validation
  • Define Keyboard struct with Id and Name
  • Implement NewKeyboard to fetch device list and validate id
  • Add NewKeyboardDevInfo to enforce DevTypeKeyboard with descriptive errors
dxinput/keyboard.go
Add methods to enable, disable, and query keyboard status
  • Implement Enable switching between Wayland and X11 backends
  • Implement IsEnabled for status checks across environments
  • Import kwayland and common package for backend integration
dxinput/keyboard.go
Integrate keyboard detection in X11 utils
  • Introduce is_keyboard_device to identify keyboard devices via XIQueryDevice
  • Update query_device_type to return TYPE_KEYBOARD when detected
dxinput/utils/type.c

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @fly602 - I've reviewed your changes and they look great!

Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread dxinput/keyboard.go
}

type Keyboard struct {
Id int32
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Use 'ID' instead of 'Id' for Go naming consistency

Consider renaming the Id field to ID and updating all references for consistency with Go conventions.

Suggested implementation:

type Keyboard struct {
	ID   int32
	Name string
}
func NewKeyboard(id int32) (*Keyboard, error) {
	infos := utils.ListDevice()
	if infos == nil {
		return nil, errors.New("no device")

If there are any other references to the Id field of Keyboard elsewhere in this file (or in other files), they should also be updated to use ID for consistency.

Comment thread dxinput/utils/type.c

// 获取所有输入设备
XIDeviceInfo *devices = XIQueryDevice(display, deviceid, &num_devices);
if (devices == NULL || num_devices != 1) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Free devices on error path to avoid memory leak

Call XIFreeDeviceInfo(devices) before returning when num_devices != 1 to prevent a memory leak.

Comment thread dxinput/utils/type.c
}

static int
is_keyboard_device(int deviceid)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider replacing the is_keyboard_device() implementation with a call to get_xdevice_by_id() to simplify resource management and error handling.

// Replace the current implementation of is_keyboard_device() with a call to
// get_xdevice_by_id(), which already handles the X11 connection, locking,
// error‐checking and cleanup for you.

static int
is_keyboard_device(int deviceid)
{
    XIDeviceInfo *info = get_xdevice_by_id(deviceid);
    if (!info)
        return 0;

    int is_kbd = (info->use == XISlaveKeyboard);
    XIFreeDeviceInfo(info);
    return is_kbd;
}

By doing this you:

  • Eliminate per-call XOpenDisplay()/XCloseDisplay() boilerplate.
  • Reuse the shared mutex and error handling in get_xdevice_by_id().
  • Keep all existing functionality (checking use == XISlaveKeyboard).

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: caixr23, fly602

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@fly602 fly602 merged commit 9f6196d into linuxdeepin:master Jun 3, 2025
14 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants