Custom sizes with XRandR (#25)

* xserver dummy with RandR.

* update.

* remove screen configurations from xorg.

* screen size cannot be nil anymore.

* use predefined screen configurations.

* use screen configurations.

* fix error.

* remove comment.
This commit is contained in:
Miroslav Šedivý 2023-02-14 21:18:47 +01:00 committed by GitHub
parent bfabee12e2
commit f8b128e1e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 666 additions and 107 deletions

View File

@ -1,3 +1,33 @@
#
# Stage 0: Build xserver-xorg-video-dummy 0.3.8-2 with RandR support.
#
FROM debian:bullseye-slim as xserver-xorg-video-dummy
WORKDIR /usr/local/src
ENV DEBIAN_FRONTEND=noninteractive
RUN set -eux; \
cp /etc/apt/sources.list /etc/apt/sources.list~; \
sed -Ei 's/^deb /deb-src /' /etc/apt/sources.list; \
cat /etc/apt/sources.list~ >> /etc/apt/sources.list; \
apt-get update; \
apt-get install -y dpkg-dev git; \
apt-get build-dep -y xserver-xorg-video-dummy; \
git clone --depth 1 --branch xserver-xorg-video-dummy-1_0.3.8-2 https://salsa.debian.org/xorg-team/driver/xserver-xorg-video-dummy; \
#
# clean up
apt-get clean -y; \
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
COPY runtime/xdummy-randr.patch /tmp/xdummy-randr.patch
RUN set -eux; \
cd xserver-xorg-video-dummy; \
patch -p1 < /tmp/xdummy-randr.patch; \
bash ./autogen.sh; \
make; \
make install;
# #
# Stage 1: Build. # Stage 1: Build.
# #
@ -12,6 +42,10 @@ RUN set -eux; \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
libx11-dev libxrandr-dev libxtst-dev libgtk-3-dev \ libx11-dev libxrandr-dev libxtst-dev libgtk-3-dev \
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev; \ libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev; \
# install libxcvt-dev (not available in debian:bullseye)
wget http://ftp.de.debian.org/debian/pool/main/libx/libxcvt/libxcvt-dev_0.1.2-1_amd64.deb; \
wget http://ftp.de.debian.org/debian/pool/main/libx/libxcvt/libxcvt0_0.1.2-1_amd64.deb; \
apt-get install --no-install-recommends ./libxcvt0_0.1.2-1_amd64.deb ./libxcvt-dev_0.1.2-1_amd64.deb; \
# #
# clean up # clean up
apt-get clean -y; \ apt-get clean -y; \
@ -57,6 +91,10 @@ RUN set -eux; \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \ gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \
gstreamer1.0-pulseaudio; \ gstreamer1.0-pulseaudio; \
# install libxcvt0 (not available in debian:bullseye)
wget http://ftp.de.debian.org/debian/pool/main/libx/libxcvt/libxcvt0_0.1.2-1_amd64.deb; \
apt-get install --no-install-recommends ./libxcvt0_0.1.2-1_amd64.deb; \
rm ./libxcvt0_0.1.2-1_amd64.deb; \
# #
# create a non-root user # create a non-root user
groupadd --gid $USER_GID $USERNAME; \ groupadd --gid $USER_GID $USERNAME; \
@ -95,6 +133,9 @@ RUN set -eux; \
apt-get clean -y; \ apt-get clean -y; \
rm -rf /var/lib/apt/lists/* /var/cache/apt/* rm -rf /var/lib/apt/lists/* /var/cache/apt/*
# replace version
COPY --from=xserver-xorg-video-dummy /usr/local/lib/xorg/modules/drivers/dummy_drv.so /usr/lib/xorg/modules/drivers/dummy_drv.so
# #
# copy runtime configs # copy runtime configs
COPY --chown=neko:neko runtime/.Xresources /home/$USERNAME/.Xresources COPY --chown=neko:neko runtime/.Xresources /home/$USERNAME/.Xresources

View File

@ -1,6 +1,36 @@
ARG UBUNTU_RELEASE=20.04 ARG UBUNTU_RELEASE=20.04
ARG CUDA_VERSION=11.2.2 ARG CUDA_VERSION=11.2.2
#
# Stage 0: Build xserver-xorg-video-dummy 0.3.8-2 with RandR support.
#
FROM debian:bullseye-slim as xserver-xorg-video-dummy
WORKDIR /usr/local/src
ENV DEBIAN_FRONTEND=noninteractive
RUN set -eux; \
cp /etc/apt/sources.list /etc/apt/sources.list~; \
sed -Ei 's/^deb /deb-src /' /etc/apt/sources.list; \
cat /etc/apt/sources.list~ >> /etc/apt/sources.list; \
apt-get update; \
apt-get install -y dpkg-dev git; \
apt-get build-dep -y xserver-xorg-video-dummy; \
git clone --depth 1 --branch xserver-xorg-video-dummy-1_0.3.8-2 https://salsa.debian.org/xorg-team/driver/xserver-xorg-video-dummy; \
#
# clean up
apt-get clean -y; \
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
COPY runtime/xdummy-randr.patch /tmp/xdummy-randr.patch
RUN set -eux; \
cd xserver-xorg-video-dummy; \
patch -p1 < /tmp/xdummy-randr.patch; \
bash ./autogen.sh; \
make; \
make install;
# #
# Stage 1: Build. # Stage 1: Build.
# #
@ -15,6 +45,10 @@ RUN set -eux; \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
libx11-dev libxrandr-dev libxtst-dev libgtk-3-dev \ libx11-dev libxrandr-dev libxtst-dev libgtk-3-dev \
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev; \ libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev; \
# install libxcvt-dev (not available in debian:bullseye)
wget http://ftp.de.debian.org/debian/pool/main/libx/libxcvt/libxcvt-dev_0.1.2-1_amd64.deb; \
wget http://ftp.de.debian.org/debian/pool/main/libx/libxcvt/libxcvt0_0.1.2-1_amd64.deb; \
apt-get install --no-install-recommends ./libxcvt0_0.1.2-1_amd64.deb ./libxcvt-dev_0.1.2-1_amd64.deb; \
# #
# clean up # clean up
apt-get clean -y; \ apt-get clean -y; \
@ -73,6 +107,10 @@ RUN set -eux; \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \ gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \
gstreamer1.0-pulseaudio; \ gstreamer1.0-pulseaudio; \
# install libxcvt0 (not available in debian:bullseye)
wget http://ftp.de.debian.org/debian/pool/main/libx/libxcvt/libxcvt0_0.1.2-1_amd64.deb; \
apt-get install --no-install-recommends ./libxcvt0_0.1.2-1_amd64.deb; \
rm ./libxcvt0_0.1.2-1_amd64.deb; \
# #
# create a non-root user # create a non-root user
groupadd --gid $USER_GID $USERNAME; \ groupadd --gid $USER_GID $USERNAME; \
@ -111,6 +149,9 @@ RUN set -eux; \
apt-get clean -y; \ apt-get clean -y; \
rm -rf /var/lib/apt/lists/* /var/cache/apt/* rm -rf /var/lib/apt/lists/* /var/cache/apt/*
# replace version
COPY --from=xserver-xorg-video-dummy /usr/local/lib/xorg/modules/drivers/dummy_drv.so /usr/lib/xorg/modules/drivers/dummy_drv.so
# #
# Install and configure Vulkan manually # Install and configure Vulkan manually
RUN if [ "${UBUNTU_RELEASE}" = "18.04" ]; then apt-get update && apt-get install --no-install-recommends -y vulkan-utils; else apt-get update && apt-get install --no-install-recommends -y vulkan-tools; fi && \ RUN if [ "${UBUNTU_RELEASE}" = "18.04" ]; then apt-get update && apt-get install --no-install-recommends -y vulkan-utils; else apt-get update && apt-get install --no-install-recommends -y vulkan-tools; fi && \

View File

@ -20,12 +20,11 @@ type ScreenConfigurationPayload struct {
func (h *RoomHandler) screenConfiguration(w http.ResponseWriter, r *http.Request) error { func (h *RoomHandler) screenConfiguration(w http.ResponseWriter, r *http.Request) error {
size := h.desktop.GetScreenSize() size := h.desktop.GetScreenSize()
if size == nil { return utils.HttpSuccess(w, ScreenConfigurationPayload{
return utils.HttpInternalServerError().WithInternalMsg("unable to get screen configuration") Width: size.Width,
} Height: size.Height,
Rate: size.Rate,
payload := ScreenConfigurationPayload(*size) })
return utils.HttpSuccess(w, payload)
} }
func (h *RoomHandler) screenConfigurationChange(w http.ResponseWriter, r *http.Request) error { func (h *RoomHandler) screenConfigurationChange(w http.ResponseWriter, r *http.Request) error {
@ -34,30 +33,37 @@ func (h *RoomHandler) screenConfigurationChange(w http.ResponseWriter, r *http.R
return err return err
} }
size := types.ScreenSize(*data) size, err := h.desktop.SetScreenSize(types.ScreenSize{
if err := h.desktop.SetScreenSize(size); err != nil { Width: data.Width,
Height: data.Height,
Rate: data.Rate,
})
if err != nil {
return utils.HttpUnprocessableEntity("cannot set screen size").WithInternalErr(err) return utils.HttpUnprocessableEntity("cannot set screen size").WithInternalErr(err)
} }
payload := message.ScreenSize(*data) h.sessions.Broadcast(event.SCREEN_UPDATED, message.ScreenSize{
h.sessions.Broadcast(event.SCREEN_UPDATED, payload) Width: size.Width,
Height: size.Height,
Rate: size.Rate,
})
return utils.HttpSuccess(w, data) return utils.HttpSuccess(w, data)
} }
// TODO: remove.
func (h *RoomHandler) screenConfigurationsList(w http.ResponseWriter, r *http.Request) error { func (h *RoomHandler) screenConfigurationsList(w http.ResponseWriter, r *http.Request) error {
list := []ScreenConfigurationPayload{} configurations := h.desktop.ScreenConfigurations()
ScreenConfigurations := h.desktop.ScreenConfigurations() list := make([]ScreenConfigurationPayload, 0, len(configurations))
for _, size := range ScreenConfigurations { for _, conf := range configurations {
for _, fps := range size.Rates {
list = append(list, ScreenConfigurationPayload{ list = append(list, ScreenConfigurationPayload{
Width: size.Width, Width: conf.Width,
Height: size.Height, Height: conf.Height,
Rate: fps, Rate: conf.Rate,
}) })
} }
}
return utils.HttpSuccess(w, list) return utils.HttpSuccess(w, list)
} }

View File

@ -44,7 +44,7 @@ func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCt
} }
screen := desktop.GetScreenSize() screen := desktop.GetScreenSize()
pipeline, err := pipelineConf.GetPipeline(*screen) pipeline, err := pipelineConf.GetPipeline(screen)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -40,9 +40,9 @@ func (manager *DesktopManagerCtx) Start() {
xorg.GetScreenConfigurations() xorg.GetScreenConfigurations()
err := xorg.ChangeScreenSize(manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate) width, height, rate, err := xorg.ChangeScreenSize(manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate)
manager.logger.Err(err). manager.logger.Err(err).
Str("screen_size", fmt.Sprintf("%dx%d@%d", manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate)). Str("screen_size", fmt.Sprintf("%dx%d@%d", width, height, rate)).
Msgf("setting initial screen size") Msgf("setting initial screen size")
go xevent.EventLoop(manager.config.Display) go xevent.EventLoop(manager.config.Display)

View File

@ -66,11 +66,26 @@ func (manager *DesktopManagerCtx) ResetKeys() {
xorg.ResetKeys() xorg.ResetKeys()
} }
func (manager *DesktopManagerCtx) ScreenConfigurations() map[int]types.ScreenConfiguration { func (manager *DesktopManagerCtx) ScreenConfigurations() []types.ScreenSize {
return xorg.ScreenConfigurations var configs []types.ScreenSize
for _, size := range xorg.ScreenConfigurations {
for _, fps := range size.Rates {
// filter out all irrelevant rates
if fps > 60 || (fps > 30 && fps%10 != 0) {
continue
}
configs = append(configs, types.ScreenSize{
Width: size.Width,
Height: size.Height,
Rate: fps,
})
}
}
return configs
} }
func (manager *DesktopManagerCtx) SetScreenSize(size types.ScreenSize) error { func (manager *DesktopManagerCtx) SetScreenSize(size types.ScreenSize) (types.ScreenSize, error) {
mu.Lock() mu.Lock()
manager.emmiter.Emit("before_screen_size_change") manager.emmiter.Emit("before_screen_size_change")
@ -79,10 +94,15 @@ func (manager *DesktopManagerCtx) SetScreenSize(size types.ScreenSize) error {
mu.Unlock() mu.Unlock()
}() }()
return xorg.ChangeScreenSize(size.Width, size.Height, size.Rate) w, h, r, err := xorg.ChangeScreenSize(size.Width, size.Height, size.Rate)
return types.ScreenSize{
Width: w,
Height: h,
Rate: r,
}, err
} }
func (manager *DesktopManagerCtx) GetScreenSize() *types.ScreenSize { func (manager *DesktopManagerCtx) GetScreenSize() types.ScreenSize {
return xorg.GetScreenSize() return xorg.GetScreenSize()
} }

View File

@ -13,11 +13,20 @@ func (h *MessageHandlerCtx) screenSet(session types.Session, payload *message.Sc
return errors.New("is not the admin") return errors.New("is not the admin")
} }
data := types.ScreenSize(*payload) size, err := h.desktop.SetScreenSize(types.ScreenSize{
if err := h.desktop.SetScreenSize(data); err != nil { Width: payload.Width,
Height: payload.Height,
Rate: payload.Rate,
})
if err != nil {
return err return err
} }
h.sessions.Broadcast(event.SCREEN_UPDATED, payload) h.sessions.Broadcast(event.SCREEN_UPDATED, message.ScreenSize{
Width: size.Width,
Height: size.Height,
Rate: size.Rate,
})
return nil return nil
} }

View File

@ -1,8 +1,6 @@
package handler package handler
import ( import (
"errors"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -25,8 +23,10 @@ func (h *MessageHandlerCtx) systemInit(session types.Session) error {
} }
size := h.desktop.GetScreenSize() size := h.desktop.GetScreenSize()
if size == nil { screenSize := message.ScreenSize{
return errors.New("could not get screen size") Width: size.Width,
Height: size.Height,
Rate: size.Rate,
} }
sessions := map[string]message.SessionData{} sessions := map[string]message.SessionData{}
@ -44,7 +44,7 @@ func (h *MessageHandlerCtx) systemInit(session types.Session) error {
message.SystemInit{ message.SystemInit{
SessionId: session.ID(), SessionId: session.ID(),
ControlHost: controlHost, ControlHost: controlHost,
ScreenSize: message.ScreenSize(*size), ScreenSize: screenSize,
Sessions: sessions, Sessions: sessions,
Settings: h.sessions.Settings(), Settings: h.sessions.Settings(),
ScreencastEnabled: h.capture.Screencast().Enabled(), ScreencastEnabled: h.capture.Screencast().Enabled(),
@ -57,22 +57,22 @@ func (h *MessageHandlerCtx) systemInit(session types.Session) error {
} }
func (h *MessageHandlerCtx) systemAdmin(session types.Session) error { func (h *MessageHandlerCtx) systemAdmin(session types.Session) error {
screenSizesList := []message.ScreenSize{} configurations := h.desktop.ScreenConfigurations()
for _, size := range h.desktop.ScreenConfigurations() {
for _, rate := range size.Rates { list := make([]message.ScreenSize, 0, len(configurations))
screenSizesList = append(screenSizesList, message.ScreenSize{ for _, conf := range configurations {
Width: size.Width, list = append(list, message.ScreenSize{
Height: size.Height, Width: conf.Width,
Rate: rate, Height: conf.Height,
Rate: conf.Rate,
}) })
} }
}
broadcast := h.capture.Broadcast() broadcast := h.capture.Broadcast()
session.Send( session.Send(
event.SYSTEM_ADMIN, event.SYSTEM_ADMIN,
message.SystemAdmin{ message.SystemAdmin{
ScreenSizesList: screenSizesList, ScreenSizesList: list, // TODO: remove
BroadcastStatus: message.BroadcastStatus{ BroadcastStatus: message.BroadcastStatus{
IsActive: broadcast.Started(), IsActive: broadcast.Started(),
URL: broadcast.Url(), URL: broadcast.Url(),

View File

@ -188,16 +188,13 @@ func (config *VideoConfig) GetPipeline(screen ScreenSize) (string, error) {
}[:], " "), nil }[:], " "), nil
} }
func (config *VideoConfig) GetBitrateFn(getScreen func() *ScreenSize) func() (int, error) { func (config *VideoConfig) GetBitrateFn(getScreen func() ScreenSize) func() (int, error) {
return func() (int, error) { return func() (int, error) {
if config.Bitrate > 0 { if config.Bitrate > 0 {
return config.Bitrate, nil return config.Bitrate, nil
} }
screen := getScreen() screen := getScreen()
if screen == nil {
return 0, fmt.Errorf("screen is nil")
}
values := map[string]any{ values := map[string]any{
"width": screen.Width, "width": screen.Width,

View File

@ -19,12 +19,6 @@ type ScreenSize struct {
Rate int16 Rate int16
} }
type ScreenConfiguration struct {
Width int
Height int
Rates map[int]int16
}
type KeyboardModifiers struct { type KeyboardModifiers struct {
NumLock *bool NumLock *bool
CapsLock *bool CapsLock *bool
@ -57,9 +51,9 @@ type DesktopManager interface {
ButtonPress(code uint32) error ButtonPress(code uint32) error
KeyPress(codes ...uint32) error KeyPress(codes ...uint32) error
ResetKeys() ResetKeys()
ScreenConfigurations() map[int]ScreenConfiguration ScreenConfigurations() []ScreenSize
SetScreenSize(ScreenSize) error SetScreenSize(ScreenSize) (ScreenSize, error)
GetScreenSize() *ScreenSize GetScreenSize() ScreenSize
SetKeyboardMap(KeyboardMap) error SetKeyboardMap(KeyboardMap) error
GetKeyboardMap() (*KeyboardMap, error) GetKeyboardMap() (*KeyboardMap, error)
SetKeyboardModifiers(mod KeyboardModifiers) SetKeyboardModifiers(mod KeyboardModifiers)

View File

@ -229,6 +229,82 @@ void XKey(KeySym keysym, int down) {
XSync(display, 0); XSync(display, 0);
} }
Status XSetScreenConfiguration(int width, int height, short *rate) {
Display *display = getXDisplay();
Window root = RootWindow(display, 0);
XRRScreenConfiguration *conf = XRRGetScreenInfo(display, root);
XRRScreenSize *xrrs;
int num_sizes;
xrrs = XRRConfigSizes(conf, &num_sizes);
int size_index = -1;
for (int i = 0; i < num_sizes; i++) {
if (xrrs[i].width == width && xrrs[i].height == height) {
size_index = i;
break;
}
}
// if we cannot find the size
if (size_index == -1) {
return RRSetConfigFailed;
}
short current_rate = 0;
if (rate != NULL) {
short *rates;
int num_rates;
rates = XRRConfigRates(conf, size_index, &num_rates);
// try to find the nearest rate
short nearest_rate = 0;
float diff = 0;
for (int i = 0; i < num_rates; i++) {
if (nearest_rate == 0 || abs(rates[i] - *rate) < diff) {
nearest_rate = rates[i];
diff = abs(rates[i] - *rate);
}
}
if (nearest_rate != 0 && diff < 10) {
current_rate = nearest_rate;
}
*rate = current_rate;
}
Status status;
status = XRRSetScreenConfigAndRate(display, conf, root, size_index, RR_Rotate_0, current_rate, CurrentTime);
XRRFreeScreenConfigInfo(conf);
return status;
}
void XGetScreenConfiguration(int *width, int *height, short *rate) {
Display *display = getXDisplay();
Window root = RootWindow(display, 0);
XRRScreenConfiguration *conf = XRRGetScreenInfo(display, root);
Rotation current_rotation;
SizeID current_size_id = XRRConfigCurrentConfiguration(conf, &current_rotation);
XRRScreenSize *xrrs;
int num_sizes;
xrrs = XRRConfigSizes(conf, &num_sizes);
// if we cannot find the size
if (current_size_id >= num_sizes) {
return;
}
*width = xrrs[current_size_id].width;
*height = xrrs[current_size_id].height;
*rate = XRRConfigCurrentRate(conf);
XRRFreeScreenConfigInfo(conf);
}
void XGetScreenConfigurations() { void XGetScreenConfigurations() {
Display *display = getXDisplay(); Display *display = getXDisplay();
Window root = RootWindow(display, 0); Window root = RootWindow(display, 0);
@ -248,23 +324,71 @@ void XGetScreenConfigurations() {
} }
} }
void XSetScreenConfiguration(int index, short rate) { // Inspired by https://github.com/raboof/xrandr/blob/master/xrandr.c
void XCreateScreenMode(int width, int height, short rate) {
Display *display = getXDisplay(); Display *display = getXDisplay();
Window root = RootWindow(display, 0); Window root = RootWindow(display, 0);
XRRSetScreenConfigAndRate(display, XRRGetScreenInfo(display, root), root, index, RR_Rotate_0, rate, CurrentTime);
char name[128];
XRRModeInfo mode;
mode = XCreateScreenModeInfo(width, height, rate);
snprintf(name, sizeof name, "%dx%d_%d", width, height, rate);
mode.nameLength = strlen(name);
mode.name = name;
// create new mode
XRRCreateMode(display, root, &mode);
XSync(display, 0);
// find newly created mode in resources
RRMode mode_id;
XRRScreenResources *resources = XRRGetScreenResources(display, root);
for (int i = 0; i < resources->nmode; ++i) {
if (strcmp(resources->modes[i].name, mode.name) == 0) {
mode_id = resources->modes[i].id;
break;
}
}
// add new mode to all outputs
for (int i = 0; i < resources->noutput; ++i) {
XRRAddOutputMode(display, resources->outputs[i], mode_id);
}
XRRFreeScreenResources(resources);
} }
int XGetScreenSize() { // Inspired by https://fossies.org/linux/xwayland/hw/xwayland/xwayland-cvt.c
Display *display = getXDisplay(); XRRModeInfo XCreateScreenModeInfo(int hdisplay, int vdisplay, short vrefresh) {
XRRScreenConfiguration *conf = XRRGetScreenInfo(display, RootWindow(display, 0)); XRRModeInfo modeinfo;
Rotation original_rotation; memset(&modeinfo, 0, sizeof modeinfo);
return XRRConfigCurrentConfiguration(conf, &original_rotation);
}
short XGetScreenRate() { #ifdef _LIBCVT_H_
Display *display = getXDisplay(); struct libxcvt_mode_info *mode_info;
XRRScreenConfiguration *conf = XRRGetScreenInfo(display, RootWindow(display, 0));
return XRRConfigCurrentRate(conf); // get screen mode from libxcvt, if available
mode_info = libxcvt_gen_mode_info(hdisplay, vdisplay, vrefresh, false, false);
modeinfo.width = mode_info->hdisplay;
modeinfo.height = mode_info->vdisplay;
modeinfo.dotClock = mode_info->dot_clock * 1000;
modeinfo.hSyncStart = mode_info->hsync_start;
modeinfo.hSyncEnd = mode_info->hsync_end;
modeinfo.hTotal = mode_info->htotal;
modeinfo.vSyncStart = mode_info->vsync_start;
modeinfo.vSyncEnd = mode_info->vsync_end;
modeinfo.vTotal = mode_info->vtotal;
modeinfo.modeFlags = mode_info->mode_flags;
free(mode_info);
#else
// fallback to a simple mode without refresh rate
modeinfo.width = hdisplay;
modeinfo.height = vdisplay;
#endif
return modeinfo;
} }
void XSetKeyboardModifier(int mod, int on) { void XSetKeyboardModifier(int mod, int on) {

View File

@ -1,7 +1,7 @@
package xorg package xorg
/* /*
#cgo LDFLAGS: -lX11 -lXrandr -lXtst -lXfixes #cgo LDFLAGS: -lX11 -lXrandr -lXtst -lXfixes -lxcvt
#include "xorg.h" #include "xorg.h"
*/ */
@ -27,7 +27,13 @@ const (
KbdModNumLock KbdMod = 16 KbdModNumLock KbdMod = 16
) )
var ScreenConfigurations = make(map[int]types.ScreenConfiguration) type ScreenConfiguration struct {
Width int
Height int
Rates map[int]int16
}
var ScreenConfigurations = make(map[int]ScreenConfiguration)
var debounce_button = make(map[uint32]time.Time) var debounce_button = make(map[uint32]time.Time)
var debounce_key = make(map[uint32]time.Time) var debounce_key = make(map[uint32]time.Time)
@ -178,40 +184,46 @@ func CheckKeys(duration time.Duration) {
} }
} }
func ChangeScreenSize(width int, height int, rate int16) error { // set screen configuration, create new one if not exists
func ChangeScreenSize(width int, height int, rate int16) (int, int, int16, error) {
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
for index, size := range ScreenConfigurations { // round width and height to 8
if size.Width == width && size.Height == height { width = width - (width % 8)
for _, fps := range size.Rates { height = height - (height % 8)
if rate == fps {
C.XSetScreenConfiguration(C.int(index), C.short(fps)) // convert variables to C types
return nil c_width, c_height, c_rate := C.int(width), C.int(height), C.short(rate)
}
} // if screen configuration already exists, just set it
} if status := C.XSetScreenConfiguration(c_width, c_height, &c_rate); status == C.RRSetConfigSuccess {
return width, height, int16(c_rate), nil
} }
return fmt.Errorf("unknown screen configuration %dx%d@%d", width, height, rate) // create new screen configuration
C.XCreateScreenMode(c_width, c_height, c_rate)
// screen configuration should exist now, set it
if status := C.XSetScreenConfiguration(c_width, c_height, &c_rate); status == C.RRSetConfigSuccess {
return width, height, int16(c_rate), nil
}
return 0, 0, 0, fmt.Errorf("unknown screen configuration %dx%d@%d", width, height, rate)
} }
func GetScreenSize() *types.ScreenSize { func GetScreenSize() types.ScreenSize {
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
index := int(C.XGetScreenSize()) c_width, c_height, c_rate := C.int(0), C.int(0), C.short(0)
rate := int16(C.XGetScreenRate()) C.XGetScreenConfiguration(&c_width, &c_height, &c_rate)
if conf, ok := ScreenConfigurations[index]; ok { return types.ScreenSize{
return &types.ScreenSize{ Width: int(c_width),
Width: conf.Width, Height: int(c_height),
Height: conf.Height, Rate: int16(c_rate),
Rate: rate,
} }
}
return nil
} }
func SetKeyboardModifier(mod KbdMod, active bool) { func SetKeyboardModifier(mod KbdMod, active bool) {
@ -300,7 +312,7 @@ func GetScreenshotImage() *image.RGBA {
//export goCreateScreenSize //export goCreateScreenSize
func goCreateScreenSize(index C.int, width C.int, height C.int, mwidth C.int, mheight C.int) { func goCreateScreenSize(index C.int, width C.int, height C.int, mwidth C.int, mheight C.int) {
ScreenConfigurations[int(index)] = types.ScreenConfiguration{ ScreenConfigurations[int(index)] = ScreenConfiguration{
Width: int(width), Width: int(width),
Height: int(height), Height: int(height),
Rates: make(map[int]int16), Rates: make(map[int]int16),
@ -309,12 +321,5 @@ func goCreateScreenSize(index C.int, width C.int, height C.int, mwidth C.int, mh
//export goSetScreenRates //export goSetScreenRates
func goSetScreenRates(index C.int, rate_index C.int, rateC C.short) { func goSetScreenRates(index C.int, rate_index C.int, rateC C.short) {
rate := int16(rateC) ScreenConfigurations[int(index)].Rates[int(rate_index)] = 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

@ -7,6 +7,11 @@
#include <X11/extensions/XTest.h> #include <X11/extensions/XTest.h>
#include <X11/extensions/Xfixes.h> #include <X11/extensions/Xfixes.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include <string.h>
// for computing xrandr modelines at runtime
#include <libxcvt/libxcvt.h>
extern void goCreateScreenSize(int index, int width, int height, int mwidth, int mheight); extern void goCreateScreenSize(int index, int width, int height, int mwidth, int mheight);
extern void goSetScreenRates(int index, int rate_index, short rate); extern void goSetScreenRates(int index, int rate_index, short rate);
@ -31,10 +36,11 @@ static KeyCode XKeyEntryGet(KeySym keysym);
static KeyCode XkbKeysymToKeycode(Display *dpy, KeySym keysym); static KeyCode XkbKeysymToKeycode(Display *dpy, KeySym keysym);
void XKey(KeySym keysym, int down); void XKey(KeySym keysym, int down);
Status XSetScreenConfiguration(int width, int height, short *rate);
void XGetScreenConfiguration(int *width, int *height, short *rate);
void XGetScreenConfigurations(); void XGetScreenConfigurations();
void XSetScreenConfiguration(int index, short rate); void XCreateScreenMode(int width, int height, short rate);
int XGetScreenSize(); XRRModeInfo XCreateScreenModeInfo(int hdisplay, int vdisplay, short vrefresh);
short XGetScreenRate();
void XSetKeyboardModifier(int mod, int on); void XSetKeyboardModifier(int mod, int on);
char XGetKeyboardModifiers(); char XGetKeyboardModifiers();

316
runtime/xdummy-randr.patch Normal file
View File

@ -0,0 +1,316 @@
diff --git a/src/dummy.h b/src/dummy.h
index c3fdd6e..9c74f56 100644
--- a/src/dummy.h
+++ b/src/dummy.h
@@ -13,6 +13,8 @@
#include "compat-api.h"
+#define DUMMY_MAX_SCREENS 4
+
/* Supported chipsets */
typedef enum {
DUMMY_CHIP
@@ -72,6 +74,12 @@ typedef struct dummyRec
pointer* FBBase;
Bool (*CreateWindow)() ; /* wrapped CreateWindow */
Bool prop;
+ /* XRANDR support begin */
+ int num_screens;
+ struct _xf86Crtc *paCrtcs[DUMMY_MAX_SCREENS];
+ struct _xf86Output *paOutputs[DUMMY_MAX_SCREENS];
+ int connected_outputs;
+ /* XRANDR support end */
} DUMMYRec, *DUMMYPtr;
/* The privates of the DUMMY driver */
diff --git a/src/dummy_driver.c b/src/dummy_driver.c
index 2656602..069e330 100644
--- a/src/dummy_driver.c
+++ b/src/dummy_driver.c
@@ -34,6 +34,8 @@
#include <X11/extensions/Xv.h>
#endif
+#include "xf86Crtc.h"
+
/*
* Driver data structures.
*/
@@ -141,6 +143,219 @@ static XF86ModuleVersionInfo dummyVersRec =
{0,0,0,0}
};
+
+/************************
+ * XRANDR support begin *
+ ************************/
+
+static Bool dummy_config_resize(ScrnInfoPtr pScrn, int cw, int ch);
+static Bool DUMMYAdjustScreenPixmap(ScrnInfoPtr pScrn, int width, int height);
+
+static const xf86CrtcConfigFuncsRec DUMMYCrtcConfigFuncs = {
+ .resize = dummy_config_resize
+};
+
+
+static void
+dummy_crtc_dpms(xf86CrtcPtr crtc, int mode)
+{
+}
+
+static Bool
+dummy_crtc_lock (xf86CrtcPtr crtc)
+{
+ return FALSE;
+}
+
+static Bool
+dummy_crtc_mode_fixup (xf86CrtcPtr crtc, DisplayModePtr mode,
+ DisplayModePtr adjusted_mode)
+{
+ return TRUE;
+}
+
+static void
+dummy_crtc_stub (xf86CrtcPtr crtc)
+{
+}
+
+static void
+dummy_crtc_gamma_set (xf86CrtcPtr crtc, CARD16 *red,
+ CARD16 *green, CARD16 *blue, int size)
+{
+}
+
+static void *
+dummy_crtc_shadow_allocate (xf86CrtcPtr crtc, int width, int height)
+{
+ return NULL;
+}
+
+static void
+dummy_crtc_mode_set (xf86CrtcPtr crtc, DisplayModePtr mode,
+ DisplayModePtr adjusted_mode, int x, int y)
+{
+}
+
+static const xf86CrtcFuncsRec DUMMYCrtcFuncs = {
+ .dpms = dummy_crtc_dpms,
+ .save = NULL, /* These two are never called by the server. */
+ .restore = NULL,
+ .lock = dummy_crtc_lock,
+ .unlock = NULL, /* This will not be invoked if lock returns FALSE. */
+ .mode_fixup = dummy_crtc_mode_fixup,
+ .prepare = dummy_crtc_stub,
+ .mode_set = dummy_crtc_mode_set,
+ .commit = dummy_crtc_stub,
+ .gamma_set = dummy_crtc_gamma_set,
+ .shadow_allocate = dummy_crtc_shadow_allocate,
+ .shadow_create = NULL, /* These two should not be invoked if allocate
+ returns NULL. */
+ .shadow_destroy = NULL,
+ .set_cursor_colors = NULL,
+ .set_cursor_position = NULL,
+ .show_cursor = NULL,
+ .hide_cursor = NULL,
+ .load_cursor_argb = NULL,
+ .destroy = dummy_crtc_stub
+};
+
+static void
+dummy_output_stub (xf86OutputPtr output)
+{
+}
+
+static void
+dummy_output_dpms (xf86OutputPtr output, int mode)
+{
+}
+
+static int
+dummy_output_mode_valid (xf86OutputPtr output, DisplayModePtr mode)
+{
+ return MODE_OK;
+}
+
+static Bool
+dummy_output_mode_fixup (xf86OutputPtr output, DisplayModePtr mode,
+ DisplayModePtr adjusted_mode)
+{
+ return TRUE;
+}
+
+static void
+dummy_output_mode_set (xf86OutputPtr output, DisplayModePtr mode,
+ DisplayModePtr adjusted_mode)
+{
+ DUMMYPtr dPtr = DUMMYPTR(output->scrn);
+ int index = (int64_t)output->driver_private;
+
+ /* set to connected at first mode set */
+ dPtr->connected_outputs |= 1 << index;
+}
+
+/* The first virtual monitor is always connected. Others only after setting its
+ * mode */
+static xf86OutputStatus
+dummy_output_detect (xf86OutputPtr output)
+{
+ DUMMYPtr dPtr = DUMMYPTR(output->scrn);
+ int index = (int64_t)output->driver_private;
+
+ if (dPtr->connected_outputs & (1 << index))
+ return XF86OutputStatusConnected;
+ else
+ return XF86OutputStatusDisconnected;
+}
+
+static DisplayModePtr
+dummy_output_get_modes (xf86OutputPtr output)
+{
+ DisplayModePtr pModes = NULL, pMode, pModeSrc;
+
+ /* copy modes from config */
+ for (pModeSrc = output->scrn->modes; pModeSrc; pModeSrc = pModeSrc->next)
+ {
+ pMode = xnfcalloc(1, sizeof(DisplayModeRec));
+ memcpy(pMode, pModeSrc, sizeof(DisplayModeRec));
+ pMode->next = NULL;
+ pMode->prev = NULL;
+ pMode->name = strdup(pModeSrc->name);
+ pModes = xf86ModesAdd(pModes, pMode);
+ if (pModeSrc->next == output->scrn->modes)
+ break;
+ }
+ return pModes;
+}
+
+
+static const xf86OutputFuncsRec DUMMYOutputFuncs = {
+ .create_resources = dummy_output_stub,
+ .dpms = dummy_output_dpms,
+ .save = NULL, /* These two are never called by the server. */
+ .restore = NULL,
+ .mode_valid = dummy_output_mode_valid,
+ .mode_fixup = dummy_output_mode_fixup,
+ .prepare = dummy_output_stub,
+ .commit = dummy_output_stub,
+ .mode_set = dummy_output_mode_set,
+ .detect = dummy_output_detect,
+ .get_modes = dummy_output_get_modes,
+#ifdef RANDR_12_INTERFACE
+ .set_property = NULL,
+#endif
+ .destroy = dummy_output_stub
+};
+
+static Bool
+dummy_config_resize(ScrnInfoPtr pScrn, int cw, int ch)
+{
+ if (!pScrn->vtSema) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "We do not own the active VT, exiting.\n");
+ return TRUE;
+ }
+ return DUMMYAdjustScreenPixmap(pScrn, cw, ch);
+}
+
+Bool DUMMYAdjustScreenPixmap(ScrnInfoPtr pScrn, int width, int height)
+{
+ ScreenPtr pScreen = pScrn->pScreen;
+ PixmapPtr pPixmap = pScreen->GetScreenPixmap(pScreen);
+ DUMMYPtr dPtr = DUMMYPTR(pScrn);
+ uint64_t cbLine = (width * xf86GetBppFromDepth(pScrn, pScrn->depth) / 8 + 3) & ~3;
+ int displayWidth = cbLine * 8 / xf86GetBppFromDepth(pScrn, pScrn->depth);
+
+ if ( width == pScrn->virtualX
+ && height == pScrn->virtualY
+ && displayWidth == pScrn->displayWidth)
+ return TRUE;
+ if (!pPixmap) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to get the screen pixmap.\n");
+ return FALSE;
+ }
+ if (cbLine > UINT32_MAX || cbLine * height >= pScrn->videoRam * 1024)
+ {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Unable to set up a virtual screen size of %dx%d with %d Kb of video memory available. Please increase the video memory size.\n",
+ width, height, pScrn->videoRam);
+ return FALSE;
+ }
+ pScreen->ModifyPixmapHeader(pPixmap, width, height,
+ pScrn->depth, xf86GetBppFromDepth(pScrn, pScrn->depth), cbLine,
+ pPixmap->devPrivate.ptr);
+ pScrn->virtualX = width;
+ pScrn->virtualY = height;
+ pScrn->displayWidth = displayWidth;
+
+ return TRUE;
+}
+
+/**********************
+ * XRANDR support end *
+ **********************/
+
/*
* This is the module init data.
* Its name has to be the driver name followed by ModuleData
@@ -568,6 +783,56 @@ DUMMYScreenInit(SCREEN_INIT_ARGS_DECL)
xf86SetBlackWhitePixels(pScreen);
+ /* initialize XRANDR */
+ xf86CrtcConfigInit(pScrn, &DUMMYCrtcConfigFuncs);
+ /* FIXME */
+ dPtr->num_screens = DUMMY_MAX_SCREENS;
+
+ for (int i=0; i < dPtr->num_screens; i++) {
+ char szOutput[256];
+
+ dPtr->paCrtcs[i] = xf86CrtcCreate(pScrn, &DUMMYCrtcFuncs);
+ dPtr->paCrtcs[i]->driver_private = (void *)(uintptr_t)i;
+
+ /* Set up our virtual outputs. */
+ snprintf(szOutput, sizeof(szOutput), "DUMMY%u", i);
+ dPtr->paOutputs[i] = xf86OutputCreate(pScrn, &DUMMYOutputFuncs,
+ szOutput);
+
+
+ xf86OutputUseScreenMonitor(dPtr->paOutputs[i], FALSE);
+ dPtr->paOutputs[i]->possible_crtcs = 1 << i;
+ dPtr->paOutputs[i]->possible_clones = 0;
+ dPtr->paOutputs[i]->driver_private = (void *)(uintptr_t)i;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Created crtc (%p) and output %s (%p)\n",
+ (void *)dPtr->paCrtcs[i], szOutput,
+ (void *)dPtr->paOutputs[i]);
+
+ }
+
+ /* bitmask */
+ dPtr->connected_outputs = 1;
+
+ xf86CrtcSetSizeRange(pScrn, 64, 64, DUMMY_MAX_WIDTH, DUMMY_MAX_HEIGHT);
+
+
+ /* Now create our initial CRTC/output configuration. */
+ if (!xf86InitialConfiguration(pScrn, TRUE)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Initial CRTC configuration failed!\n");
+ return (FALSE);
+ }
+
+ /* Initialise randr 1.2 mode-setting functions and set first mode.
+ * Note that the mode won't be usable until the server has resized the
+ * framebuffer to something reasonable. */
+ if (!xf86CrtcScreenInit(pScreen)) {
+ return FALSE;
+ }
+ if (!xf86SetDesiredModes(pScrn)) {
+ return FALSE;
+ }
+ /* XRANDR initialization end */
+
#ifdef USE_DGA
DUMMYDGAInit(pScreen);
#endif