Archived
2
0

split remote to desktop and capture.

This commit is contained in:
Miroslav Šedivý
2022-09-12 22:12:47 +02:00
parent 0bca8c9d02
commit de4f6b45e5
26 changed files with 299 additions and 194 deletions

View File

@ -0,0 +1,260 @@
#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;
}
}
void XDisplaySet(char *input) {
NAME = strdup(input);
DIRTY = 1;
}
void XMove(int x, int y) {
Display *display = getXDisplay();
XWarpPointer(display, None, DefaultRootWindow(display), 0, 0, 0, 0, x, y);
XSync(display, 0);
}
void XScroll(int x, int y) {
int ydir = 4; /* Button 4 is up, 5 is down. */
int xdir = 6;
Display *display = getXDisplay();
if (y < 0) {
ydir = 5;
}
if (x < 0) {
xdir = 7;
}
int xi;
int yi;
for (xi = 0; xi < abs(x); xi++) {
XTestFakeButtonEvent(display, xdir, 1, CurrentTime);
XTestFakeButtonEvent(display, xdir, 0, CurrentTime);
}
for (yi = 0; yi < abs(y); yi++) {
XTestFakeButtonEvent(display, ydir, 1, CurrentTime);
XTestFakeButtonEvent(display, ydir, 0, CurrentTime);
}
XSync(display, 0);
}
void XButton(unsigned int button, int down) {
if (button != 0) {
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) {
XkbDescPtr xkb;
XkbStateRec state;
unsigned int mods;
unsigned keycode;
xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
if (!xkb)
return 0;
XkbGetState(dpy, XkbUseCoreKbd, &state);
// XkbStateFieldFromRec() doesn't work properly because
// state.lookup_mods isn't properly updated, so we do this manually
mods = XkbBuildCoreState(XkbStateMods(&state), state.group);
for (keycode = xkb->min_key_code;
keycode <= xkb->max_key_code;
keycode++) {
KeySym cursym;
unsigned int out_mods;
XkbTranslateKeyCode(xkb, keycode, mods, &out_mods, &cursym);
if (cursym == keysym)
break;
}
if (keycode > xkb->max_key_code)
keycode = 0;
XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
// Shift+Tab is usually ISO_Left_Tab, but RFB hides this fact. Do
// another attempt if we failed the initial lookup
if ((keycode == 0) && (keysym == XK_Tab) && (mods & ShiftMask))
return XkbKeysymToKeycode(dpy, XK_ISO_Left_Tab);
return keycode;
}
void XKey(KeySym key, int down) {
Display *display = getXDisplay();
KeyCode code = 0;
if (!down)
code = XKeysPop(key);
if (!code)
code = XkbKeysymToKeycode(display, key);
if (!code) {
int min, max, numcodes;
XDisplayKeycodes(display, &min, &max);
XGetKeyboardMapping(display, min, max-min, &numcodes);
code = (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);
}
if (!code)
return;
if (down)
XKeysInsert(key, code);
XTestFakeKeyEvent(display, code, down, CurrentTime);
XSync(display, 0);
}
void XGetScreenConfigurations() {
Display *display = getXDisplay();
Window root = RootWindow(display, 0);
XRRScreenSize *xrrs;
int num_sizes;
xrrs = XRRSizes(display, 0, &num_sizes);
for(int i = 0; i < num_sizes; i ++) {
short *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 ++) {
goSetScreenRates(i, j, rates[j]);
}
}
}
void XSetScreenConfiguration(int index, short rate) {
Display *display = getXDisplay();
Window root = RootWindow(display, 0);
XRRSetScreenConfigAndRate(display, XRRGetScreenInfo(display, root), root, index, RR_Rotate_0, rate, CurrentTime);
}
int XGetScreenSize() {
Display *display = getXDisplay();
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));
return XRRConfigCurrentRate(conf);
}
void SetKeyboardModifiers(int num_lock, int caps_lock, int scroll_lock) {
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);
}
XFlush(display);
}

View File

