neko/pkg/xorg/xorg.go

350 lines
6.7 KiB
Go
Raw Normal View History

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