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
14 changed files with 666 additions and 107 deletions

View File

@ -229,6 +229,82 @@ void XKey(KeySym keysym, int down) {
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() {
Display *display = getXDisplay();
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();
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() {
Display *display = getXDisplay();
XRRScreenConfiguration *conf = XRRGetScreenInfo(display, RootWindow(display, 0));
Rotation original_rotation;
return XRRConfigCurrentConfiguration(conf, &original_rotation);
}
// Inspired by https://fossies.org/linux/xwayland/hw/xwayland/xwayland-cvt.c
XRRModeInfo XCreateScreenModeInfo(int hdisplay, int vdisplay, short vrefresh) {
XRRModeInfo modeinfo;
memset(&modeinfo, 0, sizeof modeinfo);
short XGetScreenRate() {
Display *display = getXDisplay();
XRRScreenConfiguration *conf = XRRGetScreenInfo(display, RootWindow(display, 0));
return XRRConfigCurrentRate(conf);
#ifdef _LIBCVT_H_
struct libxcvt_mode_info *mode_info;
// 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) {

View File

@ -1,7 +1,7 @@
package xorg
/*
#cgo LDFLAGS: -lX11 -lXrandr -lXtst -lXfixes
#cgo LDFLAGS: -lX11 -lXrandr -lXtst -lXfixes -lxcvt
#include "xorg.h"
*/
@ -27,7 +27,13 @@ const (
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_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()
defer mu.Unlock()
for index, size := range ScreenConfigurations {
if size.Width == width && size.Height == height {
for _, fps := range size.Rates {
if rate == fps {
C.XSetScreenConfiguration(C.int(index), C.short(fps))
return nil
}
}
}
// round width and height to 8
width = width - (width % 8)
height = height - (height % 8)
// convert variables to C types
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()
defer mu.Unlock()
index := int(C.XGetScreenSize())
rate := int16(C.XGetScreenRate())
c_width, c_height, c_rate := C.int(0), C.int(0), C.short(0)
C.XGetScreenConfiguration(&c_width, &c_height, &c_rate)
if conf, ok := ScreenConfigurations[index]; ok {
return &types.ScreenSize{
Width: conf.Width,
Height: conf.Height,
Rate: rate,
}
return types.ScreenSize{
Width: int(c_width),
Height: int(c_height),
Rate: int16(c_rate),
}
return nil
}
func SetKeyboardModifier(mod KbdMod, active bool) {
@ -300,7 +312,7 @@ func GetScreenshotImage() *image.RGBA {
//export goCreateScreenSize
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),
Height: int(height),
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
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
ScreenConfigurations[int(index)].Rates[int(rate_index)] = int16(rateC)
}

View File

@ -7,6 +7,11 @@
#include <X11/extensions/XTest.h>
#include <X11/extensions/Xfixes.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 goSetScreenRates(int index, int rate_index, short rate);
@ -31,10 +36,11 @@ static KeyCode XKeyEntryGet(KeySym keysym);
static KeyCode XkbKeysymToKeycode(Display *dpy, KeySym keysym);
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 XSetScreenConfiguration(int index, short rate);
int XGetScreenSize();
short XGetScreenRate();
void XCreateScreenMode(int width, int height, short rate);
XRRModeInfo XCreateScreenModeInfo(int hdisplay, int vdisplay, short vrefresh);
void XSetKeyboardModifier(int mod, int on);
char XGetKeyboardModifiers();