Skip to content

Commit

Permalink
#56 add resolutions to Xvfb using XRRCreateMode, XRRAddOutputMode and…
Browse files Browse the repository at this point in the history
… XRRSetScreenSize

git-svn-id: https://xpra.org/svn/Xpra/trunk@16994 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Sep 29, 2017
1 parent eed27ba commit 61f49cd
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 9 deletions.
141 changes: 141 additions & 0 deletions src/xpra/x11/bindings/randr_bindings.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ from xpra.log import Logger
log = Logger("x11", "bindings", "randr")


from libc.stdint cimport uintptr_t
ctypedef unsigned long CARD32

cdef extern from "X11/X.h":
Expand Down Expand Up @@ -47,13 +48,42 @@ cdef extern from "X11/extensions/randr.h":
cdef unsigned int RR_Rotate_0

cdef extern from "X11/extensions/Xrandr.h":
ctypedef XID RRMode
ctypedef XID RROutput
ctypedef XID RRCrtc
ctypedef unsigned long XRRModeFlags

Bool XRRQueryExtension(Display *, int *, int *)
Status XRRQueryVersion(Display *, int * major, int * minor)
ctypedef struct XRRScreenSize:
int width, height
int mwidth, mheight
XRRScreenSize *XRRSizes(Display *dpy, int screen, int *nsizes)
void XRRSetScreenSize(Display *dpy, Window w, int width, int height, int mmWidth, int mmHeight)
ctypedef struct XRRModeInfo:
RRMode id
unsigned int width
unsigned int height
unsigned long dotClock
unsigned int hSyncStart
unsigned int hSyncEnd
unsigned int hTotal
unsigned int hSkew
unsigned int vSyncStart
unsigned int vSyncEnd
unsigned int vTotal
char *name
unsigned int nameLength
XRRModeFlags modeFlags
ctypedef struct XRRScreenResources:
Time timestamp
Time configTimestamp
int ncrtc
RRCrtc *crtcs
int noutput
RROutput *outputs
int nmode
XRRModeInfo *modes

ctypedef unsigned short SizeID
ctypedef struct XRRScreenConfiguration:
Expand All @@ -68,6 +98,13 @@ cdef extern from "X11/extensions/Xrandr.h":
SizeID XRRConfigCurrentConfiguration(XRRScreenConfiguration *config, Rotation *rotation)

void XRRFreeScreenConfigInfo(XRRScreenConfiguration *)
XRRScreenResources *XRRGetScreenResourcesCurrent(Display *dpy, Window window)
void XRRFreeScreenResources(XRRScreenResources *resources)

XRRModeInfo *XRRAllocModeInfo(char *name, int nameLength)
RRMode XRRCreateMode(Display *dpy, Window window, XRRModeInfo *modeInfo)
void XRRAddOutputMode(Display *dpy, RROutput output, RRMode mode)
void XRRFreeModeInfo(XRRModeInfo *modeInfo)

int XScreenCount(Display *display)
int XDisplayWidthMM(Display *display, int screen_number)
Expand Down Expand Up @@ -240,3 +277,107 @@ cdef class _RandRBindings(_X11CoreBindings):

def set_screen_size(self, width, height):
return self._set_screen_size(width, height)


def add_screen_size(self, unsigned int w, unsigned int h):
log("add_screen_size(%i, %i)", w, h)
cdef Window window
cdef XRRModeInfo *new_mode
cdef XRRScreenResources *rsc
cdef RRMode mode
cdef RROutput output

#monitor settings as set in xorg.conf...
cdef unsigned int maxPixelClock = 230*1000*1000 #230MHz
cdef unsigned int minHSync = 10*1000 #10KHz
cdef unsigned int maxHSync = 300*1000 #300KHz
cdef unsigned int minVSync = 10 #10Hz
cdef unsigned int maxVSync = 300 #30Hz
cdef double idealVSync = 50.0
cdef double timeHFront = 0.07 #0.074219; 0.075; Width of the black border on right edge of the screen
cdef double timeHSync = 0.1 #0.107422; 0.1125; Sync pulse duration
cdef double timeHBack = 0.15 #0.183594; 0.1875; Width of the black border on left edge of the screen
cdef double timeVBack = 0.06 #0.031901; 0.055664; // Adjust this to move picture up/down
cdef double yFactor = 1 #no interlace (0.5) or doublescan (2)

from xpra.util import roundup
w = roundup(w, 4)
name = "%sx%s" % (w, h)
new_mode = XRRAllocModeInfo(name, len(name))
try:
window = XDefaultRootWindow(self.display)

xFront = int(w * timeHFront)
xSync = int(w * timeHSync)
xBack = int(w * timeHBack)
xTotal = w + xFront + xSync + xBack
yFront = 1
ySync = 3
yBack = int(h * timeVBack)
yTotal = h + yFront + ySync + yBack

modeMaxClock = maxPixelClock
if (maxHSync * xTotal)<maxPixelClock:
modeMaxClock = maxHSync * xTotal
tmp = maxVSync * xTotal * yTotal * yFactor
if tmp<modeMaxClock:
modeMaxClock = tmp
modeMinClock = minHSync * xTotal
# Monitor minVSync too low? => increase mode minimum pixel clock
tmp = minVSync * xTotal * yTotal * yFactor
if tmp > modeMinClock:
modeMinClock = tmp
# If minimum clock > maximum clock, the mode is impossible...
if modeMinClock > modeMaxClock:
log.warn("Warning: cannot add mode %s", name)
log.warn(" no suitable clocks could be found")
return None

idealClock = idealVSync * xTotal * yTotal * yFactor
clock = idealClock;
if clock < modeMinClock:
clock = modeMinClock
elif clock > modeMaxClock:
clock = modeMaxClock

log("Modeline %sx%s %s %s %s %s %s %s %s %s %s", w, h, clock/1000/1000,
w, w+xFront, w+xFront+xSync, xTotal,
h, h+yFront, h+yFront+ySync, yTotal)
new_mode.width = w
new_mode.height = h
new_mode.dotClock = long(clock)
new_mode.hSyncStart = int(w+xFront)
new_mode.hSyncEnd = int(w+xFront+xSync)
new_mode.hTotal = int(xTotal)
new_mode.hSkew = 0
new_mode.vSyncStart = int(h+yFront)
new_mode.vSyncEnd = int(h+yFront+ySync)
new_mode.vTotal = int(yTotal)
new_mode.modeFlags = 0
mode = XRRCreateMode(self.display, window, new_mode)
log("XRRCreateMode returned %#x" % mode)
if mode<=0:
return None
#now add it to the output:
try:
rsc = XRRGetScreenResourcesCurrent(self.display, window)
log("screen_resources: crtcs=%s, outputs=%s, modes=%s", rsc.ncrtc, rsc.noutput, rsc.nmode)
if rsc.noutput!=1:
log.warn("Warning: unexpected number of outputs: %s", rsc.noutput)
return None
output = rsc.outputs[0]
log("adding mode %#x to output %#x", mode, output)
XRRAddOutputMode(self.display, output, mode)
finally:
XRRFreeScreenResources(rsc)
finally:
XRRFreeModeInfo(new_mode)
return w, h

def xrr_set_screen_size(self, w, h, xdpi, ydpi):
#and now use it:
cdef Window window = XDefaultRootWindow(self.display)
wmm = int(w*25.4/xdpi + 0.5)
hmm = int(h*25.4/ydpi + 0.5)
log("XRRSetScreenSize(%#x, %#x, %i, %i, %i, %i)", <uintptr_t> self.display, window, w, h, wmm, hmm)
XRRSetScreenSize(self.display, window, w, h, wmm, hmm)
52 changes: 43 additions & 9 deletions src/xpra/x11/x11_server_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ def init(self, opts):

