Commit c08bfcfd authored by runge's avatar runge

x11vnc options -nosel -noprimary -visual.

 add clipboard/selection handling.
 add visual option (mostly for testing and workarounds).
 improve shm cleanup on failures.
parent 50a80e73
2004-02-19 Karl Runge <runge@karlrunge.com>
* x11vnc options -nosel -noprimary -visual.
* add clipboard/selection handling.
* add visual option (mostly for testing and workarounds).
* improve shm cleanup on failures.
2004-02-04 Johannes E. Schindelin <Johannes.Schindelin@gmx.de> 2004-02-04 Johannes E. Schindelin <Johannes.Schindelin@gmx.de>
* Make examples (at least a few) compileable with g++, * Make examples (at least a few) compileable with g++,
as pointed out by Juan Jose Costello as pointed out by Juan Jose Costello
...@@ -14,7 +20,7 @@ ...@@ -14,7 +20,7 @@
* ignore SIGPIPE; it is handled by EPIPE * ignore SIGPIPE; it is handled by EPIPE
* add an example how to use rfbDoCopyRect * add an example how to use rfbDoCopyRect
* add experimental progressive updating (off by default) * add experimental progressive updating (off by default)
2004-01-19 Karl Runge <runge@karlrunge.com> 2004-01-19 Karl Runge <runge@karlrunge.com>
* handle mouse button number mismatch * handle mouse button number mismatch
* improved pointer input handling during drags, etc. * improved pointer input handling during drags, etc.
...@@ -44,7 +50,7 @@ ...@@ -44,7 +50,7 @@
bootstrap.sh, config.h.in, configure, bootstrap.sh, config.h.in, configure,
depcomp, install-sh, missing, mkinstalldirs, depcomp, install-sh, missing, mkinstalldirs,
Removed auto-generated files from CVS. Removed auto-generated files from CVS.
2003-09-11 Mark McLoughlin <mark@skynet.ie> 2003-09-11 Mark McLoughlin <mark@skynet.ie>
* rdr/Exception.h, rdr/FdInStream.cxx, rdr/FdInStream.h, * rdr/Exception.h, rdr/FdInStream.cxx, rdr/FdInStream.h,
......
2004-02-19 Karl Runge <runge@karlrunge.com>
* added handling of clipboard/selection exchange to/from clients,
even holds PRIMARY which Xvnc does not do. disable with -nosel.
use -noprimary to disable polling of PRIMARY selection.
* added -visual option to force framebuffer visual. not really
of general use, more for testing and workarounds (e.g. win2vnc
fails under 8bpp index color)
* improve cleanup and error handling WRT shm and other failures.
2004-01-19 Karl Runge <runge@karlrunge.com> 2004-01-19 Karl Runge <runge@karlrunge.com>
* improvements to pointer event handling primarily during window * improvements to pointer event handling primarily during window
dragging. check_user_input() for non-threaded and pointer() dragging. check_user_input() for non-threaded and pointer()
......
...@@ -102,6 +102,7 @@ ...@@ -102,6 +102,7 @@
#include <X11/extensions/XShm.h> #include <X11/extensions/XShm.h>
#include <X11/extensions/XTest.h> #include <X11/extensions/XTest.h>
#include <X11/keysym.h> #include <X11/keysym.h>
#include <X11/Xatom.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
...@@ -112,6 +113,7 @@ ...@@ -112,6 +113,7 @@
#include <X11/XKBlib.h> #include <X11/XKBlib.h>
#endif #endif
/* X and rfb framebuffer */ /* X and rfb framebuffer */
Display *dpy = 0; Display *dpy = 0;
Visual *visual; Visual *visual;
...@@ -187,7 +189,7 @@ int show_dragging = 1; /* process mouse movement events */ ...@@ -187,7 +189,7 @@ int show_dragging = 1; /* process mouse movement events */
int watch_bell = 1; /* watch for the bell using XKEYBOARD */ int watch_bell = 1; /* watch for the bell using XKEYBOARD */
int old_pointer = 0; /* use the old way of updating the pointer */ int old_pointer = 0; /* use the old way of updating the pointer */
int old_copytile = 0; /* use the old way copy_tile() */ int single_copytile = 0; /* use the old way copy_tile() */
int using_shm = 1; /* whether mit-shm is used */ int using_shm = 1; /* whether mit-shm is used */
int flip_byte_order = 0; /* sometimes needed when using_shm = 0 */ int flip_byte_order = 0; /* sometimes needed when using_shm = 0 */
...@@ -206,6 +208,10 @@ int napfac = 4; /* time = napfac*waitms, cut load with extra waits */ ...@@ -206,6 +208,10 @@ int napfac = 4; /* time = napfac*waitms, cut load with extra waits */
int napmax = 1500; /* longest nap in ms. */ int napmax = 1500; /* longest nap in ms. */
int ui_skip = 10; /* see watchloop. negative means ignore input */ int ui_skip = 10; /* see watchloop. negative means ignore input */
/* for -visual override */
VisualID visual_id = (VisualID) 0;
int visual_depth = 0;
int nap_ok = 0, nap_diff_count = 0; int nap_ok = 0, nap_diff_count = 0;
time_t last_event, last_input; time_t last_event, last_input;
...@@ -286,7 +292,7 @@ void clean_up_exit (int ret) { ...@@ -286,7 +292,7 @@ void clean_up_exit (int ret) {
for(i=1; i<=ntiles_x; i++) { for(i=1; i<=ntiles_x; i++) {
shm_clean(&tile_row_shm[i], tile_row[i]); shm_clean(&tile_row_shm[i], tile_row[i]);
if (old_copytile && i == 1) { if (single_copytile && i >= single_copytile) {
break; break;
} }
} }
...@@ -332,8 +338,8 @@ void interrupted (int sig) { ...@@ -332,8 +338,8 @@ void interrupted (int sig) {
* as one might like... sometimes need to run ipcrm(1). * as one might like... sometimes need to run ipcrm(1).
*/ */
for(i=1; i<=ntiles_x; i++) { for(i=1; i<=ntiles_x; i++) {
shm_clean(&tile_row_shm[i], tile_row[i]); shm_delete(&tile_row_shm[i]);
if (old_copytile && i == 1) { if (single_copytile && i >= single_copytile) {
break; break;
} }
} }
...@@ -511,10 +517,12 @@ void DebugXTestFakeKeyEvent(Display* dpy, KeyCode keysym, Bool down, time_t cur_ ...@@ -511,10 +517,12 @@ void DebugXTestFakeKeyEvent(Display* dpy, KeyCode keysym, Bool down, time_t cur_
} }
/* /*
* Uncomment the next line to aid in debugging keymapping problems. * Uncomment the two lines to aid in debugging keymapping problems.
*/ */
/*
/* #define XTestFakeKeyEvent DebugXTestFakeKeyEvent */ #define XTestFakeKeyEvent DebugXTestFakeKeyEvent
#define DebugKeyEvent
*/
void tweak_mod(signed char mod, rfbBool down) { void tweak_mod(signed char mod, rfbBool down) {
rfbBool is_shift = mod_state & (LEFTSHIFT|RIGHTSHIFT); rfbBool is_shift = mod_state & (LEFTSHIFT|RIGHTSHIFT);
...@@ -593,7 +601,7 @@ static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr ...@@ -593,7 +601,7 @@ static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr
static void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { static void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
KeyCode k; KeyCode k;
#ifdef DebugXTestFakeKeyEvent #ifdef DebugKeyEvent
X_LOCK; X_LOCK;
rfbLog("keyboard(%s,%s(0x%x),client)\n", rfbLog("keyboard(%s,%s(0x%x),client)\n",
down?"down":"up",XKeysymToString(keysym),(int)keysym); down?"down":"up",XKeysymToString(keysym),(int)keysym);
...@@ -899,6 +907,386 @@ void watch_bell_event() { ...@@ -899,6 +907,386 @@ void watch_bell_event() {
void watch_bell_event() {} void watch_bell_event() {}
#endif #endif
/*
* Selection/Cutbuffer/Clipboard handlers.
*/
int watch_selection = 1; /* normal selection/cutbuffer maintenance */
int watch_primary = 1; /* more dicey, poll for changes in PRIMARY */
int own_selection = 0; /* whether we currently own PRIMARY or not */
int set_cutbuffer = 0; /* to avoid bouncing the CutText right back */
int sel_waittime = 5; /* some seconds to skip before first send */
Window selwin; /* special window for our selection */
/*
* This is where we keep our selection: the string sent TO us from VNC
* clients, and the string sent BY us to requesting X11 clients.
*/
char *xcut_string = NULL;
/*
* Our callbacks instruct us to check for changes in the cutbuffer
* and PRIMARY selection on the local X11 display.
*
* We store the new cutbuffer and/or PRIMARY selection data in this
* constant sized array selection_str[].
* TODO: check if malloc does not cause performance issues (esp. WRT
* SelectionNotify handling).
*/
#define PROP_MAX (131072L)
char selection_str[PROP_MAX+1];
/*
* An X11 (not VNC) client on the local display has requested the selection
* from us (because we are the current owner).
*
* n.b.: our caller already has the X_LOCK.
*/
void selection_request(XEvent *ev) {
XSelectionEvent notify_event;
XSelectionRequestEvent *req_event;
unsigned int length;
unsigned char *data;
#ifndef XA_LENGTH
unsigned long XA_LENGTH = XInternAtom(dpy, "LENGTH", True);
#endif
req_event = &(ev->xselectionrequest);
notify_event.type = SelectionNotify;
notify_event.display = req_event->display;
notify_event.requestor = req_event->requestor;
notify_event.selection = req_event->selection;
notify_event.target = req_event->target;
notify_event.time = req_event->time;
if (req_event->property == None) {
notify_event.property = req_event->target;
} else {
notify_event.property = req_event->property;
}
if (xcut_string) {
length = strlen(xcut_string);
} else {
length = 0;
}
if (ev->xselectionrequest.target == XA_LENGTH) {
/* length request */
XChangeProperty(ev->xselectionrequest.display,
ev->xselectionrequest.requestor,
ev->xselectionrequest.property,
ev->xselectionrequest.target, 32, PropModeReplace,
(unsigned char *) &length, sizeof(unsigned int));
} else {
/* data request */
data = (unsigned char *)xcut_string;
XChangeProperty(ev->xselectionrequest.display,
ev->xselectionrequest.requestor,
ev->xselectionrequest.property,
ev->xselectionrequest.target, 8, PropModeReplace,
data, length);
}
XSendEvent(req_event->display, req_event->requestor, False, 0,
(XEvent *)&notify_event);
XFlush(dpy);
}
/*
* CUT_BUFFER0 property on the local display has changed, we read and
* store it and send it out to any connected VNC clients.
*
* n.b.: our caller already has the X_LOCK.
*/
void cutbuffer_send() {
Atom type;
int format, slen, dlen;
unsigned long nitems = 0, bytes_after = 0;
unsigned char* data = NULL;
selection_str[0] = '\0';
slen = 0;
/* read the property value into selection_str: */
do {
if (XGetWindowProperty(dpy, DefaultRootWindow(dpy),
XA_CUT_BUFFER0, nitems/4, PROP_MAX/16, False,
AnyPropertyType, &type, &format, &nitems, &bytes_after,
&data) == Success) {
dlen = nitems * (format/8);
if (slen + dlen > PROP_MAX) {
/* too big */
rfbLog("warning: truncating large CUT_BUFFER0"
" selection > %d bytes.\n", PROP_MAX);
XFree(data);
break;
}
memcpy(selection_str+slen, data, dlen);
slen += dlen;
selection_str[slen] = '\0';
XFree(data);
}
} while (bytes_after > 0);
selection_str[PROP_MAX] = '\0';
/* now send it to any connected VNC clients (rfbServerCutText) */
rfbSendServerCutText(screen, selection_str, strlen(selection_str));
}
/*
* "callback" for our SelectionNotify polling. We try to determine if
* the PRIMARY selection has changed (checking length and first CHKSZ bytes)
* and if it has we store it and send it off to any connected VNC clients.
*
* n.b.: our caller already has the X_LOCK.
*
* TODO: if we were willing to use libXt, we could perhaps get selection
* timestamps to speed up the checking... XtGetSelectionValue().
*/
#define CHKSZ 32
void selection_send(XEvent *ev) {
Atom type;
int format, slen, dlen, oldlen, newlen, toobig = 0;
static int skip_count = 2, err = 0, sent_one = 0;
char before[CHKSZ], after[CHKSZ];
unsigned long nitems = 0, bytes_after = 0;
unsigned char* data = NULL;
/*
* remember info about our last value of PRIMARY (or CUT_BUFFER0)
* so we can check for any changes below.
*/
oldlen = strlen(selection_str);
strncpy(before, selection_str, CHKSZ);
selection_str[0] = '\0';
slen = 0;
/* read in the current value of PRIMARY: */
do {
if (XGetWindowProperty(dpy, ev->xselection.requestor,
ev->xselection.property, nitems/4, PROP_MAX/16, True,
AnyPropertyType, &type, &format, &nitems, &bytes_after,
&data) == Success) {
dlen = nitems * (format/8);
if (slen + dlen > PROP_MAX) {
/* too big */
toobig = 1;
XFree(data);
if (err) { /* cut down on messages */
break;
} else {
err = 5;
}
rfbLog("warning: truncating large PRIMARY"
" selection > %d bytes.\n", PROP_MAX);
break;
}
memcpy(selection_str+slen, data, dlen);
slen += dlen;
selection_str[slen] = '\0';
XFree(data);
}
} while (bytes_after > 0);
if (! toobig) {
err = 0;
} else if (err) {
err--;
}
if (! sent_one) {
/* try to force a send first time in */
oldlen = -1;
sent_one = 1;
}
/* look for changes in the new value */
newlen = strlen(selection_str);
strncpy(after, selection_str, CHKSZ);
if (oldlen == newlen && strncmp(before, after, CHKSZ) == 0) {
/* evidently no change */
return;
}
if (newlen == 0) {
/* do not bother sending a null string out */
return;
}
/* now send it to any connected VNC clients (rfbServerCutText) */
rfbSendServerCutText(screen, selection_str, newlen);
}
/*
* This routine is periodically called to check for selection related
* X11 events and respond to them as needed.
*/
void watch_selection_event() {
XEvent xev;
static int last_request = 0, first = 1, sent_sel = 0, starttime;
X_LOCK;
if (first) {
/* create fake window for our selection ownership, etc */
selwin = XCreateSimpleWindow(dpy, rootwin, 0, 0, 1, 1, 0, 0, 0);
/*
* register desired event(s) for notification.
* PropertyChangeMask is for CUT_BUFFER0 changes.
* TODO: does this cause a flood of other stuff?
*/
XSelectInput(dpy, rootwin, PropertyChangeMask);
starttime = time(0);
first = 0;
}
/*
* There is a bug where we have to wait before sending text to
* the client... so instead of sending right away we wait a
* the few seconds.
*/
if (! sent_sel && time(0) > starttime + sel_waittime) {
if (XGetSelectionOwner(dpy, XA_PRIMARY) == None) {
cutbuffer_send();
}
sent_sel = 1;
}
/* check for CUT_BUFFER0 change: */
if (XCheckTypedEvent(dpy, PropertyNotify, &xev)) {
if (xev.type == PropertyNotify &&
xev.xproperty.atom == XA_CUT_BUFFER0) {
/*
* Go retrieve CUT_BUFFER0 and send it.
*
* set_cutbuffer is a flag to try to avoid processing
* our own cutbuffer changes.
*/
if (! set_cutbuffer) {
cutbuffer_send();
sent_sel = 1;
}
set_cutbuffer = 0;
}
}
/* check for our PRIMARY request notification: */
if (watch_primary) {
if (XCheckTypedEvent(dpy, SelectionNotify, &xev)) {
if (xev.type == SelectionNotify &&
xev.xselection.requestor == selwin &&
xev.xselection.selection == XA_PRIMARY &&
xev.xselection.property != None &&
xev.xselection.target == XA_STRING) {
/* go retrieve PRIMARY and check it */
if (sent_sel ||
time(0) > starttime + sel_waittime) {
selection_send(&xev);
}
}
}
if (time(0) > last_request + 1) {
/*
* Every second or two, request PRIMARY, unless we
* already own it or there is no owner.
* TODO: even at this low rate we should look into
* and performance problems in odds cases, etc.
*/
last_request = time(0);
if (! own_selection &&
XGetSelectionOwner(dpy, XA_PRIMARY) != None) {
XConvertSelection(dpy, XA_PRIMARY, XA_STRING,
XA_STRING, selwin, CurrentTime);
}
}
}
if (! own_selection) {
/*
* no need to do the PRIMARY maintenance tasks below if
* no we do not own it (right?).
*/
X_UNLOCK;
return;
}
/* we own PRIMARY, see if someone requested it: */
if (XCheckTypedEvent(dpy, SelectionRequest, &xev)) {
if (xev.type == SelectionRequest &&
xev.xselectionrequest.selection == XA_PRIMARY) {
selection_request(&xev);
}
}
/* we own PRIMARY, see if we no longer own it: */
if (XCheckTypedEvent(dpy, SelectionClear, &xev)) {
if (xev.type == SelectionClear &&
xev.xselectionclear.selection == XA_PRIMARY) {
own_selection = 0;
if (xcut_string) {
free(xcut_string);
xcut_string = NULL;
}
}
}
X_UNLOCK;
}
/*
* hook called when a VNC client sends us some "XCut" text (rfbClientCutText).
*/
void xcut_receive(char *text, int len, rfbClientPtr cl) {
static int first = 1;
if (text == NULL || len == 0) {
return;
}
X_LOCK;
/* associate this text with PRIMARY (and SECONDARY...) */
if (! own_selection) {
own_selection = 1;
/* we need to grab the PRIMARY selection */
XSetSelectionOwner(dpy, XA_PRIMARY, selwin, CurrentTime);
XFlush(dpy);
}
/* duplicate the text string for our own use. */
if (xcut_string != NULL) {
free(xcut_string);
}
xcut_string = (unsigned char *)
malloc((size_t) (len+1) * sizeof(unsigned char));
strncpy(xcut_string, text, len);
xcut_string[len] = '\0'; /* make sure null terminated */
/* copy this text to CUT_BUFFER0 as well: */
XChangeProperty(dpy, rootwin, XA_CUT_BUFFER0, XA_STRING, 8,
PropModeReplace, text, len);
XFlush(dpy);
X_UNLOCK;
set_cutbuffer = 1;
}
void mark_hint(hint_t); void mark_hint(hint_t);
/* /*
...@@ -1076,27 +1464,27 @@ void restore_mouse_patch() { ...@@ -1076,27 +1464,27 @@ void restore_mouse_patch() {
* It seems impossible to do, but if the actual cursor could ever be * It seems impossible to do, but if the actual cursor could ever be
* determined we might want to hash that info on window ID or something... * determined we might want to hash that info on window ID or something...
*/ */
int tree_depth_cursor(void) { int tree_descend_cursor(void) {
Window r, c; Window r, c;
int rx, ry, wx, wy; int rx, ry, wx, wy;
unsigned int mask; unsigned int mask;
int depth = 0, tries = 0, maxtries = 1; int descend = 0, tries = 0, maxtries = 1;
X_LOCK; X_LOCK;
c = window; c = window;
while (c) { while (c) {
if (++tries > maxtries) { if (++tries > maxtries) {
depth = maxtries; descend = maxtries;
break; break;
} }
if ( XTestCompareCurrentCursorWithWindow(dpy, c) ) { if ( XTestCompareCurrentCursorWithWindow(dpy, c) ) {
break; break;
} }
XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &mask); XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &mask);
depth++; descend++;
} }
X_UNLOCK; X_UNLOCK;
return depth; return descend;
} }
/* /*
...@@ -1258,8 +1646,8 @@ void update_mouse(void) { ...@@ -1258,8 +1646,8 @@ void update_mouse(void) {
} }
if (show_root_cursor) { if (show_root_cursor) {
int depth; int descend;
if ( (depth = tree_depth_cursor()) ) { if ( (descend = tree_descend_cursor()) ) {
which = 0; which = 0;
} else { } else {
which = 1; which = 1;
...@@ -1396,6 +1784,58 @@ void set_colormap(void) { ...@@ -1396,6 +1784,58 @@ void set_colormap(void) {
first = 0; first = 0;
} }
/*
* Experimental mode to force the visual of the window instead of querying
* it. Currently just used for testing or overriding some rare cases.
* Input string can be a decimal or 0x hex or something like TrueColor
* or TrueColor:24 to force a depth as well.
*/
void set_visual(char *vstring) {
int vis, defdepth = DefaultDepth(dpy, scr);
XVisualInfo vinfo;
char *p;
fprintf(stderr, "set_visual: %s\n", vstring);
if ((p = strchr(vstring, ':')) != NULL) {
visual_depth = atoi(p+1);
*p = '\0';
} else {
visual_depth = defdepth;
}
if (strcmp(vstring, "StaticGray") == 0) {
vis = StaticGray;
} else if (strcmp(vstring, "GrayScale") == 0) {
vis = GrayScale;
} else if (strcmp(vstring, "StaticColor") == 0) {
vis = StaticColor;
} else if (strcmp(vstring, "PseudoColor") == 0) {
vis = PseudoColor;
} else if (strcmp(vstring, "TrueColor") == 0) {
vis = TrueColor;
} else if (strcmp(vstring, "DirectColor") == 0) {
vis = DirectColor;
} else {
if (sscanf(vstring, "0x%x", &visual_id) != 1) {
if (sscanf(vstring, "%d", &visual_id) == 1) {
return;
}
fprintf(stderr, "bad -visual arg: %s\n", vstring);
exit(1);
}
return;
}
if (XMatchVisualInfo(dpy, scr, visual_depth, vis, &vinfo)) {
;
} else if (XMatchVisualInfo(dpy, scr, defdepth, vis, &vinfo)) {
;
} else {
fprintf(stderr, "could not find visual: %s\n", vstring);
exit(1);
}
visual_id = vinfo.visualid;
}
/* /*
* Presumably under -nofb the clients will never request the framebuffer. * Presumably under -nofb the clients will never request the framebuffer.
* But we have gotten such a request... so let's just give them the * But we have gotten such a request... so let's just give them the
...@@ -1519,6 +1959,9 @@ void initialize_screen(int *argc, char **argv, XImage *fb) { ...@@ -1519,6 +1959,9 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
screen->newClientHook = new_client; screen->newClientHook = new_client;
screen->kbdAddEvent = keyboard; screen->kbdAddEvent = keyboard;
screen->ptrAddEvent = pointer; screen->ptrAddEvent = pointer;
if (watch_selection) {
screen->setXCutText = xcut_receive;
}
if (local_cursor) { if (local_cursor) {
cursor = rfbMakeXCursor(CUR_SIZE, CUR_SIZE, CUR_DATA, CUR_MASK); cursor = rfbMakeXCursor(CUR_SIZE, CUR_SIZE, CUR_DATA, CUR_MASK);
...@@ -1585,13 +2028,17 @@ void set_fs_factor(int max) { ...@@ -1585,13 +2028,17 @@ void set_fs_factor(int max) {
/* /*
* set up an XShm image * set up an XShm image
*/ */
void shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h, int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
char *name) { char *name) {
XImage *xim; XImage *xim;
shm->shmid = -1;
shm->shmaddr = (char *) -1;
*ximg_ptr = NULL;
if (nofb) { if (nofb) {
return; return 1;
} }
X_LOCK; X_LOCK;
...@@ -1605,12 +2052,12 @@ void shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h, ...@@ -1605,12 +2052,12 @@ void shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
if (xim == NULL) { if (xim == NULL) {
rfbErr("XCreateImage(%s) failed.\n", name); rfbErr("XCreateImage(%s) failed.\n", name);
exit(1); return 0;
} }
xim->data = (char *) malloc(xim->bytes_per_line * xim->height); xim->data = (char *) malloc(xim->bytes_per_line * xim->height);
if (xim->data == NULL) { if (xim->data == NULL) {
rfbErr("XCreateImage(%s) data malloc failed.\n", name); rfbErr("XCreateImage(%s) data malloc failed.\n", name);
exit(1); return 0;
} }
if (flip_byte_order) { if (flip_byte_order) {
static int reported = 0; static int reported = 0;
...@@ -1632,26 +2079,31 @@ void shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h, ...@@ -1632,26 +2079,31 @@ void shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
} }
*ximg_ptr = xim; *ximg_ptr = xim;
return; return 1;
} }
xim = XShmCreateImage(dpy, visual, depth, ZPixmap, NULL, shm, w, h); xim = XShmCreateImage(dpy, visual, depth, ZPixmap, NULL, shm, w, h);
if (xim == NULL) { if (xim == NULL) {
rfbErr("XShmCreateImage(%s) failed.\n", name); rfbErr("XShmCreateImage(%s) failed.\n", name);
exit(1); X_UNLOCK;
return 0;
} }
*ximg_ptr = xim; *ximg_ptr = xim;
shm->shmid = shmget(IPC_PRIVATE, shm->shmid = shmget(IPC_PRIVATE,
xim->bytes_per_line * xim->height, IPC_CREAT | 0777); xim->bytes_per_line * xim->height, IPC_CREAT | 0777);
if (shm->shmid == -1) { if (shm->shmid == -1) {
rfbErr("shmget(%s) failed.\n", name); rfbErr("shmget(%s) failed.\n", name);
perror("shmget"); perror("shmget");
exit(1);
XDestroyImage(xim);
*ximg_ptr = NULL;
X_UNLOCK;
return 0;
} }
shm->shmaddr = xim->data = (char *) shmat(shm->shmid, 0, 0); shm->shmaddr = xim->data = (char *) shmat(shm->shmid, 0, 0);
...@@ -1659,25 +2111,48 @@ void shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h, ...@@ -1659,25 +2111,48 @@ void shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
if (shm->shmaddr == (char *)-1) { if (shm->shmaddr == (char *)-1) {
rfbErr("shmat(%s) failed.\n", name); rfbErr("shmat(%s) failed.\n", name);
perror("shmat"); perror("shmat");
exit(1);
XDestroyImage(xim);
*ximg_ptr = NULL;
shmctl(shm->shmid, IPC_RMID, 0);
shm->shmid = -1;
X_UNLOCK;
return 0;
} }
shm->readOnly = False; shm->readOnly = False;
if (! XShmAttach(dpy, shm)) { if (! XShmAttach(dpy, shm)) {
rfbErr("XShmAttach(%s) failed.\n", name); rfbErr("XShmAttach(%s) failed.\n", name);
exit(1); XDestroyImage(xim);
*ximg_ptr = NULL;
shmdt(shm->shmaddr);
shm->shmaddr = (char *) -1;
shmctl(shm->shmid, IPC_RMID, 0);
shm->shmid = -1;
X_UNLOCK;
return 0;
} }
X_UNLOCK; X_UNLOCK;
return 1;
} }
void shm_delete(XShmSegmentInfo *shm) { void shm_delete(XShmSegmentInfo *shm) {
if (! using_shm) { if (! using_shm) {
return; return;
} }
shmdt(shm->shmaddr); if (shm->shmaddr != (char *) -1) {
shmctl(shm->shmid, IPC_RMID, 0); shmdt(shm->shmaddr);
}
if (shm->shmid != -1) {
shmctl(shm->shmid, IPC_RMID, 0);
}
} }
void shm_clean(XShmSegmentInfo *shm, XImage *xim) { void shm_clean(XShmSegmentInfo *shm, XImage *xim) {
...@@ -1685,8 +2160,12 @@ void shm_clean(XShmSegmentInfo *shm, XImage *xim) { ...@@ -1685,8 +2160,12 @@ void shm_clean(XShmSegmentInfo *shm, XImage *xim) {
return; return;
} }
X_LOCK; X_LOCK;
XShmDetach(dpy, shm); if (shm->shmid != -1) {
XDestroyImage(xim); XShmDetach(dpy, shm);
}
if (xim != NULL) {
XDestroyImage(xim);
}
X_UNLOCK; X_UNLOCK;
shm_delete(shm); shm_delete(shm);
...@@ -1695,28 +2174,33 @@ void shm_clean(XShmSegmentInfo *shm, XImage *xim) { ...@@ -1695,28 +2174,33 @@ void shm_clean(XShmSegmentInfo *shm, XImage *xim) {
void initialize_shm() { void initialize_shm() {
int i; int i;
/* the tile (e.g. 32x32) shared memory area image: */ /* set all shm areas to "none" before trying to create any */
tile_shm.shmid = -1;
shm_create(&tile_shm, &tile, tile_x, tile_y, "tile"); tile_shm.shmaddr = (char *) -1;
tile = NULL;
scanline_shm.shmid = -1;
scanline_shm.shmaddr = (char *) -1;
scanline = NULL;
fullscreen_shm.shmid = -1;
fullscreen_shm.shmaddr = (char *) -1;
fullscreen = NULL;
for (i=1; i<=ntiles_x; i++) {
tile_row_shm[i].shmid = -1;
tile_row_shm[i].shmaddr = (char *) -1;
tile_row[i] = NULL;
}
/* /* the tile (e.g. 32x32) shared memory area image: */
* for copy_tiles we need a lot of shared memory areas, one for
* each possible run length of changed tiles. 32 for 1024x768
* and 40 for 1280x1024, etc.
*/
for (i=1; i<=ntiles_x; i++) { if (! shm_create(&tile_shm, &tile, tile_x, tile_y, "tile")) {
shm_create(&tile_row_shm[i], &tile_row[i], tile_x * i, tile_y, clean_up_exit(1);
"tile_row");
if (old_copytile && i == 1) {
/* only need 1x1 tiles */
break;
}
} }
/* the scanline (e.g. 1280x1) shared memory area image: */ /* the scanline (e.g. 1280x1) shared memory area image: */
shm_create(&scanline_shm, &scanline, dpy_x, 1, "scanline"); if (! shm_create(&scanline_shm, &scanline, dpy_x, 1, "scanline")) {
clean_up_exit(1);
}
/* /*
* the fullscreen (e.g. 1280x1024/fs_factor) shared memory area image: * the fullscreen (e.g. 1280x1024/fs_factor) shared memory area image:
...@@ -1726,11 +2210,36 @@ void initialize_shm() { ...@@ -1726,11 +2210,36 @@ void initialize_shm() {
set_fs_factor(1024 * 1024); set_fs_factor(1024 * 1024);
if (! fs_factor) { if (! fs_factor) {
fprintf(stderr, "warning: fullscreen updates are disabled.\n"); fprintf(stderr, "warning: fullscreen updates are disabled.\n");
return; } else {
if (! shm_create(&fullscreen_shm, &fullscreen, dpy_x,
dpy_y/fs_factor, "fullscreen")) {
clean_up_exit(1);
}
} }
shm_create(&fullscreen_shm, &fullscreen, dpy_x, dpy_y/fs_factor, /*
"fullscreen"); * for copy_tiles we need a lot of shared memory areas, one for
* each possible run length of changed tiles. 32 for 1024x768
* and 40 for 1280x1024, etc.
*/
for (i=1; i<=ntiles_x; i++) {
if (! shm_create(&tile_row_shm[i], &tile_row[i], tile_x * i,
tile_y, "tile_row")) {
int j;
if (i == 1) {
clean_up_exit(1);
}
rfbLog("error creating tile-row shm for len=%d\n", i);
rfbLog("reverting to single_copytile mode\n");
/* n.b.: "i" not "1", a kludge for cleanup */
single_copytile = i;
}
if (single_copytile && i >= 1) {
/* only need 1x1 tiles */
break;
}
}
} }
...@@ -1830,6 +2339,7 @@ void hint_updates() { ...@@ -1830,6 +2339,7 @@ void hint_updates() {
} }
} }
for (i=0; i < hint_count; i++) { for (i=0; i < hint_count; i++) {
/* pass update info to vnc: */ /* pass update info to vnc: */
mark_hint(hint_list[i]); mark_hint(hint_list[i]);
...@@ -2155,6 +2665,7 @@ void copy_tiles(int tx, int ty, int nt) { ...@@ -2155,6 +2665,7 @@ void copy_tiles(int tx, int ty, int nt) {
w1 = width1 * pixelsize; w1 = width1 * pixelsize;
w2 = width2 * pixelsize; w2 = width2 * pixelsize;
/* foreach line: */ /* foreach line: */
for (line = 0; line < size_y; line++) { for (line = 0; line < size_y; line++) {
/* foreach horizontal tile: */ /* foreach horizontal tile: */
...@@ -2989,7 +3500,7 @@ void scan_for_updates() { ...@@ -2989,7 +3500,7 @@ void scan_for_updates() {
/* copy all tiles with differences from display to rfb framebuffer: */ /* copy all tiles with differences from display to rfb framebuffer: */
fb_copy_in_progress = 1; fb_copy_in_progress = 1;
if (old_copytile) { if (single_copytile) {
/* /*
* Old way, copy I/O one tile at a time. * Old way, copy I/O one tile at a time.
*/ */
...@@ -3066,6 +3577,7 @@ void watch_loop(void) { ...@@ -3066,6 +3577,7 @@ void watch_loop(void) {
if (! use_threads) { if (! use_threads) {
rfbProcessEvents(screen, -1); rfbProcessEvents(screen, -1);
//fprintf(stderr, "watch_loop rfbProcessEvents done.\n");
if (check_user_input(dt, &cnt)) { if (check_user_input(dt, &cnt)) {
/* true means loop back for more input */ /* true means loop back for more input */
continue; continue;
...@@ -3081,6 +3593,10 @@ void watch_loop(void) { ...@@ -3081,6 +3593,10 @@ void watch_loop(void) {
continue; continue;
} }
if (watch_selection) {
watch_selection_event();
}
if (nofb) { /* no framebuffer polling needed */ if (nofb) { /* no framebuffer polling needed */
continue; continue;
} }
...@@ -3092,7 +3608,6 @@ void watch_loop(void) { ...@@ -3092,7 +3608,6 @@ void watch_loop(void) {
*/ */
watch_bell_event(); watch_bell_event();
} }
if (! show_dragging && button_mask) { if (! show_dragging && button_mask) {
/* if any button is pressed do not update screen */ /* if any button is pressed do not update screen */
/* XXX consider: use_threads || got_pointer_input */ /* XXX consider: use_threads || got_pointer_input */
...@@ -3295,6 +3810,14 @@ void print_help() { ...@@ -3295,6 +3810,14 @@ void print_help() {
" as the pointer moves from window to window (slow).\n" " as the pointer moves from window to window (slow).\n"
"-notruecolor Force 8bpp indexed color even if it looks like TrueColor.\n" "-notruecolor Force 8bpp indexed color even if it looks like TrueColor.\n"
"\n" "\n"
"-visual n Experimental option: probably does not do what you think.\n"
" It simply *forces* the visual used for the framebuffer;\n"
" this may be a bad thing... It is useful for testing and\n"
" for some workarounds. n may be a decimal number, or 0x\n"
" hex. Run xdpyinfo(1) for the values. One may also use\n"
" \"TrueColor\", etc. see <X11/X.h> for a list. If the\n"
" string ends in \":m\" the visual depth is forced to be m.\n"
"\n"
"-viewonly Clients can only watch (default %s).\n" "-viewonly Clients can only watch (default %s).\n"
"-shared VNC display is shared (default %s).\n" "-shared VNC display is shared (default %s).\n"
"-forever Keep listening for more connections rather than exiting\n" "-forever Keep listening for more connections rather than exiting\n"
...@@ -3324,6 +3847,8 @@ void print_help() { ...@@ -3324,6 +3847,8 @@ void print_help() {
"-nomodtweak Send the keysym directly to the X server.\n" "-nomodtweak Send the keysym directly to the X server.\n"
"-nobell Do not watch for XBell events.\n" "-nobell Do not watch for XBell events.\n"
"-nofb Ignore framebuffer: only process keyboard and pointer.\n" "-nofb Ignore framebuffer: only process keyboard and pointer.\n"
"-nosel Do not manage exchange of X selection/cutbuffer.\n"
"-noprimary Exchange X cutbuffer changes but not PRIMARY selection.\n"
"\n" "\n"
"-nocursor Do not have the viewer show a local cursor.\n" "-nocursor Do not have the viewer show a local cursor.\n"
"-mouse Draw a 2nd cursor at the current X pointer position.\n" "-mouse Draw a 2nd cursor at the current X pointer position.\n"
...@@ -3347,8 +3872,10 @@ void print_help() { ...@@ -3347,8 +3872,10 @@ void print_help() {
" to cut down on load (default %d).\n" " to cut down on load (default %d).\n"
"-nap Monitor activity and if low take longer naps between\n" "-nap Monitor activity and if low take longer naps between\n"
" polls to really cut down load when idle (default %s).\n" " polls to really cut down load when idle (default %s).\n"
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
"-threads Whether or not to use the threaded libvncserver\n" "-threads Whether or not to use the threaded libvncserver\n"
"-nothreads algorithm [rfbRunEventLoop] (default %s).\n" "-nothreads algorithm [rfbRunEventLoop] (default %s).\n"
#endif
"\n" "\n"
"-fs f If the fraction of changed tiles in a poll is greater\n" "-fs f If the fraction of changed tiles in a poll is greater\n"
" than f, the whole screen is updated (default %.2f).\n" " than f, the whole screen is updated (default %.2f).\n"
...@@ -3365,6 +3892,10 @@ void print_help() { ...@@ -3365,6 +3892,10 @@ void print_help() {
"\n" "\n"
"These options are passed to libvncserver:\n" "These options are passed to libvncserver:\n"
"\n" "\n"
;
"\n"
"These options are passed to libvncserver:\n"
"\n"
; ;
fprintf(stderr, help, fprintf(stderr, help,
view_only ? "on":"off", view_only ? "on":"off",
...@@ -3427,6 +3958,7 @@ int main(int argc, char** argv) { ...@@ -3427,6 +3958,7 @@ int main(int argc, char** argv) {
XImage *fb; XImage *fb;
int i, op, ev, er, maj, min; int i, op, ev, er, maj, min;
char *use_dpy = NULL; char *use_dpy = NULL;
char *visual_str = NULL;
int dt = 0; int dt = 0;
int bg = 0; int bg = 0;
int got_waitms = 0; int got_waitms = 0;
...@@ -3447,6 +3979,8 @@ int main(int argc, char** argv) { ...@@ -3447,6 +3979,8 @@ int main(int argc, char** argv) {
exit(1); exit(1);
} }
} }
} else if (!strcmp(argv[i], "-visual")) {
visual_str = argv[++i];
} else if (!strcmp(argv[i], "-flashcmap")) { } else if (!strcmp(argv[i], "-flashcmap")) {
flash_cmap = 1; flash_cmap = 1;
} else if (!strcmp(argv[i], "-notruecolor")) { } else if (!strcmp(argv[i], "-notruecolor")) {
...@@ -3476,6 +4010,10 @@ int main(int argc, char** argv) { ...@@ -3476,6 +4010,10 @@ int main(int argc, char** argv) {
watch_bell = 0; watch_bell = 0;
} else if (!strcmp(argv[i], "-nofb")) { } else if (!strcmp(argv[i], "-nofb")) {
nofb = 1; nofb = 1;
} else if (!strcmp(argv[i], "-nosel")) {
watch_selection = 0;
} else if (!strcmp(argv[i], "-noprimary")) {
watch_primary = 0;
} else if (!strcmp(argv[i], "-nocursor")) { } else if (!strcmp(argv[i], "-nocursor")) {
local_cursor = 0; local_cursor = 0;
} else if (!strcmp(argv[i], "-mouse")) { } else if (!strcmp(argv[i], "-mouse")) {
...@@ -3497,7 +4035,7 @@ int main(int argc, char** argv) { ...@@ -3497,7 +4035,7 @@ int main(int argc, char** argv) {
} else if (!strcmp(argv[i], "-old_pointer")) { } else if (!strcmp(argv[i], "-old_pointer")) {
old_pointer = 1; old_pointer = 1;
} else if (!strcmp(argv[i], "-old_copytile")) { } else if (!strcmp(argv[i], "-old_copytile")) {
old_copytile = 1; single_copytile = 1;
} else if (!strcmp(argv[i], "-defer")) { } else if (!strcmp(argv[i], "-defer")) {
defer_update = atoi(argv[++i]); defer_update = atoi(argv[++i]);
} else if (!strcmp(argv[i], "-wait")) { } else if (!strcmp(argv[i], "-wait")) {
...@@ -3634,6 +4172,10 @@ int main(int argc, char** argv) { ...@@ -3634,6 +4172,10 @@ int main(int argc, char** argv) {
exit(1); exit(1);
} }
} }
if (visual_str != NULL) {
set_visual(visual_str);
}
#ifdef LIBVNCSERVER_HAVE_XKEYBOARD #ifdef LIBVNCSERVER_HAVE_XKEYBOARD
/* check for XKEYBOARD */ /* check for XKEYBOARD */
if (watch_bell) { if (watch_bell) {
...@@ -3687,22 +4229,63 @@ int main(int argc, char** argv) { ...@@ -3687,22 +4229,63 @@ int main(int argc, char** argv) {
set_offset(); set_offset();
} }
/* initialize depth to reasonable value */
depth = DefaultDepth(dpy, scr);
if (nofb) { /*
* User asked for non-default visual, this is not working well but it
* does some useful things... What should it do in general?
*/
if (visual_id) {
XVisualInfo vinfo_tmpl, *vinfo;
int n;
vinfo_tmpl.visualid = visual_id;
vinfo = XGetVisualInfo(dpy, VisualIDMask, &vinfo_tmpl, &n);
if (vinfo == NULL || n == 0) {
fprintf(stderr, "could not match visual_id: 0x%x\n",
visual_id);
exit(1);
}
visual = vinfo->visual;
depth = vinfo->depth;
if (visual_depth) {
depth = visual_depth; /* force it */
}
if (! quiet) {
fprintf(stderr, "vis id: 0x%x\n", vinfo->visualid);
fprintf(stderr, "vis scr: %d\n", vinfo->screen);
fprintf(stderr, "vis depth %d\n", vinfo->depth);
fprintf(stderr, "vis class %d\n", vinfo->class);
fprintf(stderr, "vis rmask 0x%x\n", vinfo->red_mask);
fprintf(stderr, "vis gmask 0x%x\n", vinfo->green_mask);
fprintf(stderr, "vis bmask 0x%x\n", vinfo->blue_mask);
fprintf(stderr, "vis cmap_sz %d\n", vinfo->colormap_size);
fprintf(stderr, "vis b/rgb %d\n", vinfo->bits_per_rgb);
}
XFree(vinfo);
}
if (nofb || visual_id) {
fb = XCreateImage(dpy, visual, depth, ZPixmap, 0, NULL,
dpy_x, dpy_y, BitmapPad(dpy), 0);
/* /*
* This does not malloc the framebuffer, so can save a few * For -nofb we do not allocate the framebuffer, so we
* MB of memory in nofb mode. * can save a few MB of memory.
*/ */
fb = XCreateImage(dpy, visual, DefaultDepth(dpy, scr), ZPixmap, if (! nofb) {
0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0); fb->data = (char *) malloc(fb->bytes_per_line *
fb->height);
}
} else { } else {
fb = XGetImage(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, fb = XGetImage(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes,
ZPixmap); ZPixmap);
} if (! quiet) {
fprintf(stderr, "Read initial data from display into"
if (! quiet) { " framebuffer.\n");
fprintf(stderr, "Read initial data from display into" }
" framebuffer.\n");
} }
if (fb->bits_per_pixel == 24 && ! quiet) { if (fb->bits_per_pixel == 24 && ! quiet) {
fprintf(stderr, "warning: 24 bpp may have poor" fprintf(stderr, "warning: 24 bpp may have poor"
...@@ -3749,12 +4332,12 @@ int main(int argc, char** argv) { ...@@ -3749,12 +4332,12 @@ int main(int argc, char** argv) {
} else if (p == -1) { } else if (p == -1) {
fprintf(stderr, "could not fork\n"); fprintf(stderr, "could not fork\n");
perror("fork"); perror("fork");
exit(1); clean_up_exit(1);
} }
if (setsid() == -1) { if (setsid() == -1) {
fprintf(stderr, "setsid failed\n"); fprintf(stderr, "setsid failed\n");
perror("setsid"); perror("setsid");
exit(1); clean_up_exit(1);
} }
/* adjust our stdio */ /* adjust our stdio */
n = open("/dev/null", O_RDONLY); n = open("/dev/null", O_RDONLY);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment