2020-10-23 03:54:50 +13:00
|
|
|
package xorg
|
|
|
|
|
|
|
|
/*
|
2023-02-15 09:18:47 +13:00
|
|
|
#cgo LDFLAGS: -lX11 -lXrandr -lXtst -lXfixes -lxcvt
|
2020-10-23 03:54:50 +13:00
|
|
|
|
|
|
|
#include "xorg.h"
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-03-20 23:43:00 +13:00
|
|
|
"image"
|
|
|
|
"image/color"
|
2020-10-23 03:54:50 +13:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
"unsafe"
|
|
|
|
|
2022-07-14 10:58:22 +12:00
|
|
|
"github.com/demodesk/neko/pkg/types"
|
2020-10-23 03:54:50 +13:00
|
|
|
)
|
|
|
|
|
2022-01-30 13:20:26 +13:00
|
|
|
//go:generate ./keysymdef.sh
|
|
|
|
|
2021-09-03 07:37:24 +12:00
|
|
|
type KbdMod uint8
|
2021-01-13 10:54:13 +13:00
|
|
|
|
|
|
|
const (
|
2021-09-03 07:37:24 +12:00
|
|
|
KbdModCapsLock KbdMod = 2
|
|
|
|
KbdModNumLock KbdMod = 16
|
2021-01-13 10:54:13 +13:00
|
|
|
)
|
|
|
|
|
2023-02-15 09:18:47 +13:00
|
|
|
type ScreenConfiguration struct {
|
|
|
|
Width int
|
|
|
|
Height int
|
|
|
|
Rates map[int]int16
|
|
|
|
}
|
|
|
|
|
|
|
|
var ScreenConfigurations = make(map[int]ScreenConfiguration)
|
2020-10-23 03:54:50 +13:00
|
|
|
|
2021-02-24 09:25:55 +13:00
|
|
|
var debounce_button = make(map[uint32]time.Time)
|
|
|
|
var debounce_key = make(map[uint32]time.Time)
|
2020-10-23 03:54:50 +13:00
|
|
|
var mu = sync.Mutex{}
|
|
|
|
|
2020-11-04 12:09:52 +13:00
|
|
|
func GetScreenConfigurations() {
|
2020-11-15 10:35:50 +13:00
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
2020-10-23 03:54:50 +13:00
|
|
|
C.XGetScreenConfigurations()
|
|
|
|
}
|
|
|
|
|
2021-03-12 05:44:49 +13:00
|
|
|
func DisplayOpen(display string) bool {
|
2020-10-23 03:54:50 +13:00
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
displayUnsafe := C.CString(display)
|
|
|
|
defer C.free(unsafe.Pointer(displayUnsafe))
|
|
|
|
|
2021-03-12 05:44:49 +13:00
|
|
|
ok := C.XDisplayOpen(displayUnsafe)
|
|
|
|
return int(ok) == 1
|
2020-11-04 12:09:52 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
func DisplayClose() {
|
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
2021-02-03 06:28:32 +13:00
|
|
|
|
2020-11-04 12:09:52 +13:00
|
|
|
C.XDisplayClose()
|
2020-10-23 03:54:50 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
func Move(x, y int) {
|
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
C.XMove(C.int(x), C.int(y))
|
|
|
|
}
|
|
|
|
|
2021-02-18 09:55:11 +13:00
|
|
|
func GetCursorPosition() (int, int) {
|
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
var x C.int
|
|
|
|
var y C.int
|
|
|
|
C.XCursorPosition(&x, &y)
|
2021-02-21 12:41:04 +13:00
|
|
|
|
2021-02-18 09:55:11 +13:00
|
|
|
return int(x), int(y)
|
|
|
|
}
|
|
|
|
|
2020-10-23 03:54:50 +13:00
|
|
|
func Scroll(x, y int) {
|
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
C.XScroll(C.int(x), C.int(y))
|
|
|
|
}
|
|
|
|
|
2021-02-24 09:25:55 +13:00
|
|
|
func ButtonDown(code uint32) error {
|
2020-10-23 03:54:50 +13:00
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
if _, ok := debounce_button[code]; ok {
|
|
|
|
return fmt.Errorf("debounced button %v", code)
|
|
|
|
}
|
|
|
|
|
|
|
|
debounce_button[code] = time.Now()
|
|
|
|
|
|
|
|
C.XButton(C.uint(code), C.int(1))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-02-24 09:25:55 +13:00
|
|
|
func KeyDown(code uint32) error {
|
2020-10-23 03:54:50 +13:00
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
if _, ok := debounce_key[code]; ok {
|
|
|
|
return fmt.Errorf("debounced key %v", code)
|
|
|
|
}
|
|
|
|
|
|
|
|
debounce_key[code] = time.Now()
|
|
|
|
|
2021-04-10 11:10:14 +12:00
|
|
|
C.XKey(C.KeySym(code), C.int(1))
|
2020-10-23 03:54:50 +13:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-02-24 09:25:55 +13:00
|
|
|
func ButtonUp(code uint32) error {
|
2020-10-23 03:54:50 +13:00
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
if _, ok := debounce_button[code]; !ok {
|
|
|
|
return fmt.Errorf("debounced button %v", code)
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(debounce_button, code)
|
|
|
|
|
|
|
|
C.XButton(C.uint(code), C.int(0))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-02-24 09:25:55 +13:00
|
|
|
func KeyUp(code uint32) error {
|
2020-10-23 03:54:50 +13:00
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
if _, ok := debounce_key[code]; !ok {
|
|
|
|
return fmt.Errorf("debounced key %v", code)
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(debounce_key, code)
|
|
|
|
|
2021-04-10 11:10:14 +12:00
|
|
|
C.XKey(C.KeySym(code), C.int(0))
|
2020-10-23 03:54:50 +13:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ResetKeys() {
|
2020-11-15 10:35:50 +13:00
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
2020-10-23 03:54:50 +13:00
|
|
|
|
2020-11-15 10:35:50 +13:00
|
|
|
for code := range debounce_button {
|
|
|
|
C.XButton(C.uint(code), C.int(0))
|
2020-10-23 03:54:50 +13:00
|
|
|
delete(debounce_button, code)
|
|
|
|
}
|
|
|
|
|
2020-11-15 10:35:50 +13:00
|
|
|
for code := range debounce_key {
|
2021-04-10 11:10:14 +12:00
|
|
|
C.XKey(C.KeySym(code), C.int(0))
|
2020-10-23 03:54:50 +13:00
|
|
|
delete(debounce_key, code)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func CheckKeys(duration time.Duration) {
|
2020-11-15 10:35:50 +13:00
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
2020-10-23 03:54:50 +13:00
|
|
|
t := time.Now()
|
|
|
|
for code, start := range debounce_button {
|
|
|
|
if t.Sub(start) < duration {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-11-15 10:35:50 +13:00
|
|
|
C.XButton(C.uint(code), C.int(0))
|
2020-10-23 03:54:50 +13:00
|
|
|
delete(debounce_button, code)
|
|
|
|
}
|
2020-11-15 10:35:50 +13:00
|
|
|
|
2020-10-23 03:54:50 +13:00
|
|
|
for code, start := range debounce_key {
|
|
|
|
if t.Sub(start) < duration {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-04-10 11:10:14 +12:00
|
|
|
C.XKey(C.KeySym(code), C.int(0))
|
2020-10-23 03:54:50 +13:00
|
|
|
delete(debounce_key, code)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-15 09:18:47 +13:00
|
|
|
// set screen configuration, create new one if not exists
|
2023-08-18 02:14:59 +12:00
|
|
|
func ChangeScreenSize(s types.ScreenSize) (types.ScreenSize, error) {
|
2020-10-23 03:54:50 +13:00
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
2023-02-21 07:49:39 +13:00
|
|
|
// round width to 8, because of Xorg
|
2023-08-18 02:14:59 +12:00
|
|
|
s.Width = s.Width - (s.Width % 8)
|
2023-02-15 09:18:47 +13:00
|
|
|
|
2023-03-14 05:55:41 +13:00
|
|
|
// if rate is 0, set it to 60
|
2023-08-18 02:14:59 +12:00
|
|
|
if s.Rate == 0 {
|
|
|
|
s.Rate = 60
|
2023-03-14 05:55:41 +13:00
|
|
|
}
|
|
|
|
|
2023-02-15 09:18:47 +13:00
|
|
|
// convert variables to C types
|
2023-08-18 02:14:59 +12:00
|
|
|
c_width, c_height, c_rate := C.int(s.Width), C.int(s.Height), C.short(s.Rate)
|
2023-02-15 09:18:47 +13:00
|
|
|
|
|
|
|
// if screen configuration already exists, just set it
|
2023-03-14 05:55:41 +13:00
|
|
|
status := C.XSetScreenConfiguration(c_width, c_height, c_rate)
|
|
|
|
if status != C.RRSetConfigSuccess {
|
|
|
|
// create new screen configuration
|
|
|
|
C.XCreateScreenMode(c_width, c_height, c_rate)
|
|
|
|
|
|
|
|
// screen configuration should exist now, set it
|
|
|
|
status = C.XSetScreenConfiguration(c_width, c_height, c_rate)
|
2023-02-15 09:18:47 +13:00
|
|
|
}
|
|
|
|
|
2023-03-14 05:55:41 +13:00
|
|
|
var err error
|
|
|
|
|
|
|
|
// if screen configuration was not set successfully, return error
|
|
|
|
if status != C.RRSetConfigSuccess {
|
2023-08-18 02:14:59 +12:00
|
|
|
err = fmt.Errorf("unknown screen configuration %s", s.String())
|
2023-03-14 05:55:41 +13:00
|
|
|
}
|
2023-02-15 09:18:47 +13:00
|
|
|
|
2023-03-14 05:55:41 +13:00
|
|
|
// if specified rate is not supported a BadValue error is returned
|
|
|
|
if status == C.BadValue {
|
2023-08-18 02:14:59 +12:00
|
|
|
err = fmt.Errorf("unsupported screen rate %d", s.Rate)
|
2020-10-23 03:54:50 +13:00
|
|
|
}
|
|
|
|
|
2023-08-18 02:14:59 +12:00
|
|
|
return s, err
|
2020-10-23 03:54:50 +13:00
|
|
|
}
|
|
|
|
|
2023-02-15 09:18:47 +13:00
|
|
|
func GetScreenSize() types.ScreenSize {
|
2020-10-23 03:54:50 +13:00
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
2023-02-15 09:18:47 +13:00
|
|
|
c_width, c_height, c_rate := C.int(0), C.int(0), C.short(0)
|
|
|
|
C.XGetScreenConfiguration(&c_width, &c_height, &c_rate)
|
2020-10-23 03:54:50 +13:00
|
|
|
|
2023-02-15 09:18:47 +13:00
|
|
|
return types.ScreenSize{
|
|
|
|
Width: int(c_width),
|
|
|
|
Height: int(c_height),
|
|
|
|
Rate: int16(c_rate),
|
2020-10-23 03:54:50 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-03 07:37:24 +12:00
|
|
|
func SetKeyboardModifier(mod KbdMod, active bool) {
|
2021-01-13 10:54:13 +13:00
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
num := C.int(0)
|
|
|
|
if active {
|
|
|
|
num = C.int(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
C.XSetKeyboardModifier(C.int(mod), num)
|
|
|
|
}
|
|
|
|
|
2021-09-03 07:37:24 +12:00
|
|
|
func GetKeyboardModifiers() KbdMod {
|
2020-10-23 03:54:50 +13:00
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
2021-09-03 07:37:24 +12:00
|
|
|
return KbdMod(C.XGetKeyboardModifiers())
|
2020-10-23 03:54:50 +13:00
|
|
|
}
|
|
|
|
|
2021-01-10 10:58:18 +13:00
|
|
|
func GetCursorImage() *types.CursorImage {
|
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
2021-01-26 05:31:24 +13:00
|
|
|
cur := C.XGetCursorImage()
|
2021-01-10 10:58:18 +13:00
|
|
|
defer C.XFree(unsafe.Pointer(cur))
|
|
|
|
|
2021-02-26 02:03:57 +13:00
|
|
|
width := int(cur.width)
|
|
|
|
height := int(cur.height)
|
|
|
|
|
|
|
|
// Xlib stores 32-bit data in longs, even if longs are 64-bits long.
|
|
|
|
pixels := C.GoBytes(unsafe.Pointer(cur.pixels), C.int(width*height*8))
|
|
|
|
|
|
|
|
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
2021-03-25 23:59:57 +13:00
|
|
|
for y := 0; y < height; y++ {
|
|
|
|
for x := 0; x < width; x++ {
|
|
|
|
pos := ((y * width) + x) * 8
|
2021-02-26 02:03:57 +13:00
|
|
|
|
2021-03-25 23:59:57 +13:00
|
|
|
img.SetRGBA(x, y, color.RGBA{
|
2021-02-26 02:03:57 +13:00
|
|
|
A: pixels[pos+3],
|
|
|
|
R: pixels[pos+2],
|
|
|
|
G: pixels[pos+1],
|
|
|
|
B: pixels[pos+0],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2021-01-10 10:58:18 +13:00
|
|
|
|
|
|
|
return &types.CursorImage{
|
2021-02-26 02:03:57 +13:00
|
|
|
Width: uint16(width),
|
|
|
|
Height: uint16(height),
|
2021-02-15 02:40:17 +13:00
|
|
|
Xhot: uint16(cur.xhot),
|
|
|
|
Yhot: uint16(cur.yhot),
|
2021-01-10 10:58:18 +13:00
|
|
|
Serial: uint64(cur.cursor_serial),
|
2021-02-26 02:03:57 +13:00
|
|
|
Image: img,
|
2021-01-10 10:58:18 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-22 08:44:09 +13:00
|
|
|
func GetScreenshotImage() *image.RGBA {
|
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
var w, h C.int
|
|
|
|
pixelsUnsafe := C.XGetScreenshot(&w, &h)
|
2021-02-15 02:40:17 +13:00
|
|
|
pixels := C.GoBytes(unsafe.Pointer(pixelsUnsafe), w*h*3)
|
2021-01-22 08:44:09 +13:00
|
|
|
defer C.free(unsafe.Pointer(pixelsUnsafe))
|
|
|
|
|
|
|
|
width := int(w)
|
|
|
|
height := int(h)
|
|
|
|
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
|
|
|
for row := 0; row < height; row++ {
|
|
|
|
for col := 0; col < width; col++ {
|
|
|
|
pos := ((row * width) + col) * 3
|
|
|
|
|
|
|
|
img.SetRGBA(col, row, color.RGBA{
|
|
|
|
R: uint8(pixels[pos]),
|
|
|
|
G: uint8(pixels[pos+1]),
|
|
|
|
B: uint8(pixels[pos+2]),
|
|
|
|
A: 0xFF,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return img
|
|
|
|
}
|
|
|
|
|
2020-10-23 03:54:50 +13:00
|
|
|
//export goCreateScreenSize
|
|
|
|
func goCreateScreenSize(index C.int, width C.int, height C.int, mwidth C.int, mheight C.int) {
|
2023-02-15 09:18:47 +13:00
|
|
|
ScreenConfigurations[int(index)] = ScreenConfiguration{
|
2020-10-23 03:54:50 +13:00
|
|
|
Width: int(width),
|
|
|
|
Height: int(height),
|
|
|
|
Rates: make(map[int]int16),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//export goSetScreenRates
|
2021-02-11 06:56:20 +13:00
|
|
|
func goSetScreenRates(index C.int, rate_index C.int, rateC C.short) {
|
2023-02-15 09:18:47 +13:00
|
|
|
ScreenConfigurations[int(index)].Rates[int(rate_index)] = int16(rateC)
|
2020-10-23 03:54:50 +13:00
|
|
|
}
|