def do_init(self, opts):
self.randr = opts.resize_display
self.randr_initial_sizes = []
self.randr_added_sizes = []
self.fake_xinerama = opts.fake_xinerama
self.current_xinerama_config = None
self.x11_init()
Expand All @@ -117,11 +119,9 @@ def x11_init(self):

def init_randr(self):
self.randr = RandR.has_randr()
if self.randr and len(RandR.get_screen_sizes())<=1:
#disable randr when we are dealing with a Xvfb
#with only one resolution available
#since we don't support adding them on the fly yet
self.randr = False
log("randr=%s", self.randr)
self.randr_initial_sizes = RandR.get_screen_sizes()
log("initial screen sizes=%s", self.randr_initial_sizes)
if self.randr:
display = gdk.display_get_default()
i=0
Expand Down Expand Up @@ -326,7 +326,12 @@ def get_ui_info(self, proto, wids=None, *args):
with xsync:
sizes = RandR.get_screen_sizes()
if self.randr and len(sizes)>=0:
sinfo["randr"] = {"options" : list(reversed(sorted(sizes)))}
sinfo["randr"] = {
"" : True,
"options" : list(reversed(sorted(sizes))),
"initial" : self.randr_initial_sizes,
"added" : self.randr_added_sizes,
}
except:
pass
return info
Expand Down Expand Up @@ -475,6 +480,23 @@ def get_best_screen_size(self, desired_w, desired_h, bigger=True):
return self.do_get_best_screen_size(desired_w, desired_h, bigger)

def do_get_best_screen_size(self, desired_w, desired_h, bigger=True):
#ugly hackish way of detecting Xvfb with randr,
#assume that it has only one resolution pre-defined:
if len(self.randr_initial_sizes)==1:
try:
with xsync:
v = RandR.add_screen_size(desired_w, desired_h)
if v:
self.randr_added_sizes.append(v)
#we have to wait a little bit
#to make sure that everything sees the new resolution
#(ideally this method would be split in two and this would be a callback)
import time
time.sleep(0.5)
return v
except Exception as e:
screenlog.warn("Warning: failed to add resolution %ix%i:", desired_w, desired_h)
screenlog.warn(" %s", e)
#try to find the best screen size to resize to:
new_size = None
closest = {}
Expand Down Expand Up @@ -574,16 +596,28 @@ def set_screen_size(self, desired_w, desired_h):
tw, th = temp[k]
screenlog.info("temporarily switching to %sx%s as a Xinerama workaround", tw, th)
RandR.set_screen_size(tw, th)
screenlog("calling RandR.set_screen_size(%s, %s)", w, h)
screenlog("randr_added_sizes=%s", self.randr_added_sizes)
with xsync:
RandR.get_screen_size()
#Xdummy with randr 1.2:
screenlog("using XRRSetScreenConfigAndRate with %ix%i", w, h)
with xsync:
RandR.set_screen_size(w, h)
if (w, h) in self.randr_added_sizes:
#Xvfb with randr > 1.2: the resolution has been added
#we can use XRRSetScreenSize:
try:
with xsync:
RandR.xrr_set_screen_size(w, h, self.xdpi or self.dpi, self.ydpi or self.dpi)
except Exception:
screenlog("XRRSetScreenSize failed", exc_info=True)
screenlog("calling RandR.get_screen_size()")
root_w, root_h = RandR.get_screen_size()
screenlog("RandR.get_screen_size()=%s,%s", root_w, root_h)
screenlog("RandR.get_vrefresh()=%s", RandR.get_vrefresh())
if root_w!=w or root_h!=h:
screenlog.error("odd, failed to set the new resolution, "
"tried to set it to %sx%s and ended up with %sx%s", w, h, root_w, root_h)
screenlog.warn("Warning: tried to set resolution to %ix%i", w, h)
screenlog.warn(" and ended up with %ix%i", root_w, root_h)
else:
msg = "server virtual display now set to %sx%s" % (root_w, root_h)
if desired_w!=root_w or desired_h!=root_h:
Expand Down

0 comments on commit 61f49cd

Please sign in to comment.