generate screenshot using Xlib.

This commit is contained in:
Miroslav Šedivý 2021-01-21 20:44:09 +01:00
parent 7ff6ada205
commit afd3dd2f56
8 changed files with 110 additions and 1 deletions

View File

@ -61,7 +61,8 @@ func (h *RoomHandler) Route(r chi.Router) {
})
r.Route("/screen", func(r chi.Router) {
r.Get("/", h.screenConfiguration)
r.With(auth.CanWatchOnly).Get("/", h.screenConfiguration)
r.With(auth.CanWatchOnly).Get("/image", h.screenImageGet)
r.With(auth.AdminsOnly).Post("/", h.screenConfigurationChange)
r.With(auth.AdminsOnly).Get("/configurations", h.screenConfigurationsList)

View File

@ -1,6 +1,9 @@
package room
import (
"bytes"
"image/jpeg"
"strconv"
"net/http"
"demodesk/neko/internal/types"
@ -72,3 +75,23 @@ func (h *RoomHandler) screenConfigurationsList(w http.ResponseWriter, r *http.Re
utils.HttpSuccess(w, list)
}
func (h *RoomHandler) screenImageGet(w http.ResponseWriter, r *http.Request) {
var options *jpeg.Options
if quality, err := strconv.Atoi(r.URL.Query().Get("quality")); err == nil {
options = &jpeg.Options{ quality }
} else {
options = &jpeg.Options{ 90 }
}
img := h.desktop.GetScreenshotImage()
out := new(bytes.Buffer)
err := jpeg.Encode(out, img, options)
if err != nil {
utils.HttpInternalServerError(w, err)
return
}
w.Header().Set("Content-Type", "image/jpeg")
w.Write(out.Bytes())
}

View File

@ -1,6 +1,7 @@
package desktop
import (
"image"
"regexp"
"os/exec"
@ -113,3 +114,7 @@ func (manager *DesktopManagerCtx) GetKeyboardModifiers() types.KeyboardModifiers
func (manager *DesktopManagerCtx) GetCursorImage() *types.CursorImage {
return xorg.GetCursorImage()
}
func (manager *DesktopManagerCtx) GetScreenshotImage() *image.RGBA {
return xorg.GetScreenshotImage()
}

View File

@ -136,3 +136,33 @@ 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

@ -13,6 +13,9 @@ import (
"time"
"unsafe"
"image"
"image/color"
"demodesk/neko/internal/types"
)
@ -244,6 +247,34 @@ func GetCursorImage() *types.CursorImage {
}
}
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
func goCreateScreenSize(index C.int, width C.int, height C.int, mwidth C.int, mheight C.int) {
ScreenConfigurations[int(index)] = types.ScreenConfiguration{

View File

@ -2,6 +2,7 @@
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/XTest.h>
#include <X11/extensions/Xfixes.h>
@ -27,3 +28,5 @@ short XGetScreenRate();
void XSetKeyboardModifier(int mod, int on);
char XGetKeyboardModifiers();
XFixesCursorImage *XGetCursorImage(void);
char *XGetScreenshot(int *w, int *h);

View File

@ -66,3 +66,14 @@ func CanHostOnly(next http.Handler) http.Handler {
}
})
}
func CanWatchOnly(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session := GetSession(r)
if !session.CanWatch() {
utils.HttpForbidden(w, "Only for members, that can watch.")
} else {
next.ServeHTTP(w, r)
}
})
}

View File

@ -1,5 +1,9 @@
package types
import (
"image"
)
type CursorImage struct {
Width uint16
Height uint16
@ -53,6 +57,7 @@ type DesktopManager interface {
SetKeyboardModifiers(mod KeyboardModifiers)
GetKeyboardModifiers() KeyboardModifiers
GetCursorImage() *CursorImage
GetScreenshotImage() *image.RGBA
// xevent
OnCursorChanged(listener func(serial uint64))