xorg refactor.

This commit is contained in:
Miroslav Šedivý 2022-09-13 21:40:40 +02:00
parent 478984e944
commit 4c1c96b163
15 changed files with 467 additions and 252 deletions

View File

@ -1,7 +1,6 @@
package capture
import (
"fmt"
"time"
"m1k1o/neko/internal/capture/gst"
@ -127,7 +126,7 @@ func (manager *CaptureManagerCtx) Streaming() bool {
func (manager *CaptureManagerCtx) createPipelines() {
// handle maximum fps
rate := int(manager.desktop.GetScreenSize().Rate)
rate := manager.desktop.GetScreenSize().Rate
if manager.config.MaxFPS != 0 && manager.config.MaxFPS < rate {
rate = manager.config.MaxFPS
}
@ -158,11 +157,7 @@ func (manager *CaptureManagerCtx) createPipelines() {
}
}
func (manager *CaptureManagerCtx) ChangeResolution(width int, height int, rate int) error {
if !xorg.ValidScreenSize(width, height, rate) {
return fmt.Errorf("unknown configuration")
}
func (manager *CaptureManagerCtx) ChangeResolution(width int, height int, rate int16) error {
manager.video.Stop()
manager.broadcast.Stop()

View File

@ -52,7 +52,7 @@ func CreateRTMPPipeline(pipelineDevice string, pipelineDisplay string, pipelineS
}
// CreateAppPipeline creates a GStreamer Pipeline
func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc string, fps int, bitrate uint, hwenc string) (*gst.Pipeline, error) {
func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc string, fps int16, bitrate uint, hwenc string) (*gst.Pipeline, error) {
pipelineStr := " ! appsink name=appsink"
// if using custom pipeline

View File

@ -15,7 +15,7 @@ type Capture struct {
VideoCodec string
VideoParams string
VideoBitrate uint
MaxFPS int
MaxFPS int16
}
func (Capture) Init(cmd *cobra.Command) error {
@ -135,5 +135,5 @@ func (s *Capture) Set() {
s.VideoParams = viper.GetString("video")
s.VideoBitrate = viper.GetUint("video_bitrate")
s.MaxFPS = viper.GetInt("max_fps")
s.MaxFPS = int16(viper.GetInt("max_fps"))
}

View File

@ -14,7 +14,7 @@ type Desktop struct {
ScreenWidth int
ScreenHeight int
ScreenRate int
ScreenRate int16
}
func (Desktop) Init(cmd *cobra.Command) error {
@ -45,7 +45,7 @@ func (s *Desktop) Set() {
if err1 == nil && err2 == nil && err3 == nil {
s.ScreenWidth = int(width)
s.ScreenHeight = int(height)
s.ScreenRate = int(rate)
s.ScreenRate = int16(rate)
}
}
}

View File

@ -1,6 +1,7 @@
package desktop
import (
"fmt"
"sync"
"time"
@ -28,14 +29,17 @@ func New(config *config.Desktop, broadcast types.BroadcastManager) *DesktopManag
}
func (manager *DesktopManagerCtx) Start() {
xorg.Display(manager.config.Display)
if !xorg.ValidScreenSize(manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate) {
manager.logger.Warn().Msgf("invalid screen option %dx%d@%d", manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate)
} else if err := xorg.ChangeScreenSize(manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate); err != nil {
manager.logger.Warn().Err(err).Msg("unable to change screen size")
if xorg.DisplayOpen(manager.config.Display) {
manager.logger.Panic().Str("display", manager.config.Display).Msg("unable to open display")
}
xorg.GetScreenConfigurations()
err := xorg.ChangeScreenSize(manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate)
manager.logger.Err(err).
Str("screen_size", fmt.Sprintf("%dx%d@%d", manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate)).
Msgf("setting initial screen size")
manager.wg.Add(1)
go func() {
@ -61,5 +65,6 @@ func (manager *DesktopManagerCtx) Shutdown() error {
close(manager.shutdown)
manager.wg.Wait()
xorg.DisplayClose()
return nil
}

View File

@ -1,7 +1,10 @@
package desktop
import (
"image"
"os/exec"
"regexp"
"time"
"m1k1o/neko/internal/desktop/xorg"
"m1k1o/neko/internal/types"
@ -11,26 +14,54 @@ func (manager *DesktopManagerCtx) Move(x, y int) {
xorg.Move(x, y)
}
func (manager *DesktopManagerCtx) GetCursorPosition() (int, int) {
return xorg.GetCursorPosition()
}
func (manager *DesktopManagerCtx) Scroll(x, y int) {
xorg.Scroll(x, y)
}
func (manager *DesktopManagerCtx) ButtonDown(code int) error {
func (manager *DesktopManagerCtx) ButtonDown(code uint32) error {
return xorg.ButtonDown(code)
}
func (manager *DesktopManagerCtx) KeyDown(code uint64) error {
func (manager *DesktopManagerCtx) KeyDown(code uint32) error {
return xorg.KeyDown(code)
}
func (manager *DesktopManagerCtx) ButtonUp(code int) error {
func (manager *DesktopManagerCtx) ButtonUp(code uint32) error {
return xorg.ButtonUp(code)
}
func (manager *DesktopManagerCtx) KeyUp(code uint64) error {
func (manager *DesktopManagerCtx) KeyUp(code uint32) error {
return xorg.KeyUp(code)
}
func (manager *DesktopManagerCtx) ButtonPress(code uint32) error {
xorg.ResetKeys()
defer xorg.ResetKeys()
return xorg.ButtonDown(code)
}
func (manager *DesktopManagerCtx) KeyPress(codes ...uint32) error {
xorg.ResetKeys()
defer xorg.ResetKeys()
for _, code := range codes {
if err := xorg.KeyDown(code); err != nil {
return err
}
}
if len(codes) > 1 {
time.Sleep(10 * time.Millisecond)
}
return nil
}
func (manager *DesktopManagerCtx) ResetKeys() {
xorg.ResetKeys()
}
@ -39,14 +70,72 @@ func (manager *DesktopManagerCtx) ScreenConfigurations() map[int]types.ScreenCon
return xorg.ScreenConfigurations
}
func (manager *DesktopManagerCtx) SetScreenSize(size types.ScreenSize) error {
return xorg.ChangeScreenSize(size.Width, size.Height, size.Rate)
}
func (manager *DesktopManagerCtx) GetScreenSize() *types.ScreenSize {
return xorg.GetScreenSize()
}
func (manager *DesktopManagerCtx) SetKeyboardLayout(layout string) {
_ = exec.Command("setxkbmap", layout).Run()
func (manager *DesktopManagerCtx) SetKeyboardMap(kbd types.KeyboardMap) error {
// TOOD: Use native API.
cmd := exec.Command("setxkbmap", "-layout", kbd.Layout, "-variant", kbd.Variant)
_, err := cmd.Output()
return err
}
func (manager *DesktopManagerCtx) SetKeyboardModifiers(NumLock int, CapsLock int, ScrollLock int) {
xorg.SetKeyboardModifiers(NumLock, CapsLock, ScrollLock)
func (manager *DesktopManagerCtx) GetKeyboardMap() (*types.KeyboardMap, error) {
// TOOD: Use native API.
cmd := exec.Command("setxkbmap", "-query")
res, err := cmd.Output()
if err != nil {
return nil, err
}
kbd := types.KeyboardMap{}
re := regexp.MustCompile(`layout:\s+(.*)\n`)
arr := re.FindStringSubmatch(string(res))
if len(arr) > 1 {
kbd.Layout = arr[1]
}
re = regexp.MustCompile(`variant:\s+(.*)\n`)
arr = re.FindStringSubmatch(string(res))
if len(arr) > 1 {
kbd.Variant = arr[1]
}
return &kbd, nil
}
func (manager *DesktopManagerCtx) SetKeyboardModifiers(mod types.KeyboardModifiers) {
if mod.NumLock != nil {
xorg.SetKeyboardModifier(xorg.KbdModNumLock, *mod.NumLock)
}
if mod.CapsLock != nil {
xorg.SetKeyboardModifier(xorg.KbdModCapsLock, *mod.CapsLock)
}
}
func (manager *DesktopManagerCtx) GetKeyboardModifiers() types.KeyboardModifiers {
modifiers := xorg.GetKeyboardModifiers()
NumLock := (modifiers & xorg.KbdModNumLock) != 0
CapsLock := (modifiers & xorg.KbdModCapsLock) != 0
return types.KeyboardModifiers{
NumLock: &NumLock,
CapsLock: &CapsLock,
}
}
func (manager *DesktopManagerCtx) GetCursorImage() *types.CursorImage {
return xorg.GetCursorImage()
}
func (manager *DesktopManagerCtx) GetScreenshotImage() *image.RGBA {
return xorg.GetScreenshotImage()
}

View File

@ -1,97 +1,35 @@
#include "xorg.h"
static Display *DISPLAY = NULL;
static char *NAME = ":0.0";
static int REGISTERED = 0;
static int DIRTY = 0;
xkeys_t *xKeysHead = NULL;
void XKeysInsert(KeySym keysym, KeyCode keycode) {
xkeys_t *node = (xkeys_t *) malloc(sizeof(xkeys_t));
node->keysym = keysym;
node->keycode = keycode;
node->next = xKeysHead;
xKeysHead = node;
}
KeyCode XKeysPop(KeySym keysym) {
KeyCode keycode = 0;
xkeys_t *node = xKeysHead,
*previous = NULL;
int i = 0;
while (node) {
if (node->keysym == keysym) {
keycode = node->keycode;
if (!previous)
xKeysHead = node->next;
else
previous->next = node->next;
free(node);
return keycode;
}
previous = node;
node = node->next;
if (i++ > 120) {
// this should NEVER HAPPEN
fprintf(stderr, "[FATAL-ERROR] XKeysPop() in xorg.c: reached maximum loop limit! Something is wrong\n");
break;
}
}
return 0;
}
Display *getXDisplay(void) {
/* Close the display if displayName has changed */
if (DIRTY) {
XDisplayClose();
DIRTY = 0;
}
if (DISPLAY == NULL) {
/* First try the user set displayName */
DISPLAY = XOpenDisplay(NAME);
/* Then try using environment variable DISPLAY */
if (DISPLAY == NULL) {
DISPLAY = XOpenDisplay(NULL);
}
if (DISPLAY == NULL) {
fprintf(stderr, "[FATAL-ERROR] XKeysPop() in xorg.c: Could not open main display!");
} else if (!REGISTERED) {
atexit(&XDisplayClose);
REGISTERED = 1;
}
}
return DISPLAY;
}
void XDisplayClose(void) {
if (DISPLAY != NULL) {
XCloseDisplay(DISPLAY);
DISPLAY = NULL;
}
int XDisplayOpen(char *name) {
DISPLAY = XOpenDisplay(name);
return DISPLAY == NULL;
}
void XDisplaySet(char *input) {
NAME = strdup(input);
DIRTY = 1;
void XDisplayClose(void) {
XCloseDisplay(DISPLAY);
}
void XMove(int x, int y) {
Display *display = getXDisplay();
XWarpPointer(display, None, DefaultRootWindow(display), 0, 0, 0, 0, x, y);
XWarpPointer(display, None, DefaultRootWindow(display), 0, 0, 0, 0, x, y);
XSync(display, 0);
}
void XCursorPosition(int *x, int *y) {
Display *display = getXDisplay();
Window root = DefaultRootWindow(display);
Window window;
int i;
unsigned mask;
XQueryPointer(display, root, &root, &window, x, y, &i, &i, &mask);
}
void XScroll(int x, int y) {
int ydir = 4; /* Button 4 is up, 5 is down. */
int xdir = 6;
@ -123,15 +61,55 @@ void XScroll(int x, int y) {
}
void XButton(unsigned int button, int down) {
if (button != 0) {
Display *display = getXDisplay();
XTestFakeButtonEvent(display, button, down, CurrentTime);
XSync(display, 0);
}
if (button == 0)
return;
Display *display = getXDisplay();
XTestFakeButtonEvent(display, button, down, CurrentTime);
XSync(display, 0);
}
// From: https://github.com/TigerVNC/tigervnc/blob/0946e298075f8f7b6d63e552297a787c5f84d27c/unix/x0vncserver/XDesktop.cxx#L343-L379
KeyCode XkbKeysymToKeycode(Display *dpy, KeySym keysym) {
static xkeyentry_t *xKeysHead = NULL;
void XKeyEntryAdd(KeySym keysym, KeyCode keycode) {
xkeyentry_t *entry = (xkeyentry_t *) malloc(sizeof(xkeyentry_t));
if (entry == NULL)
return;
entry->keysym = keysym;
entry->keycode = keycode;
entry->next = xKeysHead;
xKeysHead = entry;
}
KeyCode XKeyEntryGet(KeySym keysym) {
xkeyentry_t *prev = NULL;
xkeyentry_t *curr = xKeysHead;
KeyCode keycode = 0;
while (curr != NULL) {
if (curr->keysym == keysym) {
keycode = curr->keycode;
if (prev == NULL) {
xKeysHead = curr->next;
} else {
prev->next = curr->next;
}
free(curr);
return keycode;
}
prev = curr;
curr = curr->next;
}
return 0;
}
// From https://github.com/TigerVNC/tigervnc/blob/0946e298075f8f7b6d63e552297a787c5f84d27c/unix/x0vncserver/XDesktop.cxx#L343-L379
KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym) {
XkbDescPtr xkb;
XkbStateRec state;
unsigned int mods;
@ -169,51 +147,52 @@ KeyCode XkbKeysymToKeycode(Display *dpy, KeySym keysym) {
return keycode;
}
void XKey(KeySym key, int down) {
void XKey(KeySym keysym, int down) {
if (keysym == 0)
return;
Display *display = getXDisplay();
KeyCode code = 0;
KeyCode keycode = 0;
if (!down)
code = XKeysPop(key);
keycode = XKeyEntryGet(keysym);
if (!code)
code = XkbKeysymToKeycode(display, key);
if (keycode == 0)
keycode = XkbKeysymToKeycode(display, keysym);
if (!code) {
// Map non-existing keysyms to new keycodes
if (keycode == 0) {
int min, max, numcodes;
XDisplayKeycodes(display, &min, &max);
XGetKeyboardMapping(display, min, max-min, &numcodes);
code = (max-min+1)*numcodes;
keycode = (max-min+1)*numcodes;
KeySym keysym_list[numcodes];
for (int i=0;i<numcodes;i++) keysym_list[i] = key;
XChangeKeyboardMapping(display, code, numcodes, keysym_list, 1);
for(int i=0;i<numcodes;i++) keysym_list[i] = keysym;
XChangeKeyboardMapping(display, keycode, numcodes, keysym_list, 1);
}
if (!code)
return;
if (down)
XKeysInsert(key, code);
XKeyEntryAdd(keysym, keycode);
XTestFakeKeyEvent(display, code, down, CurrentTime);
XTestFakeKeyEvent(display, keycode, down, CurrentTime);
XSync(display, 0);
}
void XGetScreenConfigurations() {
Display *display = getXDisplay();
Window root = RootWindow(display, 0);
Display *display = getXDisplay();
Window root = RootWindow(display, 0);
XRRScreenSize *xrrs;
int num_sizes;
int num_sizes;
xrrs = XRRSizes(display, 0, &num_sizes);
for(int i = 0; i < num_sizes; i ++) {
for (int i = 0; i < num_sizes; i++) {
short *rates;
int num_rates;
int num_rates;
goCreateScreenSize(i, xrrs[i].width, xrrs[i].height, xrrs[i].mwidth, xrrs[i].mheight);
rates = XRRRates(display, 0, i, &num_rates);
for (int j = 0; j < num_rates; j ++) {
for (int j = 0; j < num_rates; j++) {
goSetScreenRates(i, j, rates[j]);
}
}
@ -227,34 +206,61 @@ void XSetScreenConfiguration(int index, short rate) {
int XGetScreenSize() {
Display *display = getXDisplay();
XRRScreenConfiguration *conf = XRRGetScreenInfo(display, RootWindow(display, 0));
XRRScreenConfiguration *conf = XRRGetScreenInfo(display, RootWindow(display, 0));
Rotation original_rotation;
return XRRConfigCurrentConfiguration(conf, &original_rotation);
}
short XGetScreenRate() {
Display *display = getXDisplay();
XRRScreenConfiguration *conf = XRRGetScreenInfo(display, RootWindow(display, 0));
XRRScreenConfiguration *conf = XRRGetScreenInfo(display, RootWindow(display, 0));
return XRRConfigCurrentRate(conf);
}
void SetKeyboardModifiers(int num_lock, int caps_lock, int scroll_lock) {
void XSetKeyboardModifier(int mod, int on) {
Display *display = getXDisplay();
if (num_lock != -1) {
XkbLockModifiers(display, XkbUseCoreKbd, 16, num_lock * 16);
}
if (caps_lock != -1) {
XkbLockModifiers(display, XkbUseCoreKbd, 2, caps_lock * 2);
}
if (scroll_lock != -1) {
XKeyboardControl values;
values.led_mode = scroll_lock ? LedModeOn : LedModeOff;
values.led = 3;
XChangeKeyboardControl(display, KBLedMode, &values);
}
XkbLockModifiers(display, XkbUseCoreKbd, mod, on ? mod : 0);
XFlush(display);
}
char XGetKeyboardModifiers() {
XkbStateRec xkbState;
Display *display = getXDisplay();
XkbGetState(display, XkbUseCoreKbd, &xkbState);
return xkbState.locked_mods;
}
XFixesCursorImage *XGetCursorImage(void) {
Display *display = getXDisplay();
return XFixesGetCursorImage(display);
}
char *XGetScreenshot(int *w, int *h) {
Display *display = getXDisplay();
Window root = DefaultRootWindow(display);
XWindowAttributes attr;
XGetWindowAttributes(display, root, &attr);
int width = attr.width;
int height = attr.height;
XImage *ximage = XGetImage(display, root, 0, 0, width, height, AllPlanes, ZPixmap);
*w = width;
*h = height;
char *pixels = (char *)malloc(width * height * 3);
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int pos = ((row * width) + col) * 3;
unsigned long pixel = XGetPixel(ximage, col, row);
pixels[pos] = (pixel & ximage->red_mask) >> 16;
pixels[pos+1] = (pixel & ximage->green_mask) >> 8;
pixels[pos+2] = pixel & ximage->blue_mask;
}
}
XDestroyImage(ximage);
return pixels;
}

View File

@ -1,8 +1,7 @@
package xorg
/*
#cgo CFLAGS: -I/usr/local/include/
#cgo LDFLAGS: -lX11 -lXtst -lXrandr -lxcb
#cgo LDFLAGS: -lX11 -lXrandr -lXtst -lXfixes
#include "xorg.h"
*/
@ -10,6 +9,8 @@ import "C"
import (
"fmt"
"image"
"image/color"
"sync"
"time"
"unsafe"
@ -17,24 +18,44 @@ import (
"m1k1o/neko/internal/types"
)
//go:generate ./keysymdef.sh
type KbdMod uint8
const (
KbdModCapsLock KbdMod = 2
KbdModNumLock KbdMod = 16
)
var ScreenConfigurations = make(map[int]types.ScreenConfiguration)
var debounce_button = make(map[int]time.Time)
var debounce_key = make(map[uint64]time.Time)
var debounce_button = make(map[uint32]time.Time)
var debounce_key = make(map[uint32]time.Time)
var mu = sync.Mutex{}
func init() {
func GetScreenConfigurations() {
mu.Lock()
defer mu.Unlock()
C.XGetScreenConfigurations()
}
func Display(display string) {
func DisplayOpen(display string) bool {
mu.Lock()
defer mu.Unlock()
displayUnsafe := C.CString(display)
defer C.free(unsafe.Pointer(displayUnsafe))
C.XDisplaySet(displayUnsafe)
ok := C.XDisplayOpen(displayUnsafe)
return int(ok) == 1
}
func DisplayClose() {
mu.Lock()
defer mu.Unlock()
C.XDisplayClose()
}
func Move(x, y int) {
@ -44,6 +65,17 @@ func Move(x, y int) {
C.XMove(C.int(x), C.int(y))
}
func GetCursorPosition() (int, int) {
mu.Lock()
defer mu.Unlock()
var x C.int
var y C.int
C.XCursorPosition(&x, &y)
return int(x), int(y)
}
func Scroll(x, y int) {
mu.Lock()
defer mu.Unlock()
@ -51,7 +83,7 @@ func Scroll(x, y int) {
C.XScroll(C.int(x), C.int(y))
}
func ButtonDown(code int) error {
func ButtonDown(code uint32) error {
mu.Lock()
defer mu.Unlock()
@ -65,7 +97,7 @@ func ButtonDown(code int) error {
return nil
}
func KeyDown(code uint64) error {
func KeyDown(code uint32) error {
mu.Lock()
defer mu.Unlock()
@ -79,7 +111,7 @@ func KeyDown(code uint64) error {
return nil
}
func ButtonUp(code int) error {
func ButtonUp(code uint32) error {
mu.Lock()
defer mu.Unlock()
@ -93,7 +125,7 @@ func ButtonUp(code int) error {
return nil
}
func KeyUp(code uint64) error {
func KeyUp(code uint32) error {
mu.Lock()
defer mu.Unlock()
@ -108,60 +140,52 @@ func KeyUp(code uint64) error {
}
func ResetKeys() {
for code := range debounce_button {
_ = ButtonUp(code)
mu.Lock()
defer mu.Unlock()
for code := range debounce_button {
C.XButton(C.uint(code), C.int(0))
delete(debounce_button, code)
}
for code := range debounce_key {
_ = KeyUp(code)
for code := range debounce_key {
C.XKey(C.KeySym(code), C.int(0))
delete(debounce_key, code)
}
}
func CheckKeys(duration time.Duration) {
mu.Lock()
defer mu.Unlock()
t := time.Now()
for code, start := range debounce_button {
if t.Sub(start) < duration {
continue
}
_ = ButtonUp(code)
C.XButton(C.uint(code), C.int(0))
delete(debounce_button, code)
}
for code, start := range debounce_key {
if t.Sub(start) < duration {
continue
}
_ = KeyUp(code)
C.XKey(C.KeySym(code), C.int(0))
delete(debounce_key, code)
}
}
func ValidScreenSize(width int, height int, rate int) bool {
for _, size := range ScreenConfigurations {
if size.Width == width && size.Height == height {
for _, fps := range size.Rates {
if int16(rate) == fps {
return true
}
}
}
}
return false
}
func ChangeScreenSize(width int, height int, rate int) error {
func ChangeScreenSize(width int, height int, rate int16) error {
mu.Lock()
defer mu.Unlock()
for index, size := range ScreenConfigurations {
if size.Width == width && size.Height == height {
for _, fps := range size.Rates {
if int16(rate) == fps {
if rate == fps {
C.XSetScreenConfiguration(C.int(index), C.short(fps))
return nil
}
@ -169,7 +193,7 @@ func ChangeScreenSize(width int, height int, rate int) error {
}
}
return fmt.Errorf("unknown configuration")
return fmt.Errorf("unknown screen configuration %dx%d@%d", width, height, rate)
}
func GetScreenSize() *types.ScreenSize {
@ -190,11 +214,88 @@ func GetScreenSize() *types.ScreenSize {
return nil
}
func SetKeyboardModifiers(num_lock int, caps_lock int, scroll_lock int) {
func SetKeyboardModifier(mod KbdMod, active bool) {
mu.Lock()
defer mu.Unlock()
C.SetKeyboardModifiers(C.int(num_lock), C.int(caps_lock), C.int(scroll_lock))
num := C.int(0)
if active {
num = C.int(1)
}
C.XSetKeyboardModifier(C.int(mod), num)
}
func GetKeyboardModifiers() KbdMod {
mu.Lock()
defer mu.Unlock()
return KbdMod(C.XGetKeyboardModifiers())
}
func GetCursorImage() *types.CursorImage {
mu.Lock()
defer mu.Unlock()
cur := C.XGetCursorImage()
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))
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
pos := ((y * width) + x) * 8
img.SetRGBA(x, y, color.RGBA{
A: pixels[pos+3],
R: pixels[pos+2],
G: pixels[pos+1],
B: pixels[pos+0],
})
}
}
return &types.CursorImage{
Width: uint16(width),
Height: uint16(height),
Xhot: uint16(cur.xhot),
Yhot: uint16(cur.yhot),
Serial: uint64(cur.cursor_serial),
Image: img,
}
}
func GetScreenshotImage() *image.RGBA {
mu.Lock()
defer mu.Unlock()
var w, h C.int
pixelsUnsafe := C.XGetScreenshot(&w, &h)
pixels := C.GoBytes(unsafe.Pointer(pixelsUnsafe), w*h*3)
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
@ -207,6 +308,13 @@ func goCreateScreenSize(index C.int, width C.int, height C.int, mwidth C.int, mh
}
//export goSetScreenRates
func goSetScreenRates(index C.int, rate_index C.int, rate C.short) {
ScreenConfigurations[int(index)].Rates[int(rate_index)] = int16(rate)
func goSetScreenRates(index C.int, rate_index C.int, rateC C.short) {
rate := int16(rateC)
// filter out all irrelevant rates
if rate > 60 || (rate > 30 && rate%10 != 0) {
return
}
ScreenConfigurations[int(index)].Rates[int(rate_index)] = rate
}

View File

@ -1,39 +1,43 @@
#pragma once
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/XTest.h>
#include <stdlib.h> /* For free() */
#include <stdio.h> /* For fputs() */
#include <string.h> /* For strdup() */
#include <X11/extensions/Xfixes.h>
#include <stdlib.h>
extern void goCreateScreenSize(int index, int width, int height, int mwidth, int mheight);
extern void goSetScreenRates(int index, int rate_index, short rate);
typedef struct xkeys_t {
KeySym keysym;
KeyCode keycode;
struct xkeys_t *next;
} xkeys_t;
/* Returns the main display, closed either on exit or when closeMainDisplay()
* is invoked. This removes a bit of the overhead of calling XOpenDisplay() &
* XCloseDisplay() everytime the main display needs to be used.
*
* Note that this is almost certainly not thread safe. */
Display *getXDisplay(void);
int XDisplayOpen(char *input);
void XDisplayClose(void);
void XMove(int x, int y);
void XCursorPosition(int *x, int *y);
void XScroll(int x, int y);
void XButton(unsigned int button, int down);
void XKey(unsigned long key, int down);
typedef struct xkeyentry_t {
KeySym keysym;
KeyCode keycode;
struct xkeyentry_t *next;
} xkeyentry_t;
static void XKeyEntryAdd(KeySym keysym, KeyCode keycode);
static KeyCode XKeyEntryGet(KeySym keysym);
static KeyCode XkbKeysymToKeycode(Display *dpy, KeySym keysym);
void XKey(KeySym keysym, int down);
void XGetScreenConfigurations();
void XSetScreenConfiguration(int index, short rate);
int XGetScreenSize();
short XGetScreenRate();
void XDisplayClose(void);
void XDisplaySet(char *input);
void XSetKeyboardModifier(int mod, int on);
char XGetKeyboardModifiers();
XFixesCursorImage *XGetCursorImage(void);
void SetKeyboardModifiers(int num_lock, int caps_lock, int scroll_lock);
char *XGetScreenshot(int *w, int *h);

View File

@ -10,5 +10,5 @@ type CaptureManager interface {
StartStream()
StopStream()
Streaming() bool
ChangeResolution(width int, height int, rate int) error
ChangeResolution(width int, height int, rate int16) error
}

View File

@ -1,5 +1,16 @@
package types
import "image"
type CursorImage struct {
Width uint16
Height uint16
Xhot uint16
Yhot uint16
Serial uint64
Image *image.RGBA
}
type ScreenSize struct {
Width int `json:"width"`
Height int `json:"height"`
@ -12,22 +23,42 @@ type ScreenConfiguration struct {
Rates map[int]int16 `json:"rates"`
}
type KeyboardModifiers struct {
NumLock *bool
CapsLock *bool
}
type KeyboardMap struct {
Layout string
Variant string
}
type DesktopManager interface {
Start()
Shutdown() error
// clipboard
ReadClipboard() string
WriteClipboard(data string)
// xorg
Move(x, y int)
GetCursorPosition() (int, int)
Scroll(x, y int)
ButtonDown(code int) error
KeyDown(code uint64) error
ButtonUp(code int) error
KeyUp(code uint64) error
ButtonDown(code uint32) error
KeyDown(code uint32) error
ButtonUp(code uint32) error
KeyUp(code uint32) error
ButtonPress(code uint32) error
KeyPress(codes ...uint32) error
ResetKeys()
ScreenConfigurations() map[int]ScreenConfiguration
SetScreenSize(ScreenSize) error
GetScreenSize() *ScreenSize
SetKeyboardLayout(layout string)
SetKeyboardModifiers(NumLock int, CapsLock int, ScrollLock int)
SetKeyboardMap(KeyboardMap) error
GetKeyboardMap() (*KeyboardMap, error)
SetKeyboardModifiers(mod KeyboardModifiers)
GetKeyboardModifiers() KeyboardModifiers
GetCursorImage() *CursorImage
GetScreenshotImage() *image.RGBA
}

View File

@ -70,7 +70,7 @@ type Keyboard struct {
Layout *string `json:"layout,omitempty"`
CapsLock *bool `json:"capsLock,omitempty"`
NumLock *bool `json:"numLock,omitempty"`
ScrollLock *bool `json:"scrollLock,omitempty"`
ScrollLock *bool `json:"scrollLock,omitempty"` // TODO: ScrollLock is deprecated.
}
type Control struct {
@ -128,7 +128,7 @@ type ScreenResolution struct {
ID string `json:"id,omitempty"`
Width int `json:"width"`
Height int `json:"height"`
Rate int `json:"rate"`
Rate int16 `json:"rate"`
}
type ScreenConfigurations struct {

View File

@ -35,7 +35,7 @@ type PayloadScroll struct {
type PayloadKey struct {
PayloadHeader
Key uint64
Key uint64 // TODO: uint32
}
func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) error {
@ -85,7 +85,7 @@ func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) e
}
if payload.Key < 8 {
err := manager.desktop.ButtonDown(int(payload.Key))
err := manager.desktop.ButtonDown(uint32(payload.Key))
if err != nil {
manager.logger.Warn().Err(err).Msg("button down failed")
return nil
@ -93,7 +93,7 @@ func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) e
manager.logger.Debug().Msgf("button down %d", payload.Key)
} else {
err := manager.desktop.KeyDown(uint64(payload.Key))
err := manager.desktop.KeyDown(uint32(payload.Key))
if err != nil {
manager.logger.Warn().Err(err).Msg("key down failed")
return nil
@ -109,7 +109,7 @@ func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) e
}
if payload.Key < 8 {
err := manager.desktop.ButtonUp(int(payload.Key))
err := manager.desktop.ButtonUp(uint32(payload.Key))
if err != nil {
manager.logger.Warn().Err(err).Msg("button up failed")
return nil
@ -117,7 +117,7 @@ func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) e
manager.logger.Debug().Msgf("button up %d", payload.Key)
} else {
err := manager.desktop.KeyUp(uint64(payload.Key))
err := manager.desktop.KeyUp(uint32(payload.Key))
if err != nil {
manager.logger.Warn().Err(err).Msg("key up failed")
return nil

View File

@ -140,41 +140,18 @@ func (h *MessageHandler) controlKeyboard(id string, session types.Session, paylo
return nil
}
h.desktop.SetKeyboardModifiers(types.KeyboardModifiers{
NumLock: payload.NumLock,
CapsLock: payload.CapsLock,
// TODO: ScrollLock is deprecated.
})
// change layout
if payload.Layout != nil {
h.desktop.SetKeyboardLayout(*payload.Layout)
return h.desktop.SetKeyboardMap(types.KeyboardMap{
Layout: *payload.Layout,
})
}
// set num lock
var NumLock = 0
if payload.NumLock == nil {
NumLock = -1
} else if *payload.NumLock {
NumLock = 1
}
// set caps lock
var CapsLock = 0
if payload.CapsLock == nil {
CapsLock = -1
} else if *payload.CapsLock {
CapsLock = 1
}
// set scroll lock
var ScrollLock = 0
if payload.ScrollLock == nil {
ScrollLock = -1
} else if *payload.ScrollLock {
ScrollLock = 1
}
h.logger.Debug().
Int("NumLock", NumLock).
Int("CapsLock", CapsLock).
Int("ScrollLock", ScrollLock).
Msg("setting keyboard modifiers")
h.desktop.SetKeyboardModifiers(NumLock, CapsLock, ScrollLock)
return nil
}

View File

@ -38,7 +38,7 @@ func (h *MessageHandler) screenResolution(id string, session types.Session) erro
Event: event.SCREEN_RESOLUTION,
Width: size.Width,
Height: size.Height,
Rate: int(size.Rate),
Rate: size.Rate,
}); err != nil {
h.logger.Warn().Err(err).Msgf("sending event %s has failed", event.SCREEN_RESOLUTION)
return err