Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 49 additions & 14 deletions dxinput/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,43 +190,78 @@ func setTransformationMatrix(id int32, m [9]float32) error {
}

func getInt8Prop(id int32, prop string, nitems int32) ([]int8, error) {
datas, num := utils.GetProperty(id, prop)
if len(datas) == 0 || num != nitems {
return nil, fmt.Errorf("Get prop '%v -- %s' values failed",
datas, nBytes := utils.GetProperty(id, prop)
if len(datas) == 0 {
return nil, fmt.Errorf("Get prop '%v -- %s' failed: property data is empty",
id, prop)
}
// For int8, nBytes should equal nitems (1 byte per item)
if nBytes != nitems {
return nil, fmt.Errorf("Get prop '%v -- %s' failed: expected %d items but got %d bytes",
id, prop, nitems, nBytes)
}

return utils.ReadInt8(datas, nitems), nil
}

func getInt16Prop(id int32, prop string, nitems int32) ([]int16, error) {
datas, num := utils.GetProperty(id, prop)
if len(datas) == 0 || num != nitems {
return nil, fmt.Errorf("Get prop '%v -- %s' values failed",
datas, nBytes := utils.GetProperty(id, prop)
if len(datas) == 0 {
return nil, fmt.Errorf("Get prop '%v -- %s' failed: property data is empty",
id, prop)
}
// Check if nBytes is divisible by 2 (int16 size)
if nBytes%2 != 0 {
return nil, fmt.Errorf("Get prop '%v -- %s' failed: byte count %d is not divisible by 2 (int16 size)",
id, prop, nBytes)
}
// For int16, nBytes should equal nitems * 2 (2 bytes per item)
if nBytes/2 != nitems {
return nil, fmt.Errorf("Get prop '%v -- %s' failed: expected %d items but got %d bytes (%d items)",
id, prop, nitems, nBytes, nBytes/2)
}

return utils.ReadInt16(datas, nitems), nil
}

func getInt32Prop(id int32, prop string, nitems int32) ([]int32, error) {
datas, num := utils.GetProperty(id, prop)
if len(datas) == 0 || num != nitems {
return nil, fmt.Errorf("Get prop '%v -- %s' values failed",
datas, nBytes := utils.GetProperty(id, prop)
if len(datas) == 0 {
return nil, fmt.Errorf("Get prop '%v -- %s' failed: property data is empty",
id, prop)
}
// Check if nBytes is divisible by 4 (int32 size)
if nBytes%4 != 0 {
return nil, fmt.Errorf("Get prop '%v -- %s' failed: byte count %d is not divisible by 4 (int32 size)",
id, prop, nBytes)
}
// For int32, nBytes should equal nitems * 4 (4 bytes per item)
if nBytes/4 != nitems {
return nil, fmt.Errorf("Get prop '%v -- %s' failed: expected %d items but got %d bytes (%d items)",
id, prop, nitems, nBytes, nBytes/4)
}

return utils.ReadInt32(datas, nitems), nil
}

func getFloat32Prop(id int32, prop string, nitems int32) ([]float32, error) {
datas, num := utils.GetProperty(id, prop)
if len(datas) == 0 || num != nitems {
return nil, fmt.Errorf("Get prop '%v -- %s' values failed",
func getFloat32Prop(id int32, prop string, nItems int32) ([]float32, error) {
datas, nBytes := utils.GetProperty(id, prop)
if len(datas) == 0 {
return nil, fmt.Errorf("Get prop '%v -- %s' failed: property data is empty",
id, prop)
}
// Check if nBytes is divisible by 4 (float32 size)
if nBytes%4 != 0 {
return nil, fmt.Errorf("Get prop '%v -- %s' failed: byte count %d is not divisible by 4 (float32 size)",
id, prop, nBytes)
}
// utils.GetProperty returns byte count, float32 occupies 4 bytes, divide by 4 to get item count
if nBytes/4 != nItems {
return nil, fmt.Errorf("Get prop '%v -- %s' failed: expected %d items but got %d bytes (%d items)",
id, prop, nItems, nBytes, nBytes/4)
}

return utils.ReadFloat32(datas, nitems), nil
return utils.ReadFloat32(datas, nItems), nil
}

func absInt32(v int32) int32 {
Expand Down
2 changes: 1 addition & 1 deletion dxinput/utils/button_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func GetButtonMap(xid uint32, devName string) ([]byte, error) {
}
defer C.free(unsafe.Pointer(cbtnMap))

return ucharArrayToByte(cbtnMap, int(cbtnNum)), nil
return C.GoBytes(unsafe.Pointer(cbtnMap), cbtnNum), nil
}

func SetButtonMap(xid uint32, devName string, btnMap []byte) error {
Expand Down
78 changes: 70 additions & 8 deletions dxinput/utils/property.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#include <stdio.h>
#include <limits.h>
#include <pthread.h>

#include <X11/Xatom.h>
Expand All @@ -12,26 +13,29 @@
#include "type.h"
#include "x11_mutex.h"

#define MAX_BUF_LEN 1000

/**
* The return data type if 'char' must be convert to 'int8_t*'
* if 'int' must be convert to 'int32_t*'
* if 'float' must be convert to 'float*'
*
* Returns the property data and sets nbytes to the actual byte length.
* The caller is responsible for calling XFree() on the returned data.
**/
unsigned char*
get_prop(int id, const char* prop, int* nitems)
get_prop(int id, const char* prop, int* nbytes)
{
if (!prop) {
fprintf(stderr, "[get_prop] Empty property for %d\n", id);
return NULL;
}

if (!nitems) {
fprintf(stderr, "[get_prop] Invalid item number for %d\n", id);
if (!nbytes) {
fprintf(stderr, "[get_prop] Invalid nbytes pointer for %d\n", id);
return NULL;
}

*nbytes = 0;

pthread_mutex_lock(&x11_global_mutex);
setErrorHandler();

Expand All @@ -54,24 +58,82 @@ get_prop(int id, const char* prop, int* nitems)
int act_format;
unsigned long num_items, bytes_after;
unsigned char* data = NULL;
int ret = XIGetProperty(disp, id, prop_id, 0, MAX_BUF_LEN, False,

// Step 1: Query property size (length=0 to get bytes_after)
int ret = XIGetProperty(disp, id, prop_id, 0, 0, False,
AnyPropertyType, &act_type, &act_format,
&num_items, &bytes_after, &data);
if (ret != Success || act_type == None) {
if (data) {
XFree(data);
}
XCloseDisplay(disp);
pthread_mutex_unlock(&x11_global_mutex);
return NULL;
}

if (data) {
XFree(data);
data = NULL;
}

if (bytes_after == 0) {
// Property exists but has no data
XCloseDisplay(disp);
pthread_mutex_unlock(&x11_global_mutex);
return NULL;
}

// Step 2: Read all property data
// length is in 32-bit units, so divide bytes_after by 4 (round up)
unsigned long length = (bytes_after + 3) / 4;

ret = XIGetProperty(disp, id, prop_id, 0, length, False,
AnyPropertyType, &act_type, &act_format,
&num_items, &bytes_after, &data);
if (ret != Success) {
if (data) {
XFree(data);
}
XCloseDisplay(disp);
fprintf(stderr, "[get_prop] Get %s data failed for %d\n", prop, id);
pthread_mutex_unlock(&x11_global_mutex);
return NULL;
}

*nitems = (int)num_items;
XCloseDisplay(disp);
// Calculate actual byte length based on format and num_items
// format is 8, 16, or 32 bits per item
// Guard against integer overflow when converting unsigned long to int
unsigned long nbytes_ul = num_items * (act_format / 8);
if (nbytes_ul > INT_MAX) {
if (data) {
XFree(data);
}
XCloseDisplay(disp);
fprintf(stderr, "[get_prop] Property data too large: %lu bytes (max %d)\n", nbytes_ul, INT_MAX);
pthread_mutex_unlock(&x11_global_mutex);
return NULL;
}
*nbytes = (int)nbytes_ul;

XCloseDisplay(disp);
pthread_mutex_unlock(&x11_global_mutex);

return data;
}

/**
* Free the property data returned by get_prop.
* This is a wrapper for XFree to be called from Go.
*/
void
free_prop_data(unsigned char* data)
{
if (data) {
XFree(data);
}
}

// bit: range(8,16,32)
int
set_prop_int(int id, const char* prop, unsigned char* data, int nitems, int bit)
Expand Down
3 changes: 2 additions & 1 deletion dxinput/utils/property.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

#include <X11/Xlib.h>

unsigned char* get_prop(int id, const char* prop, int* nitems);
unsigned char* get_prop(int id, const char* prop, int* nbytes);
void free_prop_data(unsigned char* data);
int set_prop_int(int id, const char* prop, unsigned char* data, int nitems, int bit);
int set_prop_float(int id, const char* prop, unsigned char* data, int nitems);
int set_prop(int id, const char* prop, unsigned char* data, int nitems,
Expand Down
3 changes: 2 additions & 1 deletion dxinput/utils/type.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ listener_error_handler(Display * display, XErrorEvent * event)
int
listener_ioerror_handler(Display * display)
{
(void)display; // Suppress unused parameter warning
return 0;
}

Expand Down Expand Up @@ -157,7 +158,7 @@ static int
is_keyboard_device(int deviceid)
{
Display *display;
int num_devices, i;
int num_devices;

// NOTE: No mutex lock here - called from query_device_type which already holds the lock
// 打开 X11 显示
Expand Down
44 changes: 9 additions & 35 deletions dxinput/utils/wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ import (
"github.com/linuxdeepin/dde-api/dxinput/kwayland"
)

const (
// see 'property.c' MAX_BUF_LEN
maxBufferLen = 1000
)

func ListDevice() DeviceInfos {
if len(os.Getenv("WAYLAND_DISPLAY")) != 0 {
infos, _ := kwayland.ListDevice()
Expand Down Expand Up @@ -103,21 +98,19 @@ func GetProperty(id int32, prop string) ([]byte, int32) {
}

cprop := C.CString(prop)
defer C.free(unsafe.Pointer(cprop))

defer func() {
if cprop != nil {
C.free(unsafe.Pointer(cprop))
}
}()

nitems := C.int(0)
cdatas := C.get_prop(C.int(id), cprop, &nitems)
if cdatas == nil || nitems == 0 {
// nbytes now represents the actual byte length returned by C layer
nbytes := C.int(0)
cdatas := C.get_prop(C.int(id), cprop, &nbytes)
if cdatas == nil || nbytes == 0 {
return nil, 0
}
// XIGetProperty allocates memory for data, caller must free it with XFree
defer C.free_prop_data(cdatas)

datas := ucharArrayToByte(cdatas, maxBufferLen)
return datas, int32(nitems)
datas := C.GoBytes(unsafe.Pointer(cdatas), nbytes)
return datas, int32(nbytes)
}

func SetInt8Prop(id int32, prop string, values []int8) error {
Expand Down Expand Up @@ -196,25 +189,6 @@ func SetFloat32Prop(id int32, prop string, values []float32) error {
return nil
}

func ucharArrayToByte(cData *C.uchar, length int) []byte {
if cData == nil {
return nil
}
cItemSize := unsafe.Sizeof(*cData)

var data []byte
for i := 0; i < length; i++ {
offset := uintptr(i) * cItemSize
addr := uintptr(unsafe.Pointer(cData)) + offset
cdata := (*C.uchar)(unsafe.Pointer(addr))
if cdata == nil {
break
}
data = append(data, byte(*cdata))
}
return data
}

func byteArrayToUChar(datas []byte) []C.uchar {
var cdatas []C.uchar
for i := 0; i < len(datas); i++ {
Expand Down
4 changes: 2 additions & 2 deletions dxinput/wacom.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ package dxinput

import (
"bytes"
"errors"
"fmt"
"os/exec"
"strconv"
"strings"
"errors"

. "github.com/linuxdeepin/dde-api/dxinput/common"
"github.com/linuxdeepin/dde-api/dxinput/utils"
Expand Down Expand Up @@ -251,7 +251,7 @@ func doAction(cmd string) error {
// #nosec G204
out, err := exec.Command("/bin/sh", "-c", cmd).CombinedOutput()
if err != nil {
return fmt.Errorf(string(out))
return errors.New(string(out))
}
return nil
}