Browse Source

Add RandR support for x0vncserver

Change Xserver screen through libXrandr. For complex configurations,
all outputs must have corresponding size modes. As a special case, if
the client requests a single screen with an odd size (for example when
adjusting the size of a non-fullscreen vncviewer), find a smaller
suitable mode, and reduce the framebuffer size as well.
tags/v1.8.90
Peter Åstrand (astrand) 6 years ago
parent
commit
242c5b2c8a

+ 3
- 0
cmake/StaticBuild.cmake View File

@@ -109,6 +109,9 @@ if(BUILD_STATIC)
if(X11_Xdamage_LIB)
set(X11_Xdamage_LIB "-Wl,-Bstatic -lXdamage -Wl,-Bdynamic")
endif()
if(X11_Xrandr_LIB)
set(X11_Xrandr_LIB "-Wl,-Bstatic -lXrandr -lXrender -Wl,-Bdynamic")
endif()
endif()
endif()


+ 10
- 1
unix/x0vncserver/CMakeLists.txt View File

@@ -1,4 +1,5 @@
include_directories(${X11_INCLUDE_DIR})
include_directories(${CMAKE_SOURCE_DIR}/unix/common)
include_directories(${CMAKE_SOURCE_DIR}/unix/tx)
include_directories(${CMAKE_SOURCE_DIR}/unix)
include_directories(${CMAKE_SOURCE_DIR}/common)
@@ -15,10 +16,11 @@ add_executable(x0vncserver
x0vncserver.cxx
XPixelBuffer.cxx
XDesktop.cxx
RandrGlue.c
../vncconfig/QueryConnectDialog.cxx
)

target_link_libraries(x0vncserver tx rfb network rdr)
target_link_libraries(x0vncserver tx rfb network rdr unixcommon)

if(X11_FOUND AND X11_XTest_LIB)
add_definitions(-DHAVE_XTEST)
@@ -41,6 +43,13 @@ else()
message(WARNING "No XFIXES extension. x0vncserver will not be able to show cursors.")
endif()

if(X11_FOUND AND X11_Xrandr_LIB)
add_definitions(-DHAVE_XRANDR)
target_link_libraries(x0vncserver ${X11_Xrandr_LIB})
else()
message(WARNING "No Xrandr extension. x0vncserver will not be able to resize session.")
endif()

target_link_libraries(x0vncserver ${X11_LIBRARIES})

install(TARGETS x0vncserver DESTINATION ${BIN_DIR})

+ 9
- 3
unix/x0vncserver/Geometry.cxx View File

@@ -35,10 +35,16 @@ StringParameter Geometry::m_geometryParam("Geometry",
"");

Geometry::Geometry(int fullWidth, int fullHeight)
: m_fullWidth(fullWidth),
m_fullHeight(fullHeight),
m_rect(0, 0, fullWidth, fullHeight)
{
recalc(fullWidth, fullHeight);
}

void Geometry::recalc(int fullWidth, int fullHeight)
{
m_fullWidth = fullWidth;
m_fullHeight = fullHeight;
m_rect.setXYWH(0, 0, fullWidth, fullHeight);

// Parse geometry specification and save the result in m_rect.
const char *param = m_geometryParam.getData();
bool geometrySpecified = (strlen(param) > 0);

+ 1
- 0
unix/x0vncserver/Geometry.h View File

@@ -30,6 +30,7 @@ class Geometry
{
public:
Geometry(int fullWidth, int fullHeight);
void recalc(int fullWidth, int fullHeight);

// Return coordinates and dimensions that specify a rectangular part
// of the desktop that would be shown to RFB clients. This

+ 511
- 0
unix/x0vncserver/RandrGlue.c View File

@@ -0,0 +1,511 @@
/* Copyright 2018 Peter Astrand <astrand@cendio.se> for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

#ifdef HAVE_XRANDR
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#include <stdlib.h>
#include <string.h>

#include "RandrGlue.h"

typedef struct _vncGlueContext {
Display *dpy;
XRRScreenResources *res;
} vncGlueContext;

static vncGlueContext randrGlueContext;

void vncSetGlueContext(Display *dpy, void *res)
{
randrGlueContext.dpy = dpy;
randrGlueContext.res = (XRRScreenResources *)res;
}

static RRMode vncRandRGetMatchingMode(XRROutputInfo *output,
unsigned int width, unsigned int height)
{
vncGlueContext *ctx = &randrGlueContext;

/*
* We're not going to change which modes are preferred, but let's
* see if we can at least find a mode with matching dimensions.
*/

if (output->crtc) {
XRRCrtcInfo *crtc;
unsigned int swap;

crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
if (!crtc)
return None;

switch (crtc->rotation) {
case RR_Rotate_90:
case RR_Rotate_270:
swap = width;
width = height;
height = swap;
break;
}

XRRFreeCrtcInfo(crtc);
}

for (int i = 0; i < ctx->res->nmode; i++) {
for (int j = 0; j < output->nmode; j++) {
if ((output->modes[j] == ctx->res->modes[i].id) &&
(ctx->res->modes[i].width == width) &&
(ctx->res->modes[i].height == height)) {
return ctx->res->modes[i].id;
}
}
}

return None;
}

int vncGetScreenWidth(void)
{
vncGlueContext *ctx = &randrGlueContext;
return DisplayWidth(ctx->dpy, DefaultScreen(ctx->dpy));
}

int vncGetScreenHeight(void)
{
vncGlueContext *ctx = &randrGlueContext;
return DisplayHeight(ctx->dpy, DefaultScreen(ctx->dpy));
}

int vncRandRIsValidScreenSize(int width, int height)
{
vncGlueContext *ctx = &randrGlueContext;
/* Assert size ranges */
int minwidth, minheight, maxwidth, maxheight;
int ret = XRRGetScreenSizeRange(ctx->dpy, DefaultRootWindow(ctx->dpy),
&minwidth, &minheight,
&maxwidth, &maxheight);
if (!ret) {
return 0;
}
if (width < minwidth || maxwidth < width) {
return 0;
}
if (height < minheight || maxheight < height) {
return 0;
}

return 1;
}

int vncRandRResizeScreen(int width, int height)
{
vncGlueContext *ctx = &randrGlueContext;

int xwidth = DisplayWidth(ctx->dpy, DefaultScreen(ctx->dpy));
int xheight = DisplayHeight(ctx->dpy, DefaultScreen(ctx->dpy));
int xwidthmm = DisplayWidthMM(ctx->dpy, DefaultScreen(ctx->dpy));
int xheightmm = DisplayHeightMM(ctx->dpy, DefaultScreen(ctx->dpy));

/* Try to retain DPI when we resize */
XRRSetScreenSize(ctx->dpy, DefaultRootWindow(ctx->dpy), width, height,
xwidthmm * width / xwidth,
xheightmm * height / xheight);

return 1;
}

void vncRandRUpdateSetTime(void)
{

}

int vncRandRHasOutputClones(void)
{
vncGlueContext *ctx = &randrGlueContext;
for (int i = 0; i < ctx->res->ncrtc; i++) {
XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, ctx->res->crtcs[i]);
if (!crtc) {
return 0;
}
if (crtc->noutput > 1) {
XRRFreeCrtcInfo (crtc);
return 1;
}
XRRFreeCrtcInfo (crtc);
}
return 0;
}

int vncRandRGetOutputCount(void)
{
vncGlueContext *ctx = &randrGlueContext;
return ctx->res->noutput;
}

int vncRandRGetAvailableOutputs(void)
{
vncGlueContext *ctx = &randrGlueContext;

int availableOutputs;
RRCrtc *usedCrtcs;
int numUsed;

int i, j, k;

usedCrtcs = (RRCrtc*)malloc(sizeof(RRCrtc) * ctx->res->ncrtc);
if (usedCrtcs == NULL)
return 0;

/*
* This gets slightly complicated because we might need to hook a CRTC
* up to the output, but also check that we don't try to use the same
* CRTC for multiple outputs.
*/
availableOutputs = 0;
numUsed = 0;
for (i = 0;i < ctx->res->noutput; i++) {
XRROutputInfo *output;

output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[i]);
if (!output) {
continue;
}

if (output->crtc != None)
availableOutputs++;
else {
for (j = 0;j < output->ncrtc;j++) {
XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtcs[j]);
if (!crtc) {
continue;
}
if (crtc->noutput != 0) {
XRRFreeCrtcInfo(crtc);
continue;
}
XRRFreeCrtcInfo(crtc);

for (k = 0;k < numUsed;k++) {
if (usedCrtcs[k] == output->crtcs[j])
break;
}
if (k != numUsed)
continue;

availableOutputs++;

usedCrtcs[numUsed] = output->crtcs[j];
numUsed++;

break;
}
}
XRRFreeOutputInfo(output);
}

free(usedCrtcs);

return availableOutputs;
}

char *vncRandRGetOutputName(int outputIdx)
{
vncGlueContext *ctx = &randrGlueContext;
XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
if (!output) {
return strdup("");
}
char *ret = strdup(output->name);
XRRFreeOutputInfo(output);
return ret;
}

int vncRandRIsOutputEnabled(int outputIdx)
{
vncGlueContext *ctx = &randrGlueContext;
XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
if (!output) {
return 0;
}

if (output->crtc == None) {
XRRFreeOutputInfo(output);
return 0;
}
XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
XRRFreeOutputInfo(output);
if (!crtc) {
return 0;
}
if (crtc->mode == None) {
XRRFreeCrtcInfo(crtc);
return 0;
}
XRRFreeCrtcInfo(crtc);
return 1;
}

int vncRandRIsOutputUsable(int outputIdx)
{
vncGlueContext *ctx = &randrGlueContext;

XRROutputInfo *output;
int i;

output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
if (!output) {
return 0;
}

if (output->crtc != None) {
XRRFreeOutputInfo(output);
return 1;
}

/* Any unused CRTCs? */
for (i = 0;i < output->ncrtc;i++) {
XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtcs[i]);
if (crtc->noutput == 0) {
XRRFreeOutputInfo(output);
XRRFreeCrtcInfo(crtc);
return 1;
}
XRRFreeCrtcInfo(crtc);
}

XRRFreeOutputInfo(output);
return 0;
}

int vncRandRIsOutputConnected(int outputIdx)
{
vncGlueContext *ctx = &randrGlueContext;
XRROutputInfo *output;

output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
if (!output) {
return 0;
}

int ret = (output->connection == RR_Connected);
XRRFreeOutputInfo(output);
return ret;
}

int vncRandRCheckOutputMode(int outputIdx, int width, int height)
{
vncGlueContext *ctx = &randrGlueContext;
XRROutputInfo *output;
RRMode mode;

output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
if (!output)
return 0;

/* Make sure we have the mode we want */
mode = vncRandRGetMatchingMode(output, width, height);
XRRFreeOutputInfo(output);

if (mode == None)
return 0;

return 1;
}

int vncRandRDisableOutput(int outputIdx)
{
vncGlueContext *ctx = &randrGlueContext;
RRCrtc crtcid;
int i;
int move = 0;

XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
if (!output) {
return 0;
}

crtcid = output->crtc;
if (crtcid == 0) {
XRRFreeOutputInfo(output);
return 1;
}

XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
XRRFreeOutputInfo(output);
if (!crtc) {
return 0;
}

/* Remove this output from the CRTC configuration */
for (i = 0; i < crtc->noutput; i++) {
if (ctx->res->outputs[outputIdx] == crtc->outputs[i]) {
crtc->noutput -= 1;
move = 1;
}
if (move && i < crtc->noutput) {
crtc->outputs[i] = crtc->outputs[i+1];
}
}
if (crtc->noutput == 0) {
crtc->mode = None;
crtc->outputs = NULL;
}

int ret = XRRSetCrtcConfig(ctx->dpy,
ctx->res,
crtcid,
CurrentTime,
crtc->x, crtc->y,
crtc->mode, crtc->rotation,
crtc->outputs, crtc->noutput);

XRRFreeCrtcInfo(crtc);

return (ret == RRSetConfigSuccess);
}

unsigned int vncRandRGetOutputId(int outputIdx)
{
vncGlueContext *ctx = &randrGlueContext;
return ctx->res->outputs[outputIdx];
}

int vncRandRGetOutputDimensions(int outputIdx,
int *x, int *y, int *width, int *height)
{
vncGlueContext *ctx = &randrGlueContext;
int swap;
*x = *y = *width = *height = 0;

XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
if (!output) {
return 1;
}

if (!output->crtc) {
XRRFreeOutputInfo(output);
return 1;
}

XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
XRRFreeOutputInfo(output);
if (!crtc) {
return 1;
}
if (crtc->mode == None) {
XRRFreeCrtcInfo(crtc);
return 1;
}

*x = crtc->x;
*y = crtc->y;
for (int m = 0; m < ctx->res->nmode; m++) {
if (crtc->mode == ctx->res->modes[m].id) {
*width = ctx->res->modes[m].width;
*height = ctx->res->modes[m].height;
}
}

switch (crtc->rotation) {
case RR_Rotate_90:
case RR_Rotate_270:
swap = *width;
*width = *height;
*height = swap;
break;
}

XRRFreeCrtcInfo(crtc);
return 0;
}

int vncRandRReconfigureOutput(int outputIdx, int x, int y,
int width, int height)
{
vncGlueContext *ctx = &randrGlueContext;

XRROutputInfo *output;
RRCrtc crtcid;
RRMode mode;
XRRCrtcInfo *crtc = NULL;

int i, ret;

output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
if (!output) {
return 0;
}

crtcid = output->crtc;

/* Need a CRTC? */
if (crtcid == None) {
for (i = 0;i < output->ncrtc;i++) {
crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtcs[i]);
if (!crtc) {
continue;
}

if (crtc->noutput != 0) {
XRRFreeCrtcInfo(crtc);
continue;
}

crtcid = output->crtcs[i];
break;
}
} else {
crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, crtcid);
}

/* Couldn't find one... */
if (crtc == NULL) {
XRRFreeOutputInfo(output);
return 0;
}

/* Make sure we have the mode we want */
mode = vncRandRGetMatchingMode(output, width, height);
if (mode == None) {
XRRFreeCrtcInfo(crtc);
XRRFreeOutputInfo(output);
return 0;
}

/* Reconfigure new mode and position */
ret = XRRSetCrtcConfig (ctx->dpy, ctx->res, crtcid, CurrentTime, x, y,
mode, crtc->rotation, ctx->res->outputs+outputIdx, 1);

XRRFreeCrtcInfo(crtc);
XRRFreeOutputInfo(output);

return (ret == RRSetConfigSuccess);
}

int vncRandRCanCreateOutputs(int extraOutputs)
{
return 0;
}

int vncRandRCreateOutputs(int extraOutputs)
{
return 0;
}

#endif /* HAVE_XRANDR */

+ 287
- 2
unix/x0vncserver/XDesktop.cxx View File

@@ -30,7 +30,13 @@
#ifdef HAVE_XFIXES
#include <X11/extensions/Xfixes.h>
#endif

#ifdef HAVE_XRANDR
#include <X11/extensions/Xrandr.h>
#include <RandrGlue.h>
extern "C" {
void vncSetGlueContext(Display *dpy, void *res);
}
#endif
#include <x0vncserver/Geometry.h>
#include <x0vncserver/XPixelBuffer.h>

@@ -162,6 +168,24 @@ XDesktop::XDesktop(Display* dpy_, Geometry *geometry_)
}
#endif

#ifdef HAVE_XRANDR
int xrandrErrorBase;

randrSyncSerial = 0;
if (XRRQueryExtension(dpy, &xrandrEventBase, &xrandrErrorBase)) {
XRRSelectInput(dpy, DefaultRootWindow(dpy),
RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask);
/* Override TXWindow::init input mask */
XSelectInput(dpy, DefaultRootWindow(dpy),
PropertyChangeMask | StructureNotifyMask | ExposureMask);
} else {
#endif
vlog.info("RANDR extension not present");
vlog.info("Will not be able to handle session resize");
#ifdef HAVE_XRANDR
}
#endif

TXWindow::setGlobalEventHandler(this);
}