@ -0,0 +1,212 @@
package xorg
/*
#cgo CFLAGS: -I/usr/local/include/
#cgo LDFLAGS: -lX11 -lXtst -lXrandr -lxcb
#include "xorg.h"
*/
import "C"
import (
"fmt"
"sync"
"time"
"unsafe"
"m1k1o/neko/internal/types"
)
var ScreenConfigurations = make(map[int]types.ScreenConfiguration)
var debounce_button = make(map[int]time.Time)
var debounce_key = make(map[uint64]time.Time)
var mu = sync.Mutex{}
func init() {
C.XGetScreenConfigurations()
}
func Display(display string) {
mu.Lock()
defer mu.Unlock()
displayUnsafe := C.CString(display)
defer C.free(unsafe.Pointer(displayUnsafe))
C.XDisplaySet(displayUnsafe)
}
func Move(x, y int) {
mu.Lock()
defer mu.Unlock()
C.XMove(C.int(x), C.int(y))
}
func Scroll(x, y int) {
mu.Lock()
defer mu.Unlock()
C.XScroll(C.int(x), C.int(y))
}
func ButtonDown(code int) 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
}
func KeyDown(code uint64) error {
mu.Lock()
defer mu.Unlock()
if _, ok := debounce_key[code]; ok {
return fmt.Errorf("debounced key %v", code)
}
debounce_key[code] = time.Now()
C.XKey(C.KeySym(code), C.int(1))
return nil
}
func ButtonUp(code int) 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
}
func KeyUp(code uint64) error {
mu.Lock()
defer mu.Unlock()
if _, ok := debounce_key[code]; !ok {
return fmt.Errorf("debounced key %v", code)
}
delete(debounce_key, code)
C.XKey(C.KeySym(code), C.int(0))
return nil
}
func ResetKeys() {
for code := range debounce_button {
_ = ButtonUp(code)
delete(debounce_button, code)
}
for code := range debounce_key {
_ = KeyUp(code)
delete(debounce_key, code)
}
}
func CheckKeys(duration time.Duration) {
t := time.Now()
for code, start := range debounce_button {
if t.Sub(start) < duration {
continue
}
_ = ButtonUp(code)
delete(debounce_button, code)
}
for code, start := range debounce_key {
if t.Sub(start) < duration {
continue
}
_ = KeyUp(code)
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 {
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 {
C.XSetScreenConfiguration(C.int(index), C.short(fps))
return nil
}
}
}
}
return fmt.Errorf("unknown configuration")
}
func GetScreenSize() *types.ScreenSize {
mu.Lock()
defer mu.Unlock()
index := int(C.XGetScreenSize())
rate := int16(C.XGetScreenRate())
if conf, ok := ScreenConfigurations[index]; ok {
return &types.ScreenSize{
Width: conf.Width,
Height: conf.Height,
Rate: rate,
}
}
return nil
}
func SetKeyboardModifiers(num_lock int, caps_lock int, scroll_lock int) {
mu.Lock()
defer mu.Unlock()
C.SetKeyboardModifiers(C.int(num_lock), C.int(caps_lock), C.int(scroll_lock))
}
//export goCreateScreenSize
func goCreateScreenSize(index C.int, width C.int, height C.int, mwidth C.int, mheight C.int) {
ScreenConfigurations[int(index)] = types.ScreenConfiguration{
Width: int(width),
Height: int(height),
Rates: make(map[int]int16),
}
}
//export goSetScreenRates
func goSetScreenRates(index C.int, rate_index C.int, rate C.short) {
ScreenConfigurations[int(index)].Rates[int(rate_index)] = int16(rate)
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <X11/XKBlib.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() */
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);
void XMove(int x, int y);
void XScroll(int x, int y);
void XButton(unsigned int button, int down);
void XKey(unsigned long key, int down);
void XGetScreenConfigurations();
void XSetScreenConfiguration(int index, short rate);
int XGetScreenSize();
short XGetScreenRate();
void XDisplayClose(void);
void XDisplaySet(char *input);
void SetKeyboardModifiers(int num_lock, int caps_lock, int scroll_lock);