Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental support for setdesktopsize / extendeddesktopsize #107

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,12 @@ elif test "$X_CFLAGS" != "-DX_DISPLAY_MISSING"; then
case `(uname -sr) 2>/dev/null` in
"SunOS 5"*) CPPFLAGS="$CPPFLAGS -I/usr/X11/include" ;;
esac

# support for setdesktopsize needs xrandr and libvncserver with setDesktopSizeHook
AC_CHECK_MEMBER([struct _rfbScreenInfo.setDesktopSizeHook],
[AC_DEFINE(HAVE_SETDESKTOPSIZE, 1, [libvncserver supports setDesktopSizeHook])],
[AC_MSG_WARN([Support for option -setdesktopsize disabled. Needs libvncserver 0.9.13])],
[[#include "rfb/rfb.h"]])
fi

X_LIBS="$X_LIBS $X_PRELIBS -lX11 $X_EXTRA_LIBS"
Expand Down
6 changes: 5 additions & 1 deletion src/connections.c
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,11 @@ void client_gone(rfbClientPtr client) {
}
}


#if HAVE_SETDESKTOPSIZE
if (enable_setdesktopsize && xrandr && client_count == 0) {
xrandr_reset_scaling();
}
#endif
if (no_autorepeat && client_count == 0) {
autorepeat(1, 0);
}
Expand Down
8 changes: 8 additions & 0 deletions src/help.c
Original file line number Diff line number Diff line change
Expand Up @@ -3058,6 +3058,14 @@ void print_help(int mode) {
" prefix \"string\" with \"nc:\", e.g. \"nc:+90\",\n"
" \"nc:xy\", etc.\n"
"\n"
#if HAVE_SETDESKTOPSIZE
"-setdesktopsize Allow client to change framebuffer resolution to fit\n"
" size of client window. x11vnc will use xrandr commands\n"
" to change the X framebuffer size. The mode of the physical\n"
" screen will not be changed, but scaling will be used to\n"
" display the new framebuffer size on the physical screen.\n"
"\n"
#endif
"-padgeom WxH Whenever a new vncviewer connects, the framebuffer is\n"
" replaced with a fake, solid black one of geometry WxH.\n"
" Shortly afterwards the framebuffer is replaced with the\n"
Expand Down
1 change: 1 addition & 0 deletions src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ char *xrandr_mode = NULL;
char *pad_geometry = NULL;
time_t pad_geometry_time = 0;
int use_snapfb = 0;
int enable_setdesktopsize = 0;

int use_xrecord = 0;
int noxrecord = 0;
Expand Down
1 change: 1 addition & 0 deletions src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ extern char *xrandr_mode;
extern char *pad_geometry;
extern time_t pad_geometry_time;
extern int use_snapfb;
extern int enable_setdesktopsize;

extern int use_xrecord;
extern int noxrecord;
Expand Down
27 changes: 27 additions & 0 deletions src/screen.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ rfbBool vnc_reflect_send_cuttext(char *str, int len);
static void debug_colormap(XImage *fb);
static void set_visual(char *str);
static void nofb_hook(rfbClientPtr cl);
#if HAVE_SETDESKTOPSIZE
static int set_desktop_size_hook(int width, int height, int numScreens, rfbExtDesktopScreen* extDesktopScreens, rfbClientPtr cl);
#endif
static void remove_fake_fb(void);
static void install_fake_fb(int w, int h, int bpp);
static void initialize_snap_fb(void);
Expand Down Expand Up @@ -819,6 +822,25 @@ void free_old_fb(void) {
}
}

#if HAVE_SETDESKTOPSIZE
static int set_desktop_size_hook(int width, int height, int numScreens, rfbExtDesktopScreen* extDesktopScreens, rfbClientPtr cl)
{
int i;

rfbLog("Received SetDesktopSize message from client requesting (%dx%d) framebuffer with screen configuration:\n", width, height);
for (i=0; i<numScreens; i++) {
rfbLog("- id: %d resolution: %dx%d x offset: %d y offset: %d flags: %d\n", extDesktopScreens[i].id, extDesktopScreens[i].width,
extDesktopScreens[i].height, extDesktopScreens[i].x, extDesktopScreens[i].y, extDesktopScreens[i].flags);
}
if (cl->viewOnly) {
rfbLog("Denying setDesktopSize request as client is view-only\n");
return rfbExtDesktopSize_ResizeProhibited;
}

return xrandr_set_scale_from(width, height) ? rfbExtDesktopSize_Success : rfbExtDesktopSize_InvalidScreenLayout;
}
#endif

static char _lcs_tmp[128];
static int _bytes0_size = 128, _bytes0[128];

Expand Down Expand Up @@ -3651,6 +3673,11 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
screen->ptrAddEvent = pointer_event;
screen->setXCutText = xcut_receive;
screen->setTranslateFunction = set_xlate_wrapper;
#if HAVE_SETDESKTOPSIZE
if (enable_setdesktopsize) {
screen->setDesktopSizeHook = set_desktop_size_hook;
}
#endif

screen->kbdReleaseAllKeys = kbd_release_all_keys;
screen->setSingleWindow = set_single_window;
Expand Down
6 changes: 6 additions & 0 deletions src/x11vnc.c
Original file line number Diff line number Diff line change
Expand Up @@ -3888,6 +3888,12 @@ int main(int argc, char* argv[]) {
grab_buster = 0;
continue;
}
#if HAVE_SETDESKTOPSIZE
if (!strcmp(arg, "-setdesktopsize")) {
enable_setdesktopsize = 1;
continue;
}
#endif
if (!strcmp(arg, "-snapfb")) {
use_snapfb = 1;
continue;
Expand Down
150 changes: 150 additions & 0 deletions src/xrandr.c
Original file line number Diff line number Diff line change
Expand Up @@ -304,4 +304,154 @@ int known_xrandr_mode(char *s) {
}
}

#if HAVE_SETDESKTOPSIZE
/* Set framebuffer size to w x h
* Does not alter physical resolution but scales desired framebuffer to physical display resolution */
rfbBool xrandr_set_scale_from(int w, int h)
{
XTransform transform;
XRRScreenResources *screens;
XRRCrtcInfo *crtcInfo;
XRROutputInfo *outputInfo;
RRCrtc xrandr_crtc;
RROutput xrandr_output;
XRRModeInfo *modeInfo = NULL;
int i;

double sx, sy;
int major, minor, minWidth, minHeight, maxWidth, maxHeight;
char *filter;

if (!xrandr_present)
return FALSE;

X_LOCK;
XRRQueryVersion(dpy, &major, &minor);
if (major < 1 || (major == 1 && minor < 3)) {
rfbLog("Need at least RANDR 1.3 to support scaling, only %d.%d available\n", major, minor);
X_UNLOCK;
return FALSE;
}

if (w != -1 && XRRGetScreenSizeRange(dpy, rootwin, &minWidth, &minHeight, &maxWidth, &maxHeight)) {
if (w > maxWidth || h > maxHeight) {
w = nmin(w, maxWidth);
h = nmin(h, maxHeight);
rfbLog("Requested size exceeds maximum size of (%dx%d), reduced to (%dx%d)\n", maxWidth, maxHeight, w, h);
}
if (w < minWidth || h < minHeight) {
w = nmax(w, minWidth);
h = nmax(h, minHeight);
rfbLog("Requested size is smaller than minimum size of (%dx%d), enlarged to (%dx%d)\n", minWidth, minHeight, w, h);
}
}

screens = XRRGetScreenResourcesCurrent(dpy, rootwin);
if (!screens->ncrtc || !screens->noutput) {
rfbLog("RANDR Error: XRRGetScreenResourcesCurrent() did not return any crtcs and/or outputs\n");
XRRFreeScreenResources(screens);
X_UNLOCK;
return FALSE;
}

/* We only support using using one screen for now */
xrandr_output = XRRGetOutputPrimary(dpy, rootwin);
if (!xrandr_output)
xrandr_output = screens->outputs[0];

xrandr = 1;
outputInfo = XRRGetOutputInfo(dpy, screens, xrandr_output);
xrandr_crtc = outputInfo->crtc;

if (!xrandr_crtc) {
if (w != -1) {
rfbLog("RANDR: running headless without screen. Setting fb size without scaling.\n");
XRRSetScreenSize(dpy, rootwin, w, h, w / 2.8, h / 2.8);
}
XRRFreeOutputInfo(outputInfo);
XRRFreeScreenResources(screens);
X_UNLOCK;
return TRUE;
}
crtcInfo = XRRGetCrtcInfo(dpy, screens, xrandr_crtc);

for (i=0; i<screens->nmode; i++)
{
if (screens->modes[i].id == crtcInfo->mode)
{
modeInfo = &screens->modes[i];
break;
}
}

if (!modeInfo) {
rfbLog("RANDR Error: cannot find current mode\n");
XRRFreeCrtcInfo(crtcInfo);
XRRFreeOutputInfo(outputInfo);
XRRFreeScreenResources(screens);
X_UNLOCK;
return FALSE;
}

if (w == -1) {
w = modeInfo->width;
h = modeInfo->height;
}
if (!outputInfo->mm_width) {
/* Just assume 72 dpi, which is about 2.8 dpmm */
outputInfo->mm_width = modeInfo->width / 2.8;
outputInfo->mm_height = modeInfo->height / 2.8;
}

sx = (double) w / modeInfo->width;
sy = (double) h / modeInfo->height;

memset(&transform, 0, sizeof(transform));
transform.matrix[0][0] = XDoubleToFixed(sx);
transform.matrix[1][1] = XDoubleToFixed(sy);
transform.matrix[2][2] = XDoubleToFixed(1.0);

if (sx != 1 || sy != 1)
filter = "bilinear";
else
filter = "nearest";

XGrabServer(dpy);

/* Disable all crtc */
for (i=0; i<screens->ncrtc; i++)
{
XRRSetCrtcConfig(dpy, screens, screens->crtcs[i], CurrentTime,
0, 0, None, RR_Rotate_0, NULL, 0);
}

/* Set framebuffer size */
XRRSetScreenSize(dpy, rootwin, w, h, outputInfo->mm_width, outputInfo->mm_height);
/* Set transform */
XRRSetCrtcTransform(dpy, xrandr_crtc, &transform, filter, NULL, 0);
/* Enable first crtc again */
XRRSetCrtcConfig(dpy, screens, xrandr_crtc, CurrentTime,
0, 0, crtcInfo->mode, crtcInfo->rotation, crtcInfo->outputs, crtcInfo->noutput);

XUngrabServer(dpy);
XRRFreeOutputInfo(outputInfo);
XRRFreeCrtcInfo(crtcInfo);
XRRFreeScreenResources(screens);
X_UNLOCK;

/* Leave creating the new frame buffer up to the xrandr event monitoring
code. (If we already create it here, we risk it is going
to be resized back to the old size, because multiple randr events
are enroute due to our changes, and the first may still mention
the old size.) */

return TRUE;
}

/* Restore scaling to original size */
void xrandr_reset_scaling()
{
xrandr_set_scale_from(-1, -1);
}
#endif

2 changes: 2 additions & 0 deletions src/xrandr.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ extern Time xrandr_cfg_time;
extern void initialize_xrandr(void);
extern int check_xrandr_event(char *msg);
extern int known_xrandr_mode(char *s);
extern rfbBool xrandr_set_scale_from(int w, int h);
extern void xrandr_reset_scaling();

#define XRANDR_SET_TRAP_RET(x,y) \
if (subwin || xrandr) { \
Expand Down
8 changes: 8 additions & 0 deletions x11vnc.1
Original file line number Diff line number Diff line change
Expand Up @@ -3365,6 +3365,14 @@ If you do not want the cursor shape to be rotated
prefix \fIstring\fR with "nc:", e.g. "nc:+90",
"nc:xy", etc.
.PP
\fB-setdesktopsize\fR
.IP
Allow client to change framebuffer resolution to fit
size of client window. x11vnc will use xrandr commands
to change the X framebuffer size. The mode of the physical
screen will not be changed, but scaling will be used to
display the new framebuffer size on the physical screen.
.PP
\fB-padgeom\fR \fIWxH\fR
.IP
Whenever a new vncviewer connects, the framebuffer is
Expand Down