@@ -202,7 +226,7 @@ void XDesktop::start(VNCServer* vs) {
vlog.info("Allocated %s", pb->getImage()->classDesc());

server = (VNCServerST *)vs;
server->setPixelBuffer(pb);
server->setPixelBuffer(pb, computeScreenLayout());

#ifdef HAVE_XDAMAGE
if (haveDamage) {
@@ -331,6 +355,217 @@ void XDesktop::keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) {
void XDesktop::clientCutText(const char* str, int len) {
}

ScreenSet XDesktop::computeScreenLayout()
{
ScreenSet layout;

#ifdef HAVE_XRANDR
XRRScreenResources *res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
if (!res) {
vlog.error("XRRGetScreenResources failed");
return layout;
}
vncSetGlueContext(dpy, res);

layout = ::computeScreenLayout(&outputIdMap);
XRRFreeScreenResources(res);
#endif

return layout;
}

#ifdef HAVE_XRANDR
/* Get the biggest mode which is equal or smaller to requested
size. If no such mode exists, return the smallest. */
static void GetSmallerMode(XRRScreenResources *res,
XRROutputInfo *output,
unsigned int *width, unsigned int *height)
{
XRRModeInfo best = {};
XRRModeInfo smallest = {};
smallest.width = -1;
smallest.height = -1;

for (int i = 0; i < res->nmode; i++) {
for (int j = 0; j < output->nmode; j++) {
if (output->modes[j] == res->modes[i].id) {
if ((res->modes[i].width > best.width && res->modes[i].width <= *width) &&
(res->modes[i].height > best.height && res->modes[i].height <= *height)) {
best = res->modes[i];
}
if ((res->modes[i].width < smallest.width) && res->modes[i].height < smallest.height) {
smallest = res->modes[i];
}
}
}
}

if (best.id == 0 && smallest.id != 0) {
best = smallest;
}

*width = best.width;
*height = best.height;
}
#endif /* HAVE_XRANDR */

unsigned int XDesktop::setScreenLayout(int fb_width, int fb_height,
const rfb::ScreenSet& layout)
{
#ifdef HAVE_XRANDR
char buffer[2048];
vlog.debug("Got request for framebuffer resize to %dx%d",
fb_width, fb_height);
layout.print(buffer, sizeof(buffer));
vlog.debug("%s", buffer);

XRRScreenResources *res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
if (!res) {
vlog.error("XRRGetScreenResources failed");
return rfb::resultProhibited;
}
vncSetGlueContext(dpy, res);

/* The client may request a screen layout which is not supported by
the Xserver. This happens, for example, when adjusting the size
of a non-fullscreen vncviewer window. To handle this and other
cases, we first call tryScreenLayout. If this fails, we try to
adjust the request to one screen with a smaller mode. */
vlog.debug("Testing screen layout");
unsigned int tryresult = ::tryScreenLayout(fb_width, fb_height, layout, &outputIdMap);
rfb::ScreenSet adjustedLayout;
if (tryresult == rfb::resultSuccess) {
adjustedLayout = layout;
} else {
vlog.debug("Impossible layout - trying to adjust");

ScreenSet::const_iterator firstscreen = layout.begin();
adjustedLayout.add_screen(*firstscreen);
ScreenSet::iterator iter = adjustedLayout.begin();
RROutput outputId = None;

for (int i = 0;i < vncRandRGetOutputCount();i++) {
unsigned int oi = vncRandRGetOutputId(i);

/* Known? */
if (outputIdMap.count(oi) == 0)
continue;

/* Find the corresponding screen... */
if (iter->id == outputIdMap[oi]) {
outputId = oi;
} else {
outputIdMap.erase(oi);
}
}

/* New screen */
if (outputId == None) {
int i = getPreferredScreenOutput(&outputIdMap, std::set<unsigned int>());
if (i != -1) {
outputId = vncRandRGetOutputId(i);
}
}
if (outputId == None) {
vlog.debug("Resize adjust: Could not find corresponding screen");
XRRFreeScreenResources(res);
return rfb::resultInvalid;
}
XRROutputInfo *output = XRRGetOutputInfo(dpy, res, outputId);
if (!output) {
vlog.debug("Resize adjust: XRRGetOutputInfo failed");
XRRFreeScreenResources(res);
return rfb::resultInvalid;
}
if (!output->crtc) {
vlog.debug("Resize adjust: Selected output has no CRTC");
XRRFreeScreenResources(res);
XRRFreeOutputInfo(output);
return rfb::resultInvalid;
}
XRRCrtcInfo *crtc = XRRGetCrtcInfo(dpy, res, output->crtc);
if (!crtc) {
vlog.debug("Resize adjust: XRRGetCrtcInfo failed");
XRRFreeScreenResources(res);
XRRFreeOutputInfo(output);
return rfb::resultInvalid;
}

unsigned int swidth = iter->dimensions.width();
unsigned int sheight = iter->dimensions.height();

switch (crtc->rotation) {
case RR_Rotate_90:
case RR_Rotate_270:
unsigned int swap = swidth;
swidth = sheight;
sheight = swap;
break;
}

GetSmallerMode(res, output, &swidth, &sheight);
XRRFreeOutputInfo(output);

switch (crtc->rotation) {
case RR_Rotate_90:
case RR_Rotate_270:
unsigned int swap = swidth;
swidth = sheight;
sheight = swap;
break;
}

XRRFreeCrtcInfo(crtc);

if (sheight != 0 && swidth != 0) {
vlog.debug("Adjusted resize request to %dx%d", swidth, sheight);
iter->dimensions.setXYWH(0, 0, swidth, sheight);
fb_width = swidth;
fb_height = sheight;
} else {
vlog.error("Failed to find smaller or equal screen size");
XRRFreeScreenResources(res);
return rfb::resultInvalid;
}
}

vlog.debug("Changing screen layout");
unsigned int ret = ::setScreenLayout(fb_width, fb_height, adjustedLayout, &outputIdMap);
XRRFreeScreenResources(res);

/* Send a dummy event to the root window. When this event is seen,
earlier change events (ConfigureNotify and/or CrtcChange) have
been processed. An Expose event is used for simplicity; does not
require any Atoms, and will not affect other applications. */
unsigned long serial = XNextRequest(dpy);
XExposeEvent ev = {}; /* zero x, y, width, height, count */
ev.type = Expose;
ev.display = dpy;
ev.window = DefaultRootWindow(dpy);
if (XSendEvent(dpy, DefaultRootWindow(dpy), False, ExposureMask, (XEvent*)&ev)) {
while (randrSyncSerial < serial) {
TXWindow::handleXEvents(dpy);
}
} else {
vlog.error("XSendEvent failed");
}

/* The protocol requires that an error is returned if the requested
layout could not be set. This is checked by
VNCSConnectionST::setDesktopSize. Another ExtendedDesktopSize
with reason=0 will be sent in response to the changes seen by the
event handler. */
if (adjustedLayout != layout) {
return rfb::resultInvalid;
} else {
return ret;
}

#else
return rfb::resultProhibited;
#endif /* HAVE_XRANDR */
}


bool XDesktop::handleGlobalEvent(XEvent* ev) {
if (ev->type == xkbEventBase + XkbEventCode) {
@@ -378,6 +613,56 @@ bool XDesktop::handleGlobalEvent(XEvent* ev) {
return false;

return setCursor();
#endif
#ifdef HAVE_XRANDR
} else if (ev->type == Expose) {
XExposeEvent* eev = (XExposeEvent*)ev;
randrSyncSerial = eev->serial;

return false;

} else if (ev->type == ConfigureNotify) {
XConfigureEvent* cev = (XConfigureEvent*)ev;

if (cev->window != DefaultRootWindow(dpy)) {
return false;
}

XRRUpdateConfiguration(ev);
geometry->recalc(cev->width, cev->height);

if (!running) {
return false;
}

if ((cev->width != pb->width() || (cev->height != pb->height()))) {
// Recreate pixel buffer
ImageFactory factory((bool)useShm);
delete pb;
pb = new XPixelBuffer(dpy, factory, geometry->getRect());
server->setPixelBuffer(pb, computeScreenLayout());

// Mark entire screen as changed
server->add_changed(rfb::Region(Rect(0, 0, cev->width, cev->height)));
}

return true;

} else if (ev->type == xrandrEventBase + RRNotify) {
XRRNotifyEvent* rev = (XRRNotifyEvent*)ev;

if (rev->window != DefaultRootWindow(dpy)) {
return false;
}

if (!running)
return false;

if (rev->subtype == RRNotify_CrtcChange) {
server->setScreenLayout(computeScreenLayout());
}

return true;
#endif
}


+ 10
- 0
unix/x0vncserver/XDesktop.h View File

@@ -23,6 +23,7 @@

#include <rfb/VNCServerST.h>
#include <tx/TXWindow.h>
#include <unixcommon.h>

#include <X11/XKBlib.h>
#ifdef HAVE_XDAMAGE
@@ -49,6 +50,9 @@ public:
KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym);
virtual void keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down);
virtual void clientCutText(const char* str, int len);
virtual unsigned int setScreenLayout(int fb_width, int fb_height,
const rfb::ScreenSet& layout);

// -=- TXGlobalEventHandler interface
virtual bool handleGlobalEvent(XEvent* ev);

@@ -70,12 +74,18 @@ protected:
int xkbEventBase;
#ifdef HAVE_XFIXES
int xfixesEventBase;
#endif
#ifdef HAVE_XRANDR
int xrandrEventBase;
OutputIdMap outputIdMap;
unsigned long randrSyncSerial;
#endif
int ledMasks[XDESKTOP_N_LEDS];
unsigned ledState;
const unsigned short *codeMap;
unsigned codeMapLen;
bool setCursor();
rfb::ScreenSet computeScreenLayout();
};

#endif // __XDESKTOP_H__

Loading…
Cancel
Save