/* -- screen.c -- */ #include "x11vnc.h" #include "xevents.h" #include "xwrappers.h" #include "xinerama.h" #include "xdamage.h" #include "win_utils.h" #include "cleanup.h" #include "userinput.h" #include "scan.h" #include "user.h" #include "rates.h" #include "pointer.h" #include "keyboard.h" #include "cursor.h" #include "connections.h" #include "remote.h" #include "unixpw.h" #include "sslcmds.h" #include "sslhelper.h" #include "v4l.h" #include "linuxfb.h" #include "macosx.h" #include "macosxCG.h" #include "avahi.h" #include "solid.h" #include "inet.h" #include "xrandr.h" #include "xrecord.h" #include "pm.h" #include void set_greyscale_colormap(void); void set_hi240_colormap(void); void unset_colormap(void); void set_colormap(int reset); void set_nofb_params(int restore); void set_raw_fb_params(int restore); void do_new_fb(int reset_mem); void free_old_fb(void); void check_padded_fb(void); void install_padded_fb(char *geom); XImage *initialize_xdisplay_fb(void); void parse_scale_string(char *str, double *factor_x, double *factor_y, int *scaling, int *blend, int *nomult4, int *pad, int *interpolate, int *numer, int *denom, int w_in, int h_in); int parse_rotate_string(char *str, int *mode); int scale_round(int len, double fac); void initialize_screen(int *argc, char **argv, XImage *fb); void set_vnc_desktop_name(void); void announce(int lport, int ssl, char *iface); char *vnc_reflect_guess(char *str, char **raw_fb_addr); void vnc_reflect_process_client(void); rfbBool vnc_reflect_send_pointer(int x, int y, int mask); rfbBool vnc_reflect_send_key(uint32_t key, rfbBool down); 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); static void remove_fake_fb(void); static void install_fake_fb(int w, int h, int bpp); static void initialize_snap_fb(void); XImage *initialize_raw_fb(int); static void initialize_clipshift(void); static int wait_until_mapped(Window win); static void setup_scaling(int *width_in, int *height_in); static void check_filexfer(void); static void record_last_fb_update(void); static void check_cursor_changes(void); static int choose_delay(double dt); int rawfb_reset = -1; int rawfb_dev_video = 0; int rawfb_vnc_reflect = 0; /* * X11 and rfb display/screen related routines */ /* * Some handling of 8bpp PseudoColor colormaps. Called for initializing * the clients and dynamically if -flashcmap is specified. */ #define NCOLOR 256 /* this is only for rawfb */ void set_greyscale_colormap(void) { int i; if (! screen) { return; } if (screen->colourMap.data.shorts) { free(screen->colourMap.data.shorts); screen->colourMap.data.shorts = NULL; } if (0) fprintf(stderr, "set_greyscale_colormap: %s\n", raw_fb_pixfmt); screen->colourMap.count = NCOLOR; screen->serverFormat.trueColour = FALSE; screen->colourMap.is16 = TRUE; screen->colourMap.data.shorts = (unsigned short *) malloc(3*sizeof(unsigned short) * NCOLOR); for(i = 0; i < NCOLOR; i++) { unsigned short lvl = i * 256; screen->colourMap.data.shorts[i*3+0] = lvl; screen->colourMap.data.shorts[i*3+1] = lvl; screen->colourMap.data.shorts[i*3+2] = lvl; } rfbSetClientColourMaps(screen, 0, NCOLOR); } /* this is specific to bttv rf tuner card */ void set_hi240_colormap(void) { int i; if (! screen) { return; } if (0) fprintf(stderr, "set_hi240_colormap: %s\n", raw_fb_pixfmt); if (screen->colourMap.data.shorts) { free(screen->colourMap.data.shorts); screen->colourMap.data.shorts = NULL; } screen->colourMap.count = 256; screen->serverFormat.trueColour = FALSE; screen->colourMap.is16 = TRUE; screen->colourMap.data.shorts = (unsigned short *) calloc(3*sizeof(unsigned short) * 256, 1); for(i = 0; i < 225; i++) { int r, g, b; r = ( (i/5) % 5 ) * 255.0 / 4 + 0.5; g = ( (i/25) ) * 255.0 / 8 + 0.5; b = ( i % 5 ) * 255.0 / 4 + 0.5; screen->colourMap.data.shorts[(i+16)*3+0] = (unsigned short) (r * 256); screen->colourMap.data.shorts[(i+16)*3+1] = (unsigned short) (g * 256); screen->colourMap.data.shorts[(i+16)*3+2] = (unsigned short) (b * 256); } rfbSetClientColourMaps(screen, 0, 256); } /* this is only for rawfb */ void unset_colormap(void) { if (! screen) { return; } if (screen->colourMap.data.shorts) { free(screen->colourMap.data.shorts); screen->colourMap.data.shorts = NULL; } screen->serverFormat.trueColour = TRUE; if (0) fprintf(stderr, "unset_colormap: %s\n", raw_fb_pixfmt); } /* this is X11 case */ void set_colormap(int reset) { #if NO_X11 if (!reset) {} return; #else static int init = 1; static XColor *color = NULL, *prev = NULL; static int ncolor = 0; Colormap cmap; Visual *vis; int i, ncells, diffs = 0; if (reset) { init = 1; ncolor = 0; if (screen->colourMap.data.shorts) { free(screen->colourMap.data.shorts); screen->colourMap.data.shorts = NULL; } if (color) { free(color); color = NULL; } if (prev) { free(prev); prev = NULL; } } if (init) { if (depth > 16) { ncolor = NCOLOR; } else if (depth > 8) { ncolor = 1 << depth; } else { ncolor = NCOLOR; } screen->colourMap.count = ncolor; screen->serverFormat.trueColour = FALSE; screen->colourMap.is16 = TRUE; screen->colourMap.data.shorts = (unsigned short *) malloc(3*sizeof(unsigned short) * ncolor); } if (color == NULL) { color = (XColor *) calloc(ncolor * sizeof(XColor), 1); prev = (XColor *) calloc(ncolor * sizeof(XColor), 1); } for (i=0; i < ncolor; i++) { prev[i].red = color[i].red; prev[i].green = color[i].green; prev[i].blue = color[i].blue; } RAWFB_RET_VOID X_LOCK; cmap = DefaultColormap(dpy, scr); ncells = CellsOfScreen(ScreenOfDisplay(dpy, scr)); vis = default_visual; if (subwin) { XWindowAttributes attr; if (XGetWindowAttributes(dpy, window, &attr)) { cmap = attr.colormap; vis = attr.visual; ncells = vis->map_entries; } } if (ncells != ncolor) { if (! shift_cmap) { screen->colourMap.count = ncells; } } if (init && ! quiet) { rfbLog("set_colormap: number of cells: %d, " "ncolor(%d) is %d.\n", ncells, depth, ncolor); } if (flash_cmap && ! init) { XWindowAttributes attr; Window c; int tries = 0; c = window; while (c && tries++ < 16) { c = query_pointer(c); if (valid_window(c, &attr, 0)) { if (attr.colormap && attr.map_installed) { cmap = attr.colormap; vis = attr.visual; ncells = vis->map_entries; break; } } else { break; } } } if (ncells > ncolor && ! quiet) { rfbLog("set_colormap: big problem: ncells=%d > %d\n", ncells, ncolor); } if (vis->class == TrueColor || vis->class == DirectColor) { /* * Kludge to make 8bpp TrueColor & DirectColor be like * the StaticColor map. The ncells = 8 is "8 per subfield" * mentioned in xdpyinfo. Looks OK... perhaps fortuitously. */ if (ncells == 8 && ! shift_cmap) { ncells = ncolor; } } for (i=0; i < ncells; i++) { color[i].pixel = i; color[i].pad = 0; } XQueryColors(dpy, cmap, color, ncells); X_UNLOCK; for(i = ncells - 1; i >= 0; i--) { int k = i + shift_cmap; screen->colourMap.data.shorts[i*3+0] = color[i].red; screen->colourMap.data.shorts[i*3+1] = color[i].green; screen->colourMap.data.shorts[i*3+2] = color[i].blue; if (prev[i].red != color[i].red || prev[i].green != color[i].green || prev[i].blue != color[i].blue ) { diffs++; } if (shift_cmap && k >= 0 && k < ncolor) { /* kludge to copy the colors to higher pixel values */ screen->colourMap.data.shorts[k*3+0] = color[i].red; screen->colourMap.data.shorts[k*3+1] = color[i].green; screen->colourMap.data.shorts[k*3+2] = color[i].blue; } } if (diffs && ! init) { if (! all_clients_initialized()) { rfbLog("set_colormap: warning: sending cmap " "with uninitialized clients.\n"); } if (shift_cmap) { rfbSetClientColourMaps(screen, 0, ncolor); } else { rfbSetClientColourMaps(screen, 0, ncells); } } init = 0; #endif /* NO_X11 */ } static void debug_colormap(XImage *fb) { static int debug_cmap = -1; int i, k, *histo; int ncolor; if (debug_cmap < 0) { if (getenv("DEBUG_CMAP") != NULL) { debug_cmap = 1; } else { debug_cmap = 0; } } if (! debug_cmap) { return; } if (! fb) { return; } if (fb->bits_per_pixel > 16) { return; } ncolor = screen->colourMap.count; histo = (int *) calloc(ncolor * sizeof(int), 1); for (i=0; i < ncolor; i++) { histo[i] = 0; } for (k = 0; k < fb->width * fb->height; k++) { unsigned char n; char c = *(fb->data + k); n = (unsigned char) c; histo[n]++; } fprintf(stderr, "\nColormap histogram for current screen contents:\n"); for (i=0; i < ncolor; i++) { unsigned short r = screen->colourMap.data.shorts[i*3+0]; unsigned short g = screen->colourMap.data.shorts[i*3+1]; unsigned short b = screen->colourMap.data.shorts[i*3+2]; fprintf(stderr, " %03d: %7d %04x/%04x/%04x", i, histo[i], r, g, b); if ((i+1) % 2 == 0) { fprintf(stderr, "\n"); } } free(histo); fprintf(stderr, "\n"); } /* * Experimental mode to force the visual of the window instead of querying * it. Used for testing, overriding some rare cases (win2vnc), and for * -overlay . Input string can be a decimal or 0x hex or something like * TrueColor or TrueColor:24 to force a depth as well. * * visual_id and possibly visual_depth are set. */ static void set_visual(char *str) { #if NO_X11 RAWFB_RET_VOID if (!str) {} return; #else int vis, vdepth, defdepth; XVisualInfo vinfo; char *p, *vstring = strdup(str); RAWFB_RET_VOID defdepth = DefaultDepth(dpy, scr); visual_id = (VisualID) 0; visual_depth = 0; if (!strcmp(vstring, "ignore") || !strcmp(vstring, "default") || !strcmp(vstring, "")) { free(vstring); return; } /* set visual depth */ if ((p = strchr(vstring, ':')) != NULL) { visual_depth = atoi(p+1); *p = '\0'; vdepth = visual_depth; } else { vdepth = defdepth; } if (! quiet) { fprintf(stderr, "\nVisual Info:\n"); fprintf(stderr, " set_visual(\"%s\")\n", str); fprintf(stderr, " visual_depth: %d\n", vdepth); } /* set visual id number */ 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 { unsigned int v_in; if (sscanf(vstring, "0x%x", &v_in) != 1) { if (sscanf(vstring, "%u", &v_in) == 1) { visual_id = (VisualID) v_in; return; } rfbLogEnable(1); rfbLog("invalid -visual arg: %s\n", vstring); X_UNLOCK; clean_up_exit(1); } visual_id = (VisualID) v_in; free(vstring); return; } if (! quiet) fprintf(stderr, " visual: %d\n", vis); if (XMatchVisualInfo(dpy, scr, visual_depth, vis, &vinfo)) { ; } else if (XMatchVisualInfo(dpy, scr, defdepth, vis, &vinfo)) { ; } else { rfbLogEnable(1); rfbLog("could not find visual: %s\n", vstring); X_UNLOCK; clean_up_exit(1); } free(vstring); /* set numerical visual id. */ visual_id = vinfo.visualid; #endif /* NO_X11 */ } void set_nofb_params(int restore) { static int first = 1; static int save[100]; static char *scroll = NULL; int i = 0; if (first) { first = 0; save[i++] = use_xfixes; save[i++] = use_xdamage; save[i++] = use_xrecord; save[i++] = wireframe; save[i++] = use_solid_bg; save[i++] = overlay; save[i++] = overlay_cursor; save[i++] = using_shm; save[i++] = single_copytile; save[i++] = take_naps; save[i++] = measure_speeds; save[i++] = grab_buster; save[i++] = show_cursor; save[i++] = cursor_shape_updates; save[i++] = cursor_pos_updates; save[i++] = ncache; scroll = scroll_copyrect; } if (restore) { i = 0; use_xfixes = save[i++]; use_xdamage = save[i++]; use_xrecord = save[i++]; wireframe = save[i++]; use_solid_bg = save[i++]; overlay = save[i++]; overlay_cursor = save[i++]; using_shm = save[i++]; single_copytile = save[i++]; take_naps = save[i++]; measure_speeds = save[i++]; grab_buster = save[i++]; show_cursor = save[i++]; cursor_shape_updates = save[i++]; cursor_pos_updates = save[i++]; ncache = save[i++]; scroll_copyrect = scroll; if (cursor_shape_updates) { restore_cursor_shape_updates(screen); } initialize_cursors_mode(); return; } use_xfixes = 0; use_xdamage = 0; use_xrecord = 0; wireframe = 0; use_solid_bg = 0; overlay = 0; overlay_cursor = 0; using_shm = 0; single_copytile = 1; take_naps = 0; measure_speeds = 0; /* got_grab_buster? */ grab_buster = 0; show_cursor = 0; show_multiple_cursors = 0; cursor_shape_updates = 0; if (! got_cursorpos) { cursor_pos_updates = 0; } ncache = 0; scroll_copyrect = "never"; if (! quiet) { rfbLog("disabling: xfixes, xdamage, solid, overlay, shm,\n"); rfbLog(" wireframe, scrollcopyrect, ncache,\n"); rfbLog(" noonetile, nap, cursor, %scursorshape\n", got_cursorpos ? "" : "cursorpos, " ); rfbLog(" in -nofb mode.\n"); } } static char *raw_fb_orig_dpy = NULL; void set_raw_fb_params(int restore) { static int first = 1; static int vo0, us0, sm0, ws0, wp0, wc0, wb0, na0, tn0; static int xr0, xrm0, sb0, re0; static char *mc0; /* * set turn off a bunch of parameters not compatible with * -rawfb mode: 1) ignoring the X server 2) ignoring user input. */ if (first) { /* at least save the initial settings... */ vo0 = view_only; ws0 = watch_selection; wp0 = watch_primary; wc0 = watch_clipboard; wb0 = watch_bell; na0 = no_autorepeat; sb0 = use_solid_bg; us0 = use_snapfb; sm0 = using_shm; tn0 = take_naps; xr0 = xrandr; xrm0 = xrandr_maybe; re0 = noxrecord; mc0 = multiple_cursors_mode; first = 0; } if (restore) { view_only = vo0; watch_selection = ws0; watch_primary = wp0; watch_clipboard = wc0; watch_bell = wb0; no_autorepeat = na0; use_solid_bg = sb0; use_snapfb = us0; using_shm = sm0; take_naps = tn0; xrandr = xr0; xrandr_maybe = xrm0; noxrecord = re0; multiple_cursors_mode = mc0; if (! dpy && raw_fb_orig_dpy) { dpy = XOpenDisplay_wr(raw_fb_orig_dpy); if (dpy) { if (! quiet) rfbLog("reopened DISPLAY: %s\n", raw_fb_orig_dpy); scr = DefaultScreen(dpy); rootwin = RootWindow(dpy, scr); check_xevents(1); } else { if (! quiet) rfbLog("WARNING: failed to reopen " "DISPLAY: %s\n", raw_fb_orig_dpy); } } return; } if (verbose) { rfbLog("set_raw_fb_params: modifying settings for " "-rawfb mode.\n"); } if (got_noviewonly) { /* * The user input parameters are not unset under * -noviewonly... this usage should be very rare * (i.e. rawfb but also send user input to the X * display, most likely using /dev/fb0 for some reason...) */ if (verbose) { rfbLog("rawfb: -noviewonly mode: still sending mouse and\n"); rfbLog("rawfb: keyboard input to the X DISPLAY!!\n"); } } else { /* Normal case: */ #if 0 if (! view_only && ! pipeinput_str) { if (! quiet) rfbLog(" rawfb: setting view_only\n"); view_only = 1; } #endif if (raw_fb_str && strstr(raw_fb_str, "vnc") == raw_fb_str) { ; } else if (watch_selection) { if (verbose) rfbLog(" rawfb: turning off " "watch_selection\n"); watch_selection = 0; } if (watch_primary) { if (verbose) rfbLog(" rawfb: turning off " "watch_primary\n"); watch_primary = 0; } if (watch_clipboard) { if (verbose) rfbLog(" rawfb: turning off " "watch_clipboard\n"); watch_clipboard = 0; } if (watch_bell) { if (verbose) rfbLog(" rawfb: turning off watch_bell\n"); watch_bell = 0; } if (no_autorepeat) { if (verbose) rfbLog(" rawfb: turning off " "no_autorepeat\n"); no_autorepeat = 0; } if (use_solid_bg) { if (verbose) rfbLog(" rawfb: turning off " "use_solid_bg\n"); use_solid_bg = 0; } #ifndef MACOSX if (raw_fb_str && strstr(raw_fb_str, "vnc") == raw_fb_str) { ; } else { multiple_cursors_mode = strdup("arrow"); } #endif } if (using_shm) { if (verbose) rfbLog(" rawfb: turning off using_shm\n"); using_shm = 0; } if (take_naps) { if (verbose) rfbLog(" rawfb: turning off take_naps\n"); take_naps = 0; } if (xrandr) { if (verbose) rfbLog(" rawfb: turning off xrandr\n"); xrandr = 0; } if (xrandr_maybe) { if (verbose) rfbLog(" rawfb: turning off xrandr_maybe\n"); xrandr_maybe = 0; } if (! noxrecord) { if (verbose) rfbLog(" rawfb: turning off xrecord\n"); noxrecord = 1; } } /* * Presumably under -nofb the clients will never request the framebuffer. * However, we have gotten such a request... so let's just give them * the current view on the display. n.b. x2vnc and perhaps win2vnc * requests a 1x1 pixel for some workaround so sadly this evidently * nearly always happens. */ static void nofb_hook(rfbClientPtr cl) { XImage *fb; XImage raw; rfbLog("framebuffer requested in -nofb mode by client %s\n", cl->host); /* ignore xrandr */ if (raw_fb && ! dpy) { fb = &raw; fb->data = (char *)malloc(32); } else { int use_real_ximage = 0; if (use_real_ximage) { fb = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap); } else { fb = &raw; fb->data = (char *) calloc(dpy_x*dpy_y*bpp/8, 1); } } main_fb = fb->data; rfb_fb = main_fb; screen->frameBuffer = rfb_fb; screen->displayHook = NULL; } void free_old_fb(void) { char *fbs[16]; int i, j, nfb = 0, db = 0; fbs[nfb++] = main_fb; main_fb = NULL; fbs[nfb++] = rfb_fb; rfb_fb = NULL; fbs[nfb++] = cmap8to24_fb; cmap8to24_fb = NULL; fbs[nfb++] = snap_fb; snap_fb = NULL; fbs[nfb++] = rot_fb; rot_fb = NULL; fbs[nfb++] = raw_fb; raw_fb = NULL; for (i=0; i < nfb; i++) { char *fb = fbs[i]; int freeit = 1; if (! fb || fb < (char *) 0x10) { continue; } for (j=0; j < i; j++) { if (fb == fbs[j]) { freeit = 0; break; } } if (freeit) { if (db) fprintf(stderr, "free: %i %p\n", i, fb); free(fb); } else { if (db) fprintf(stderr, "skip: %i %p\n", i, fb); } } } void do_new_fb(int reset_mem) { XImage *fb; /* for threaded we really should lock libvncserver out. */ if (use_threads) { rfbLog("warning: changing framebuffers while threaded may\n"); rfbLog(" not work, do not use -threads if problems arise.\n"); } if (reset_mem == 1) { /* reset_mem == 2 is a hack for changing users... */ clean_shm(0); free_tiles(); } free_old_fb(); fb = initialize_xdisplay_fb(); initialize_screen(NULL, NULL, fb); if (reset_mem) { initialize_tiles(); initialize_blackouts_and_xinerama(); initialize_polling_images(); } if (ncache) { check_ncache(1, 0); } } static void remove_fake_fb(void) { if (! screen) { return; } rfbLog("removing fake fb: 0x%x\n", fake_fb); do_new_fb(1); /* * fake_fb is freed in do_new_fb(), but we set to NULL here to * indicate it is gone. */ fake_fb = NULL; } static void install_fake_fb(int w, int h, int bpp) { int bpc; if (! screen) { return; } if (fake_fb) { free(fake_fb); } fake_fb = (char *) calloc(w*h*bpp/8, 1); if (! fake_fb) { rfbLog("could not create fake fb: %dx%d %d\n", w, h, bpp); return; } bpc = guess_bits_per_color(bpp); rfbLog("installing fake fb: %dx%d %d\n", w, h, bpp); rfbLog("rfbNewFramebuffer(0x%x, 0x%x, %d, %d, %d, %d, %d)\n", screen, fake_fb, w, h, bpc, 1, bpp/8); rfbNewFramebuffer(screen, fake_fb, w, h, bpc, 1, bpp/8); } void check_padded_fb(void) { if (! fake_fb) { return; } if (unixpw_in_progress) return; if (time(NULL) > pad_geometry_time+1 && all_clients_initialized()) { remove_fake_fb(); } } void install_padded_fb(char *geom) { int w, h; int ok = 1; if (! geom || *geom == '\0') { ok = 0; } else if (sscanf(geom, "%dx%d", &w, &h) != 2) { ok = 0; } w = nabs(w); h = nabs(h); if (w < 5) w = 5; if (h < 5) h = 5; if (!ok) { rfbLog("skipping invalid pad geometry: '%s'\n", NONUL(geom)); return; } install_fake_fb(w, h, bpp); pad_geometry_time = time(NULL); } static void initialize_snap_fb(void) { RAWFB_RET_VOID if (snap_fb) { free(snap_fb); } snap = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap); snap_fb = snap->data; } static rfbClient* client = NULL; void vnc_reflect_bell(rfbClient *cl) { if (cl) {} if (sound_bell) { if (unixpw_in_progress) { return; } if (! all_clients_initialized()) { rfbLog("vnc_reflect_bell: not sending bell: " "uninitialized clients\n"); } else { if (screen && client_count) { rfbSendBell(screen); } } } } void vnc_reflect_recv_cuttext(rfbClient *cl, const char *str, int len) { if (cl) {} if (unixpw_in_progress) { return; } if (! watch_selection) { return; } if (! all_clients_initialized()) { rfbLog("vnc_reflect_recv_cuttext: no send: uninitialized clients\n"); return; /* some clients initializing, cannot send */ } rfbSendServerCutText(screen, (char *)str, len); } void vnc_reflect_got_update(rfbClient *cl, int x, int y, int w, int h) { if (cl) {} if (use_xdamage) { static int first = 1; if (first) { collect_non_X_xdamage(-1, -1, -1, -1, 0); first = 0; } collect_non_X_xdamage(x, y, w, h, 1); } } void vnc_reflect_got_cursorshape(rfbClient *cl, int xhot, int yhot, int width, int height, int bytesPerPixel) { static int serial = 1; int i, j; char *pixels = NULL; unsigned long r, g, b; unsigned int ui = 0; unsigned long red_mask, green_mask, blue_mask; if (cl) {} if (unixpw_in_progress) { return; } if (! all_clients_initialized()) { rfbLog("vnc_reflect_got_copyshape: no send: uninitialized clients\n"); return; /* some clients initializing, cannot send */ } if (! client->rcSource) { return; } if (bytesPerPixel != 1 && bytesPerPixel != 2 && bytesPerPixel != 4) { return; } red_mask = (client->format.redMax << client->format.redShift); green_mask = (client->format.greenMax << client->format.greenShift); blue_mask = (client->format.blueMax << client->format.blueShift); pixels = (char *)malloc(4*width*height); for (j=0; jrcSource; ui = (unsigned long) *(p + j * width + i); } else if (bytesPerPixel == 2) { unsigned short* p = (unsigned short *) client->rcSource; ui = (unsigned long) *(p + j * width + i); } else if (bytesPerPixel == 4) { unsigned int* p = (unsigned int *) client->rcSource; ui = (unsigned long) *(p + j * width + i); } r = (red_mask & ui) >> client->format.redShift; g = (green_mask & ui) >> client->format.greenShift; b = (blue_mask & ui) >> client->format.blueShift; r = (255 * r) / client->format.redMax; g = (255 * g) / client->format.greenMax; b = (255 * b) / client->format.blueMax; ui = (r << 16 | g << 8 | b << 0) ; uic = (unsigned char *)client->rcMask; m = (int) *(uic + j * width + i); if (m) { ui |= 0xff000000; } uip = (unsigned int *)pixels; *(uip + j * width + i) = ui; } } store_cursor(serial++, (unsigned long*) pixels, width, height, 32, xhot, yhot); free(pixels); set_cursor(cursor_x, cursor_y, get_which_cursor()); } void vnc_reflect_got_copyrect(rfbClient *cl, int src_x, int src_y, int w, int h, int dest_x, int dest_y) { sraRegionPtr reg; int dx, dy, rc = -1; static int last_dx = 0, last_dy = 0; if (cl) {} if (unixpw_in_progress) { return; } if (! all_clients_initialized()) { rfbLog("vnc_reflect_got_copyrect: no send: uninitialized clients\n"); return; /* some clients initializing, cannot send */ } dx = dest_x - src_x; dy = dest_y - src_y; if (dx != last_dx || dy != last_dy) { rc = fb_push_wait(0.05, FB_COPY|FB_MOD); } if (0) fprintf(stderr, "vnc_reflect_got_copyrect: %dx%d+%d+%d %d %d rc=%d\n", dest_x, dest_y, w, h, dx, dy, rc); reg = sraRgnCreateRect(dest_x, dest_y, dest_x + w, dest_y + h); do_copyregion(reg, dx, dy, 0); sraRgnDestroy(reg); last_dx = dx; last_dy = dy; } rfbBool vnc_reflect_resize(rfbClient *cl) { static int first = 1; if(cl->frameBuffer) { free(cl->frameBuffer); } cl->frameBuffer= malloc(cl->width * cl->height * cl->format.bitsPerPixel/8); if (!first) { do_new_fb(1); } first = 0; return cl->frameBuffer ? TRUE : FALSE; } char *vnc_reflect_guess(char *str, char **raw_fb_addr) { static int first = 1; char *hp = str + strlen("vnc:"); char *at = NULL; int argc = 0, i; char *argv[16]; char str2[256]; char *str0 = strdup(str); if (client == NULL) { client = rfbGetClient(8, 3, 4); } rfbLog("rawfb: %s\n", str); at = strchr(hp, '@'); if (at) { *at = '\0'; at++; } client->appData.useRemoteCursor = TRUE; client->Bell = vnc_reflect_bell; client->GotXCutText = vnc_reflect_recv_cuttext; client->GotCopyRect = vnc_reflect_got_copyrect; client->GotCursorShape = vnc_reflect_got_cursorshape; client->MallocFrameBuffer = vnc_reflect_resize; client->canHandleNewFBSize = TRUE; client->GotFrameBufferUpdate = vnc_reflect_got_update; if (first) { argv[argc++] = "x11vnc_rawfb_vnc"; if (strstr(hp, "listen") == hp) { char *q = strchr(hp, ':'); argv[argc++] = strdup("-listen"); if (q) { client->listenPort = atoi(q+1); } else { client->listenPort = LISTEN_PORT_OFFSET; } } else { argv[argc++] = strdup(hp); } if (! rfbInitClient(client, &argc, argv)) { rfbLog("vnc_reflector failed for: %s\n", str0); clean_up_exit(1); } } if (at) { sprintf(str2, "map:/dev/null@%s", at); } else { unsigned long red_mask, green_mask, blue_mask; red_mask = (client->format.redMax << client->format.redShift); green_mask = (client->format.greenMax << client->format.greenShift); blue_mask = (client->format.blueMax << client->format.blueShift); sprintf(str2, "map:/dev/null@%dx%dx%d:0x%lx/0x%lx/0x%lx", client->width, client->height, client->format.bitsPerPixel, red_mask, green_mask, blue_mask); } *raw_fb_addr = (char *) client->frameBuffer; free(str0); if (first) { setup_cursors_and_push(); for (i=0; i<10; i++) { vnc_reflect_process_client(); } } first = 0; return strdup(str2); } rfbBool vnc_reflect_send_pointer(int x, int y, int mask) { int rc; if (mask >= 0) { got_user_input++; got_pointer_input++; last_pointer_time = time(NULL); } if (cursor_x != x || cursor_y != y) { last_pointer_motion_time = dnow(); } cursor_x = x; cursor_y = y; /* record the x, y position for the rfb screen as well. */ cursor_position(x, y); /* change the cursor shape if necessary */ rc = set_cursor(x, y, get_which_cursor()); cursor_changes += rc; return SendPointerEvent(client, x, y, mask); } rfbBool vnc_reflect_send_key(uint32_t key, rfbBool down) { return SendKeyEvent(client, key, down); } rfbBool vnc_reflect_send_cuttext(char *str, int len) { return SendClientCutText(client, str, len); } void vnc_reflect_process_client(void) { int num; if (client == NULL) { return; } num = WaitForMessage(client, 1000); if (num > 0) { if (!HandleRFBServerMessage(client)) { rfbLog("vnc_reflect_process_client: read failure to server\n"); shut_down = 1; } } } void linux_dev_fb_msg(char* q) { if (strstr(q, "/dev/fb") && strstr(UT.sysname, "Linux")) { rfbLog("\n"); rfbLog("On Linux you may need to load a kernel module to enable\n"); rfbLog("the framebuffer device /dev/fb*; e.g.:\n"); rfbLog(" vga=0x303 (and others) kernel boot parameter\n"); rfbLog(" modprobe uvesafb\n"); rfbLog(" modprobe radeonfb (card specific)\n"); rfbLog(" modprobe nvidiafb (card specific, others)\n"); rfbLog(" modprobe vesafb (?)\n"); rfbLog(" modprobe vga16fb\n"); rfbLog("\n"); rfbLog("You may also need root permission to open /dev/fb*\n"); rfbLog("and/or /dev/tty*.\n"); rfbLog("\n"); } } #define RAWFB_MMAP 1 #define RAWFB_FILE 2 #define RAWFB_SHM 3 XImage *initialize_raw_fb(int reset) { char *str, *q; int w, h, b, shmid = 0; unsigned long rm = 0, gm = 0, bm = 0, tm; static XImage ximage_struct; /* n.b.: not (XImage *) */ static XImage ximage_struct_snap; int closedpy = 1, i, m, db = 0; int do_macosx = 0; int do_reflect = 0; char *unlink_me = NULL; static char *last_file = NULL; static int last_mode = 0; if (reset && last_mode) { int fd; if (last_mode != RAWFB_MMAP && last_mode != RAWFB_FILE) { return NULL; } if (last_mode == RAWFB_MMAP) { munmap(raw_fb_addr, raw_fb_mmap); } if (raw_fb_fd >= 0) { close(raw_fb_fd); } raw_fb_fd = -1; if (db) fprintf(stderr, "initialize_raw_fb reset\n"); fd = -1; if (rawfb_dev_video) { fd = open(last_file, O_RDWR); } if (fd < 0) { fd = open(last_file, O_RDONLY); } if (fd < 0) { rfbLogEnable(1); rfbLog("failed to rawfb file: %s\n", last_file); rfbLogPerror("open"); clean_up_exit(1); } raw_fb_fd = fd; if (last_mode == RAWFB_MMAP) { raw_fb_addr = mmap(0, raw_fb_mmap, PROT_READ, MAP_SHARED, fd, 0); if (raw_fb_addr == MAP_FAILED || raw_fb_addr == NULL) { rfbLogEnable(1); rfbLog("failed to mmap file: %s\n", last_file); rfbLog(" raw_fb_addr: %p\n", raw_fb_addr); rfbLogPerror("mmap"); clean_up_exit(1); } } return NULL; } #ifdef MACOSX if (raw_fb_addr != NULL && macosx_console && raw_fb_addr == macosx_get_fb_addr()) { raw_fb_addr = NULL; } #endif if (raw_fb_addr || raw_fb_seek) { if (raw_fb_shm) { shmdt(raw_fb_addr); #if LIBVNCSERVER_HAVE_MMAP } else if (raw_fb_mmap) { munmap(raw_fb_addr, raw_fb_mmap); if (raw_fb_fd >= 0) { close(raw_fb_fd); } raw_fb_fd = -1; #endif } else if (raw_fb_seek) { if (raw_fb_fd >= 0) { close(raw_fb_fd); } raw_fb_fd = -1; } raw_fb_addr = NULL; raw_fb_mmap = 0; raw_fb_seek = 0; } if (! raw_fb_str) { return NULL; } /* testing aliases */ if (!strcasecmp(raw_fb_str, "NULL") || !strcasecmp(raw_fb_str, "ZERO") || !strcasecmp(raw_fb_str, "NONE")) { raw_fb_str = strdup("map:/dev/zero@640x480x32"); } else if (!strcasecmp(raw_fb_str, "NULLBIG") || !strcasecmp(raw_fb_str, "NONEBIG")) { raw_fb_str = strdup("map:/dev/zero@1024x768x32"); } if (!strcasecmp(raw_fb_str, "RAND")) { raw_fb_str = strdup("file:/dev/urandom@128x128x16"); } else if (!strcasecmp(raw_fb_str, "RANDBIG")) { raw_fb_str = strdup("file:/dev/urandom@640x480x16"); } else if (!strcasecmp(raw_fb_str, "RANDHUGE")) { raw_fb_str = strdup("file:/dev/urandom@1024x768x16"); } if (strstr(raw_fb_str, "solid=") == raw_fb_str) { char *n = raw_fb_str + strlen("solid="); char tmp[] = "/tmp/solid.XXXXXX"; char str[100]; unsigned int vals[1024], val; int x, y, fd, w = 1024, h = 768; if (strstr(n, "0x")) { if (sscanf(n, "0x%x", &val) != 1) { val = 0; } } if (val == 0) { val = get_pixel(n); } if (val == 0) { val = 0xFF00FF; } fd = mkstemp(tmp); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { vals[x] = val; } write(fd, (char *)vals, 4 * w); } close(fd); fd = open(tmp, O_WRONLY); unlink_me = strdup(tmp); sprintf(str, "map:%s@%dx%dx32", tmp, w, h); raw_fb_str = strdup(str); } else if (strstr(raw_fb_str, "swirl") == raw_fb_str) { char tmp[] = "/tmp/solid.XXXXXX"; char str[100]; unsigned int val[1024]; unsigned int c1, c2, c3, c4; int x, y, fd, w = 1024, h = 768; fd = mkstemp(tmp); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { c1 = 0; c2 = ((x+y)*128)/(w+h); c3 = (x*128)/w; c4 = (y*256)/h; val[x] = (c1 << 24) | (c2 << 16) | (c3 << 8) | (c4 << 0); } write(fd, (char *)val, 4 * w); } close(fd); fd = open(tmp, O_WRONLY); unlink_me = strdup(tmp); sprintf(str, "map:%s@%dx%dx32", tmp, w, h); raw_fb_str = strdup(str); } if ( (q = strstr(raw_fb_str, "setup:")) == raw_fb_str) { FILE *pipe; char line[1024], *t; set_child_info(); q += strlen("setup:"); /* rawfb-setup */ if (no_external_cmds || !cmd_ok("rawfb-setup")) { rfbLogEnable(1); rfbLog("cannot run external commands in -nocmds " "mode:\n"); rfbLog(" \"%s\"\n", q); rfbLog(" exiting.\n"); clean_up_exit(1); } rfbLog("running command to setup rawfb: %s\n", q); close_exec_fds(); pipe = popen(q, "r"); if (! pipe) { rfbLogEnable(1); rfbLog("popen of setup command failed.\n"); rfbLogPerror("popen"); clean_up_exit(1); } line[0] = '\0'; if (fgets(line, 1024, pipe) == NULL) { rfbLogEnable(1); rfbLog("read of setup command failed.\n"); clean_up_exit(1); } pclose(pipe); str = strdup(line); t = str; while (*t != '\0') { if (*t == '\n') { *t = '\0'; } t++; } rfbLog("setup command returned: %s\n", str); } else { str = strdup(raw_fb_str); } if (str[0] == '+') { char *t = strdup(str+1); free(str); str = t; closedpy = 0; if (! window) { window = rootwin; } } raw_fb_shm = 0; raw_fb_mmap = 0; raw_fb_seek = 0; raw_fb_fd = -1; raw_fb_addr = NULL; raw_fb_offset = 0; raw_fb_bytes_per_line = 0; rawfb_vnc_reflect = 0; last_mode = 0; if (last_file) { free(last_file); last_file = NULL; } if (strstr(str, "Video") == str) { if (pipeinput_str != NULL) { free(pipeinput_str); } pipeinput_str = strdup("VID"); initialize_pipeinput(); str[0] = 'v'; } if (strstr(str, "video") == str || strstr(str, "/dev/video") == str) { char *str2 = v4l_guess(str, &raw_fb_fd); if (str2 == NULL) { rfbLog("v4l_guess failed for: %s\n", str); clean_up_exit(1); } str = str2; rfbLog("v4l_guess returned: %s\n", str); rawfb_dev_video = 1; } else if (strstr(str, "dev/video")) { rawfb_dev_video = 1; } else if (strstr(str, "console") == str || strstr(str, "fb") == str || strstr(str, "/dev/fb") == str || strstr(str, "vt") == str) { char *str2 = console_guess(str, &raw_fb_fd); if (str2 == NULL) { rfbLog("console_guess failed for: %s\n", str); clean_up_exit(1); } str = str2; rfbLog("console_guess returned: %s\n", str); } else if (strstr(str, "vnc:") == str) { char *str2 = vnc_reflect_guess(str, &raw_fb_addr); rawfb_vnc_reflect = 1; do_reflect = 1; str = str2; rfbLog("vnc_reflector set rawfb str to: %s\n", str); if (pipeinput_str == NULL) { pipeinput_str = strdup("VNC"); } initialize_pipeinput(); } if (closedpy && !view_only && got_noviewonly) { rfbLog("not closing X DISPLAY under -noviewonly option.\n"); closedpy = 0; if (! window) { window = rootwin; } } if (! raw_fb_orig_dpy && dpy) { raw_fb_orig_dpy = strdup(DisplayString(dpy)); } #ifndef BOLDLY_CLOSE_DISPLAY #define BOLDLY_CLOSE_DISPLAY 1 #endif #if BOLDLY_CLOSE_DISPLAY if (closedpy) { if (dpy) { rfbLog("closing X DISPLAY: %s in rawfb mode.\n", DisplayString(dpy)); XCloseDisplay_wr(dpy); /* yow! */ } dpy = NULL; } #endif /* * -rawfb shm:163938442@640x480x32:ff/ff00/ff0000+3000 * -rawfb map:/path/to/file@640x480x32:ff/ff00/ff0000 * -rawfb file:/path/to/file@640x480x32:ff/ff00/ff0000 */ if (raw_fb_full_str) { free(raw_fb_full_str); } raw_fb_full_str = strdup(str); /* +O offset */ if ((q = strrchr(str, '+')) != NULL) { if (sscanf(q, "+%d", &raw_fb_offset) == 1) { *q = '\0'; } else { raw_fb_offset = 0; } } /* :R/G/B masks */ if ((q = strrchr(str, ':')) != NULL) { if (sscanf(q, ":%lx/%lx/%lx", &rm, &gm, &bm) == 3) { *q = '\0'; } else if (sscanf(q, ":0x%lx/0x%lx/0x%lx", &rm, &gm, &bm)== 3) { *q = '\0'; } else if (sscanf(q, ":%lu/%lu/%lu", &rm, &gm, &bm) == 3) { *q = '\0'; } else { rm = 0; gm = 0; bm = 0; } } if ((q = strrchr(str, '@')) == NULL) { rfbLogEnable(1); rfbLog("invalid rawfb str: %s\n", str); clean_up_exit(1); } if (strrchr(q, '-')) { char *q2 = strrchr(q, '-'); raw_fb_bytes_per_line = atoi(q2+1); *q2 = '\0'; } /* @WxHxB */ if (sscanf(q, "@%dx%dx%d", &w, &h, &b) != 3) { rfbLogEnable(1); rfbLog("invalid rawfb str: %s\n", str); clean_up_exit(1); } *q = '\0'; if (rm == 0 && gm == 0 && bm == 0) { /* guess masks... */ if (b == 24 || b == 32) { rm = 0xff0000; gm = 0x00ff00; bm = 0x0000ff; } else if (b == 16) { rm = 0xf800; gm = 0x07e0; bm = 0x001f; } else if (b == 8) { rm = 0x07; gm = 0x38; bm = 0xc0; } } /* we can fake -flipbyteorder to some degree... */ if (flip_byte_order) { if (b == 24 || b == 32) { tm = rm; rm = bm; bm = tm; } else if (b == 16) { unsigned short s1, s2; s1 = (unsigned short) rm; s2 = ((0xff & s1) << 8) | ((0xff00 & s1) >> 8); rm = (unsigned long) s2; s1 = (unsigned short) gm; s2 = ((0xff & s1) << 8) | ((0xff00 & s1) >> 8); gm = (unsigned long) s2; s1 = (unsigned short) bm; s2 = ((0xff & s1) << 8) | ((0xff00 & s1) >> 8); bm = (unsigned long) s2; } } /* native fb stuff for bpp < 8 only */ raw_fb_native_bpp = b; raw_fb_native_red_mask = rm; raw_fb_native_green_mask = gm; raw_fb_native_blue_mask = bm; raw_fb_native_red_shift = 100; raw_fb_native_green_shift = 100; raw_fb_native_blue_shift = 100; raw_fb_native_red_max = 1; raw_fb_native_green_max = 1; raw_fb_native_blue_max = 1; m = 1; for (i=0; i<32; i++) { if (raw_fb_native_red_mask & m) { if (raw_fb_native_red_shift == 100) { raw_fb_native_red_shift = i; } raw_fb_native_red_max *= 2; } if (raw_fb_native_green_mask & m) { if (raw_fb_native_green_shift == 100) { raw_fb_native_green_shift = i; } raw_fb_native_green_max *= 2; } if (raw_fb_native_blue_mask & m) { if (raw_fb_native_blue_shift == 100) { raw_fb_native_blue_shift = i; } raw_fb_native_blue_max *= 2; } m = m << 1; } raw_fb_native_red_max -= 1; raw_fb_native_green_max -= 1; raw_fb_native_blue_max -= 1; if (b < 8) { /* e.g. VGA16 */ rfbLog("raw_fb_native_bpp: %d 0x%02lx 0x%02lx 0x%02lx %d/%d/%d %d/%d/%d\n", raw_fb_native_bpp, raw_fb_native_red_mask, raw_fb_native_green_mask, raw_fb_native_blue_mask, raw_fb_native_red_max, raw_fb_native_green_max, raw_fb_native_blue_max, raw_fb_native_red_shift, raw_fb_native_green_shift, raw_fb_native_blue_shift); raw_fb_expand_bytes = 1; b = 8; rm = 0x07; gm = 0x38; bm = 0xc0; } /* end of stuff for bpp < 8 */ dpy_x = wdpy_x = w; dpy_y = wdpy_y = h; off_x = 0; off_y = 0; if (rawfb_dev_video) { if (b == 24) { rfbLog("enabling -24to32 for 24bpp video\n"); xform24to32 = 1; } else { if (xform24to32) { rfbLog("disabling -24to32 for 24bpp video\n"); } xform24to32 = 0; } } if (xform24to32) { if (b != 24) { rfbLog("warning: -24to32 mode and bpp=%d\n", b); } b = 32; } if (strstr(str, "snap:") == str) { use_snapfb = 1; str[0] = 'f'; str[1] = 'i'; str[2] = 'l'; str[3] = 'e'; } if (strstr(str, "shm:") != str && strstr(str, "mmap:") != str && strstr(str, "map:") != str && strstr(str, "file:") != str) { /* hmmm, not following directions, see if map: applies */ struct stat sbuf; if (stat(str, &sbuf) == 0) { char *new; int len = strlen("map:") + strlen(str) + 1; rfbLog("no type prefix: %s\n", raw_fb_str); rfbLog(" but file exists, so assuming: map:%s\n", raw_fb_str); new = (char *) malloc(len); strcpy(new, "map:"); strcat(new, str); free(str); str = new; } } if (sscanf(str, "shm:%d", &shmid) == 1) { /* shm:N */ #if LIBVNCSERVER_HAVE_XSHM || LIBVNCSERVER_HAVE_SHMAT raw_fb_addr = (char *) shmat(shmid, 0, SHM_RDONLY); if (! raw_fb_addr) { rfbLogEnable(1); rfbLog("failed to attach to shm: %d, %s\n", shmid, str); rfbLogPerror("shmat"); clean_up_exit(1); } raw_fb_shm = 1; rfbLog("rawfb: shm: %d W: %d H: %d B: %d addr: %p\n", shmid, w, h, b, raw_fb_addr); last_mode = RAWFB_SHM; #else rfbLogEnable(1); rfbLog("x11vnc was compiled without shm support.\n"); rfbLogPerror("shmat"); clean_up_exit(1); #endif } else if (strstr(str, "map:") == str || strstr(str, "mmap:") == str || strstr(str, "file:") == str) { /* map:/path/... or file:/path */ int fd, do_mmap = 1, size; struct stat sbuf; if (*str == 'f') { do_mmap = 0; } q = strchr(str, ':'); q++; macosx_console = 0; if (strstr(q, "macosx:") == q) { /* mmap:macosx:/dev/null@... */ q += strlen("macosx:"); do_macosx = 1; do_mmap = 0; macosx_console = 1; } last_file = strdup(q); fd = raw_fb_fd; if (fd < 0 && rawfb_dev_video) { fd = open(q, O_RDWR); } if (fd < 0) { fd = open(q, O_RDONLY); } if (fd < 0) { rfbLogEnable(1); rfbLog("failed to open file: %s, %s\n", q, str); rfbLogPerror("open"); linux_dev_fb_msg(q); clean_up_exit(1); } raw_fb_fd = fd; if (raw_fb_native_bpp < 8) { size = w*h*raw_fb_native_bpp/8 + raw_fb_offset; } else if (xform24to32) { size = w*h*24/8 + raw_fb_offset; } else { size = w*h*b/8 + raw_fb_offset; } if (fstat(fd, &sbuf) == 0) { if (S_ISREG(sbuf.st_mode)) { if (0) size = sbuf.st_size; } else { rfbLog("raw fb is non-regular file: %s\n", q); } } if (do_macosx) { raw_fb_addr = macosx_get_fb_addr(); raw_fb_mmap = size; rfbLog("rawfb: macosx fb: %s\n", q); rfbLog(" w: %d h: %d b: %d addr: %p sz: %d\n", w, h, b, raw_fb_addr, size); last_mode = 0; } else if (do_reflect) { raw_fb_mmap = size; rfbLog("rawfb: vnc fb: %s\n", q); rfbLog(" w: %d h: %d b: %d addr: %p sz: %d\n", w, h, b, raw_fb_addr, size); last_mode = 0; } else if (do_mmap) { #if LIBVNCSERVER_HAVE_MMAP raw_fb_addr = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0); if (raw_fb_addr == MAP_FAILED || raw_fb_addr == NULL) { rfbLogEnable(1); rfbLog("failed to mmap file: %s, %s\n", q, str); rfbLog(" raw_fb_addr: %p\n", raw_fb_addr); rfbLogPerror("mmap"); raw_fb_addr = NULL; rfbLog("mmap(2) failed, trying slower lseek(2)\n"); raw_fb_seek = size; last_mode = RAWFB_FILE; } else { raw_fb_mmap = size; rfbLog("rawfb: mmap file: %s\n", q); rfbLog(" w: %d h: %d b: %d addr: %p sz: %d\n", w, h, b, raw_fb_addr, size); last_mode = RAWFB_MMAP; } #else rfbLog("mmap(2) not supported on system, using" " slower lseek(2)\n"); raw_fb_seek = size; last_mode = RAWFB_FILE; #endif } else { raw_fb_seek = size; last_mode = RAWFB_FILE; rfbLog("rawfb: seek file: %s\n", q); rfbLog(" W: %d H: %d B: %d sz: %d\n", w, h, b, size); } } else { rfbLogEnable(1); rfbLog("invalid rawfb str: %s\n", str); clean_up_exit(1); } if (unlink_me) { unlink(unlink_me); } if (! raw_fb_image) { raw_fb_image = &ximage_struct; } initialize_clipshift(); if (raw_fb_bytes_per_line == 0) { raw_fb_bytes_per_line = dpy_x*b/8; /* * Put cases here were we can determine that * raw_bytes_per_line != dpy_x*b/8 */ #ifdef MACOSX if (do_macosx) { raw_fb_bytes_per_line = macosxCG_CGDisplayBytesPerRow(); } #endif } raw_fb_image->bytes_per_line = dpy_x * b/8; raw_fb = (char *) malloc(dpy_y * dpy_x * b/8); raw_fb_image->data = raw_fb; raw_fb_image->format = ZPixmap; raw_fb_image->width = dpy_x; raw_fb_image->height = dpy_y; raw_fb_image->bits_per_pixel = b; raw_fb_image->bitmap_unit = -1; if (use_snapfb && (raw_fb_seek || raw_fb_mmap)) { int b_use = b; if (snap_fb) { free(snap_fb); } if (b_use == 32 && xform24to32) { /* * The actual framebuffer (e.g. mapped addr) and * snap fb must be the same bpp. E.g. both 24bpp. * Reading FROM snap to utility image will be * transformed 24->32 in copy_raw_fb_24_to_32. * * addr -> snap -> (scanline, fullscreen, ...) */ b_use = 24; raw_fb_bytes_per_line = dpy_x * b_use/8; } snap_fb = (char *) malloc(dpy_y * dpy_x * b_use/8); snap = &ximage_struct_snap; snap->data = snap_fb; snap->format = ZPixmap; snap->width = dpy_x; snap->height = dpy_y; snap->bits_per_pixel = b_use; snap->bytes_per_line = dpy_x * b_use/8; snap->bitmap_unit = -1; } raw_fb_image->red_mask = rm; raw_fb_image->green_mask = gm; raw_fb_image->blue_mask = bm; raw_fb_image->depth = 0; m = 1; for (i=0; i<32; i++) { if (rm & m) { raw_fb_image->depth++; } if (gm & m) { raw_fb_image->depth++; } if (bm & m) { raw_fb_image->depth++; } m = m << 1; } if (raw_fb_native_bpp < 8) { raw_fb_image->depth = raw_fb_expand_bytes * 8; } if (! raw_fb_image->depth) { raw_fb_image->depth = (b == 32) ? 24 : b; } depth = raw_fb_image->depth; if (raw_fb_image->depth == 15) { /* unresolved bug with RGB555... */ depth++; } if (clipshift || raw_fb_native_bpp < 8) { memset(raw_fb, 0xff, dpy_y * raw_fb_image->bytes_per_line); } else if (raw_fb_addr && ! xform24to32) { memcpy(raw_fb, raw_fb_addr + raw_fb_offset, dpy_y * raw_fb_image->bytes_per_line); } else { memset(raw_fb, 0xff, dpy_y * raw_fb_image->bytes_per_line); } if (verbose) { rfbLog("\n"); rfbLog("rawfb: raw_fb %p\n", raw_fb); rfbLog(" format %d\n", raw_fb_image->format); rfbLog(" width %d\n", raw_fb_image->width); rfbLog(" height %d\n", raw_fb_image->height); rfbLog(" bpp %d\n", raw_fb_image->bits_per_pixel); rfbLog(" depth %d\n", raw_fb_image->depth); rfbLog(" bpl %d\n", raw_fb_image->bytes_per_line); if (use_snapfb && snap_fb) { rfbLog(" snap_fb %p\n", snap_fb); } } free(str); return raw_fb_image; } static void initialize_clipshift(void) { clipshift = 0; cdpy_x = cdpy_y = coff_x = coff_y = 0; if (clip_str) { int w, h, x, y, bad = 0; if (parse_geom(clip_str, &w, &h, &x, &y, wdpy_x, wdpy_y)) { if (x < 0) { x = 0; } if (y < 0) { y = 0; } if (x + w > wdpy_x) { w = wdpy_x - x; } if (y + h > wdpy_y) { h = wdpy_y - y; } if (w <= 0 || h <= 0) { bad = 1; } } else { bad = 1; } if (bad) { rfbLog("skipping invalid -clip WxH+X+Y: %s\n", clip_str); } else { /* OK, change geom behind everyone's back... */ cdpy_x = w; cdpy_y = h; coff_x = x; coff_y = y; clipshift = 1; dpy_x = cdpy_x; dpy_y = cdpy_y; } } } static int wait_until_mapped(Window win) { #if NO_X11 if (!win) {} return 0; #else int ms = 50, waittime = 30; time_t start = time(NULL); XWindowAttributes attr; while (1) { if (! valid_window(win, NULL, 0)) { if (time(NULL) > start + waittime) { break; } usleep(ms * 1000); continue; } if (dpy && ! XGetWindowAttributes(dpy, win, &attr)) { return 0; } if (attr.map_state == IsViewable) { return 1; } usleep(ms * 1000); } return 0; #endif /* NO_X11 */ } /* * initialize a fb for the X display */ XImage *initialize_xdisplay_fb(void) { #if NO_X11 if (raw_fb_str) { return initialize_raw_fb(0); } return NULL; #else XImage *fb; char *vis_str = visual_str; int try = 0, subwin_tries = 3; XErrorHandler old_handler = NULL; int subwin_bs; if (raw_fb_str) { return initialize_raw_fb(0); } X_LOCK; if (subwin) { if (subwin_wait_mapped) { wait_until_mapped(subwin); } if (!valid_window((Window) subwin, NULL, 0)) { rfbLogEnable(1); rfbLog("invalid sub-window: 0x%lx\n", subwin); X_UNLOCK; clean_up_exit(1); } } if (overlay) { /* * ideally we'd like to not have to cook up the * visual variables but rather let it all come out * of XReadScreen(), however there is no way to get * a default visual out of it, so we pretend -visual * TrueColor:NN was supplied with NN usually 24. */ char str[32]; Window twin = subwin ? subwin : rootwin; XImage *xi; xi = xreadscreen(dpy, twin, 0, 0, 8, 8, False); sprintf(str, "TrueColor:%d", xi->depth); if (xi->depth != 24 && ! quiet) { rfbLog("warning: overlay image has depth %d " "instead of 24.\n", xi->depth); } XDestroyImage(xi); if (visual_str != NULL && ! quiet) { rfbLog("warning: replacing '-visual %s' by '%s' " "for use with -overlay\n", visual_str, str); } vis_str = strdup(str); } if (xform24to32) { if (DefaultDepth(dpy, scr) == 24) { vis_str = strdup("TrueColor:32"); rfbLog("initialize_xdisplay_fb: vis_str set to: %s\n", vis_str); visual_id = (VisualID) 0; visual_depth = 0; set_visual_str_to_something = 1; } } else if (DefaultDepth(dpy, scr) < 8) { /* check very low bpp case, e.g. mono or vga16 */ Screen *s = DefaultScreenOfDisplay(dpy); XImage *xi = XGetImage_wr(dpy, DefaultRootWindow(dpy), 0, 0, 2, 2, AllPlanes, ZPixmap); if (xi && xi->bits_per_pixel < 8) { int lowbpp = xi->bits_per_pixel; if (!vis_str) { char tmp[32]; sprintf(tmp, "0x%x:8", (int) s->root_visual->visualid); vis_str = strdup(tmp); rfbLog("initialize_xdisplay_fb: low bpp[%d], vis_str " "set to: %s\n", lowbpp, vis_str); } if (using_shm) { using_shm = 0; rfbLog("initialize_xdisplay_fb: low bpp[%d], " "disabling shm\n", lowbpp); } visual_id = (VisualID) 0; visual_depth = 0; set_visual_str_to_something = 1; } if (xi) { XDestroyImage(xi); } } if (vis_str != NULL) { set_visual(vis_str); if (vis_str != visual_str) { free(vis_str); } } if (0) fprintf(stderr, "vis_str %s\n", vis_str ? vis_str : "notset"); /* set up parameters for subwin or non-subwin cases: */ if (! subwin) { /* full screen */ window = rootwin; dpy_x = wdpy_x = DisplayWidth(dpy, scr); dpy_y = wdpy_y = DisplayHeight(dpy, scr); off_x = 0; off_y = 0; /* this may be overridden via visual_id below */ default_visual = DefaultVisual(dpy, scr); } else { /* single window */ XWindowAttributes attr; window = (Window) subwin; if (! XGetWindowAttributes(dpy, window, &attr)) { rfbLogEnable(1); rfbLog("invalid window: 0x%lx\n", window); X_UNLOCK; clean_up_exit(1); } dpy_x = wdpy_x = attr.width; dpy_y = wdpy_y = attr.height; subwin_bs = attr.backing_store; /* this may be overridden via visual_id below */ default_visual = attr.visual; X_UNLOCK; set_offset(); X_LOCK; } initialize_clipshift(); /* initialize depth to reasonable value, visual_id may override */ depth = DefaultDepth(dpy, scr); if (0) fprintf(stderr, "DefaultDepth: %d visial_id: %d\n", depth, (int) visual_id); if (visual_id) { int n; XVisualInfo vinfo_tmpl, *vinfo; /* * we are in here from -visual or -overlay options * visual_id and visual_depth were set in set_visual(). */ vinfo_tmpl.visualid = visual_id; vinfo = XGetVisualInfo(dpy, VisualIDMask, &vinfo_tmpl, &n); if (vinfo == NULL || n == 0) { rfbLogEnable(1); rfbLog("could not match visual_id: 0x%x\n", (int) visual_id); X_UNLOCK; clean_up_exit(1); } default_visual = vinfo->visual; depth = vinfo->depth; if (visual_depth) { /* force it from -visual MooColor:NN */ depth = visual_depth; } if (! quiet) { fprintf(stderr, " initialize_xdisplay_fb()\n"); fprintf(stderr, " Visual*: %p\n", (void *) vinfo->visual); fprintf(stderr, " visualid: 0x%x\n", (int) vinfo->visualid); fprintf(stderr, " screen: %d\n", vinfo->screen); fprintf(stderr, " depth: %d\n", vinfo->depth); fprintf(stderr, " class: %d\n", vinfo->class); fprintf(stderr, " red_mask: 0x%08lx %s\n", vinfo->red_mask, bitprint(vinfo->red_mask, 32)); fprintf(stderr, " green_mask: 0x%08lx %s\n", vinfo->green_mask, bitprint(vinfo->green_mask, 32)); fprintf(stderr, " blue_mask: 0x%08lx %s\n", vinfo->blue_mask, bitprint(vinfo->blue_mask, 32)); fprintf(stderr, " cmap_size: %d\n", vinfo->colormap_size); fprintf(stderr, " bits b/rgb: %d\n", vinfo->bits_per_rgb); fprintf(stderr, "\n"); } XFree_wr(vinfo); } if (! quiet) { rfbLog("Default visual ID: 0x%x\n", (int) XVisualIDFromVisual(default_visual)); } again: if (subwin) { int shift = 0; int subwin_x, subwin_y; int disp_x = DisplayWidth(dpy, scr); int disp_y = DisplayHeight(dpy, scr); Window twin; /* subwins can be a dicey if they are changing size... */ trapped_xerror = 0; old_handler = XSetErrorHandler(trap_xerror); XTranslateCoordinates(dpy, window, rootwin, 0, 0, &subwin_x, &subwin_y, &twin); if (subwin_x + wdpy_x > disp_x) { shift = 1; subwin_x = disp_x - wdpy_x - 3; } if (subwin_y + wdpy_y > disp_y) { shift = 1; subwin_y = disp_y - wdpy_y - 3; } if (subwin_x < 0) { shift = 1; subwin_x = 1; } if (subwin_y < 0) { shift = 1; subwin_y = 1; } if (shift) { XMoveWindow(dpy, window, subwin_x, subwin_y); } XMapRaised(dpy, window); XRaiseWindow(dpy, window); XFlush_wr(dpy); } try++; if (nofb) { /* * For -nofb we do not allocate the framebuffer, so we * can save a few MB of memory. */ fb = XCreateImage_wr(dpy, default_visual, depth, ZPixmap, 0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0); } else if (visual_id) { /* * we need to call XCreateImage to supply the visual */ fb = XCreateImage_wr(dpy, default_visual, depth, ZPixmap, 0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0); fb->data = (char *) malloc(fb->bytes_per_line * fb->height); } else { fb = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap); if (! quiet) { rfbLog("Read initial data from X display into" " framebuffer.\n"); } } if (subwin) { XSetErrorHandler(old_handler); if (trapped_xerror) { rfbLog("trapped GetImage at SUBWIN creation.\n"); if (try < subwin_tries) { usleep(250 * 1000); if (!get_window_size(window, &wdpy_x, &wdpy_y)) { rfbLogEnable(1); rfbLog("could not get size of subwin " "0x%lx\n", subwin); X_UNLOCK; clean_up_exit(1); } goto again; } } trapped_xerror = 0; } else if (! fb && try == 1) { /* try once more */ usleep(250 * 1000); goto again; } if (use_snapfb) { initialize_snap_fb(); } X_UNLOCK; if (fb->bits_per_pixel == 24 && ! quiet) { rfbLog("warning: 24 bpp may have poor performance.\n"); } return fb; #endif /* NO_X11 */ } void parse_scale_string(char *str, double *factor_x, double *factor_y, int *scaling, int *blend, int *nomult4, int *pad, int *interpolate, int *numer, int *denom, int w_in, int h_in) { int m, n; char *p, *tstr; double f, f2; *factor_x = 1.0; *factor_y = 1.0; *scaling = 0; *blend = 1; *nomult4 = 0; *pad = 0; *interpolate = 0; *numer = 0, *denom = 0; if (str == NULL || str[0] == '\0') { return; } tstr = strdup(str); if ( (p = strchr(tstr, ':')) != NULL) { /* options */ if (strstr(p+1, "nb") != NULL) { *blend = 0; } if (strstr(p+1, "fb") != NULL) { *blend = 2; } if (strstr(p+1, "n4") != NULL) { *nomult4 = 1; } if (strstr(p+1, "in") != NULL) { *interpolate = 1; } if (strstr(p+1, "pad") != NULL) { *pad = 1; } if (strstr(p+1, "nocr") != NULL) { /* global */ scaling_copyrect = 0; } else if (strstr(p+1, "cr") != NULL) { /* global */ scaling_copyrect = 1; } *p = '\0'; } if (strchr(tstr, '.') != NULL) { double test, diff, eps = 1.0e-7; if (sscanf(tstr, "%lfx%lf", &f, &f2) == 2) { *factor_x = (double) f; *factor_y = (double) f2; } else if (sscanf(tstr, "%lf", &f) != 1) { rfbLogEnable(1); rfbLog("invalid -scale arg: %s\n", tstr); clean_up_exit(1); } else { *factor_x = (double) f; *factor_y = (double) f; } /* look for common fractions from small ints: */ if (*factor_x == *factor_y) { for (n=2; n<=10; n++) { for (m=1; m 0 && h_in > 0) { *factor_x = ((double) m) / ((double) w_in); *factor_y = ((double) n) / ((double) h_in); } else { if (sscanf(tstr, "%d/%d", &m, &n) != 2) { if (sscanf(tstr, "%d", &m) != 1) { rfbLogEnable(1); rfbLog("invalid -scale arg: %s\n", tstr); clean_up_exit(1); } else { /* e.g. -scale 1 or -scale 2 */ n = 1; } } if (n <= 0 || m <=0) { rfbLogEnable(1); rfbLog("invalid -scale arg: %s\n", tstr); clean_up_exit(1); } *factor_x = ((double) m)/ n; *factor_y = ((double) m)/ n; if (*factor_x < 0.01) { rfbLogEnable(1); rfbLog("-scale factor too small: %f\n", *factor_x); clean_up_exit(1); } *numer = m; *denom = n; } if (*factor_x == 1.0 && *factor_y == 1.0) { if (! quiet) { rfbLog("scaling disabled for factor %f %f\n", *factor_x, *factor_y); } } else { *scaling = 1; } free(tstr); } int parse_rotate_string(char *str, int *mode) { int m = ROTATE_NONE; if (str == NULL || !strcmp(str, "") || !strcmp(str, "0")) { m = ROTATE_NONE; } else if (!strcmp(str, "x")) { m = ROTATE_X; } else if (!strcmp(str, "y")) { m = ROTATE_Y; } else if (!strcmp(str, "xy") || !strcmp(str, "yx") || !strcmp(str,"+180") || !strcmp(str,"-180") || !strcmp(str,"180")) { m = ROTATE_XY; } else if (!strcmp(str, "+90") || !strcmp(str, "90")) { m = ROTATE_90; } else if (!strcmp(str, "+90x") || !strcmp(str, "90x")) { m = ROTATE_90X; } else if (!strcmp(str, "+90y") || !strcmp(str, "90y")) { m = ROTATE_90Y; } else if (!strcmp(str, "-90") || !strcmp(str, "270") || !strcmp(str, "+270")) { m = ROTATE_270; } else { rfbLog("invalid -rotate mode: %s\n", str); } if (mode) { *mode = m; } return m; } int scale_round(int len, double fac) { double eps = 0.000001; len = (int) (len * fac + eps); if (len < 1) { len = 1; } return len; } static void setup_scaling(int *width_in, int *height_in) { int width = *width_in; int height = *height_in; parse_scale_string(scale_str, &scale_fac_x, &scale_fac_y, &scaling, &scaling_blend, &scaling_nomult4, &scaling_pad, &scaling_interpolate, &scale_numer, &scale_denom, *width_in, *height_in); if (scaling) { width = scale_round(width, scale_fac_x); height = scale_round(height, scale_fac_y); if (scale_denom && scaling_pad) { /* it is not clear this padding is useful anymore */ rfbLog("width %% denom: %d %% %d = %d\n", width, scale_denom, width % scale_denom); rfbLog("height %% denom: %d %% %d = %d\n", height, scale_denom, height % scale_denom); if (width % scale_denom != 0) { int w = width; w += scale_denom - (w % scale_denom); if (!scaling_nomult4 && w % 4 != 0) { /* need to make mult of 4 as well */ int c = 0; while (w % 4 != 0 && c++ <= 5) { w += scale_denom; } } width = w; rfbLog("padded width to: %d (mult of %d%s\n", width, scale_denom, !scaling_nomult4 ? " and 4)" : ")"); } if (height % scale_denom != 0) { height += scale_denom - (height % scale_denom); rfbLog("padded height to: %d (mult of %d)\n", height, scale_denom); } } if (!scaling_nomult4 && width % 4 != 0 && width > 2) { /* reset width to be multiple of 4 */ int width0 = width; if ((width+1) % 4 == 0) { width = width+1; } else if ((width-1) % 4 == 0) { width = width-1; } else if ((width+2) % 4 == 0) { width = width+2; } rfbLog("reset scaled width %d -> %d to be a multiple of" " 4 (to\n", width0, width); rfbLog("make vncviewers happy). use -scale m/n:n4 to " "disable.\n"); } scaled_x = width; scaled_y = height; *width_in = width; *height_in = height; } } static void setup_rotating(void) { char *rs = rotating_str; rotating_cursors = 1; if (rs && strstr(rs, "nc:") == rs) { rs += strlen("nc:"); rotating_cursors = 0; } rotating = parse_rotate_string(rs, NULL); if (! rotating) { rotating_cursors = 0; } if (rotating == ROTATE_90 || rotating == ROTATE_90X || rotating == ROTATE_90Y || rotating == ROTATE_270) { rotating_same = 0; } else { rotating_same = 1; } } static rfbBool set_xlate_wrapper(rfbClientPtr cl) { static int first = 1; if (first) { first = 0; } else if (ncache) { int save = ncache_xrootpmap; rfbLog("set_xlate_wrapper: clearing -ncache for new pixel format.\n"); ncache_xrootpmap = 0; check_ncache(1, 0); ncache_xrootpmap = save; } return rfbSetTranslateFunction(cl); } /* * initialize the rfb framebuffer/screen */ void initialize_screen(int *argc, char **argv, XImage *fb) { int have_masks = 0; int width = fb->width; int height = fb->height; int create_screen = screen ? 0 : 1; int bits_per_color; int fb_bpp, fb_Bpl, fb_depth; bpp = fb->bits_per_pixel; fb_bpp = (int) fb->bits_per_pixel; fb_Bpl = (int) fb->bytes_per_line; fb_depth = (int) fb->depth; rfbLog("initialize_screen: fb_depth/fb_bpp/fb_Bpl %d/%d/%d\n", fb_depth, fb_bpp, fb_Bpl); main_bytes_per_line = fb->bytes_per_line; if (cmap8to24) { if (raw_fb) { if (!quiet) rfbLog("disabling -8to24 mode" " in raw_fb mode.\n"); cmap8to24 = 0; } else if (depth == 24) { if (fb_bpp != 32) { if (!quiet) rfbLog("disabling -8to24 mode:" " bpp != 32 with depth == 24\n"); cmap8to24 = 0; } } else if (depth <= 16) { /* need to cook up the screen fb to be depth 24 */ fb_bpp = 32; fb_depth = 24; } else { if (!quiet) rfbLog("disabling -8to24 mode:" " default depth not 16 or less\n"); cmap8to24 = 0; } } setup_scaling(&width, &height); if (scaling) { rfbLog("scaling screen: %dx%d -> %dx%d\n", fb->width, fb->height, scaled_x, scaled_y); rfbLog("scaling screen: scale_fac_x=%.5f scale_fac_y=%.5f\n", scale_fac_x, scale_fac_y); rfb_bytes_per_line = (main_bytes_per_line / fb->width) * width; } else { rfb_bytes_per_line = main_bytes_per_line; } setup_rotating(); if (rotating) { if (! rotating_same) { int t, b = main_bytes_per_line / fb->width; if (scaling) { rot_bytes_per_line = b * height; } else { rot_bytes_per_line = b * fb->height; } t = width; width = height; /* The big swap... */ height = t; } else { rot_bytes_per_line = rfb_bytes_per_line; } } #ifndef NO_NCACHE if (ncache > 0 && !nofb) { # ifdef MACOSX if (! raw_fb_str || macosx_console) { # else if (! raw_fb_str) { # endif char *new_fb; int sz = fb->height * fb->bytes_per_line; int ns = 1+ncache; if (ncache_xrootpmap) { ns++; } new_fb = (char *) calloc((size_t) (sz * ns), 1); if (fb->data) { memcpy(new_fb, fb->data, sz); free(fb->data); } fb->data = new_fb; fb->height *= (ns); height *= (ns); ncache0 = ncache; } } #endif if (cmap8to24) { if (depth <= 8) { rfb_bytes_per_line *= 4; rot_bytes_per_line *= 4; } else if (depth <= 16) { rfb_bytes_per_line *= 2; rot_bytes_per_line *= 2; } } /* * These are just hints wrt pixel format just to let * rfbGetScreen/rfbNewFramebuffer proceed with reasonable * defaults. We manually set them in painful detail below. */ bits_per_color = guess_bits_per_color(fb_bpp); /* n.b. samplesPerPixel (set = 1 here) seems to be unused. */ if (create_screen) { if (use_openssl) { openssl_init(0); } else if (use_stunnel) { setup_stunnel(0, argc, argv); } screen = rfbGetScreen(argc, argv, width, height, bits_per_color, 1, fb_bpp/8); if (screen && http_dir) { http_connections(1); } } else { /* set set frameBuffer member below. */ rfbLog("rfbNewFramebuffer(0x%x, 0x%x, %d, %d, %d, %d, %d)\n", screen, NULL, width, height, bits_per_color, 1, fb_bpp/8); /* these are probably overwritten, but just to be safe: */ screen->bitsPerPixel = fb_bpp; screen->depth = fb_depth; rfbNewFramebuffer(screen, NULL, width, height, bits_per_color, 1, (int) fb_bpp/8); } if (! screen) { int i; rfbLogEnable(1); rfbLog("\n"); rfbLog("failed to create rfb screen.\n"); for (i=0; i< *argc; i++) { rfbLog("\t[%d] %s\n", i, argv[i]); } clean_up_exit(1); } if (create_screen && *argc != 1) { int i; rfbLogEnable(1); rfbLog("*** unrecognized option(s) ***\n"); for (i=1; i< *argc; i++) { rfbLog("\t[%d] %s\n", i, argv[i]); } rfbLog("For a list of options run: x11vnc -opts\n"); rfbLog("or for the full help: x11vnc -help\n"); rfbLog("\n"); rfbLog("Here is a list of removed or obsolete" " options:\n"); rfbLog("\n"); rfbLog("removed: -hints, -nohints\n"); rfbLog("removed: -cursorposall\n"); rfbLog("removed: -nofilexfer, now the default.\n"); rfbLog("\n"); rfbLog("renamed: -old_copytile, use -onetile\n"); rfbLog("renamed: -mouse, use -cursor\n"); rfbLog("renamed: -mouseX, use -cursor X\n"); rfbLog("renamed: -X, use -cursor X\n"); rfbLog("renamed: -nomouse, use -nocursor\n"); rfbLog("renamed: -old_pointer, use -pointer_mode 1\n"); clean_up_exit(1); } /* set up format from scratch: */ if (rotating && ! rotating_same) { screen->paddedWidthInBytes = rot_bytes_per_line; } else { screen->paddedWidthInBytes = rfb_bytes_per_line; } screen->serverFormat.bitsPerPixel = fb_bpp; screen->serverFormat.depth = fb_depth; screen->serverFormat.trueColour = TRUE; screen->serverFormat.redShift = 0; screen->serverFormat.greenShift = 0; screen->serverFormat.blueShift = 0; screen->serverFormat.redMax = 0; screen->serverFormat.greenMax = 0; screen->serverFormat.blueMax = 0; /* these main_* formats are used generally. */ main_red_shift = 0; main_green_shift = 0; main_blue_shift = 0; main_red_max = 0; main_green_max = 0; main_blue_max = 0; main_red_mask = fb->red_mask; main_green_mask = fb->green_mask; main_blue_mask = fb->blue_mask; have_masks = ((fb->red_mask|fb->green_mask|fb->blue_mask) != 0); if (force_indexed_color) { have_masks = 0; } if (cmap8to24 && depth <= 16 && dpy) { XVisualInfo vinfo; vinfo.red_mask = 0; vinfo.green_mask = 0; vinfo.blue_mask = 0; /* more cooking up... */ have_masks = 2; /* need to fetch TrueColor visual */ X_LOCK; if (dpy #if !NO_X11 && XMatchVisualInfo(dpy, scr, 24, TrueColor, &vinfo) #endif ) { main_red_mask = vinfo.red_mask; main_green_mask = vinfo.green_mask; main_blue_mask = vinfo.blue_mask; } else if (fb->byte_order == LSBFirst) { main_red_mask = 0x00ff0000; main_green_mask = 0x0000ff00; main_blue_mask = 0x000000ff; } else { main_red_mask = 0x000000ff; main_green_mask = 0x0000ff00; main_blue_mask = 0x00ff0000; } X_UNLOCK; } if (raw_fb_str && raw_fb_pixfmt) { char *fmt = strdup(raw_fb_pixfmt); uppercase(fmt); if (strstr(fmt, "GREY")) { set_greyscale_colormap(); } else if (strstr(fmt, "HI240")) { set_hi240_colormap(); } else { unset_colormap(); } free(fmt); } if (! have_masks && screen->serverFormat.bitsPerPixel <= 16 && dpy && CellsOfScreen(ScreenOfDisplay(dpy, scr))) { /* indexed color. we let 1 <= bpp <= 16, but it is normally 8 */ if (!quiet) { rfbLog("\n"); rfbLog("X display %s is %dbpp indexed color, depth=%d\n", DisplayString(dpy), screen->serverFormat.bitsPerPixel, screen->serverFormat.depth); if (! flash_cmap && ! overlay) { rfbLog("\n"); rfbLog("In 8bpp PseudoColor mode if you " "experience color\n"); rfbLog("problems you may want to enable " "following the\n"); rfbLog("changing colormap by using the " "-flashcmap option.\n"); rfbLog("\n"); } } if (screen->serverFormat.depth < 8) { rfbLog("resetting serverFormat.depth %d -> 8.\n", screen->serverFormat.depth); rfbLog("resetting serverFormat.bpp %d -> 8.\n", screen->serverFormat.bitsPerPixel); screen->serverFormat.depth = 8; screen->serverFormat.bitsPerPixel = 8; } if (screen->serverFormat.depth > 8) { rfbLog("WARNING: indexed color depth > 8, Tight encoding will crash.\n"); } screen->serverFormat.trueColour = FALSE; indexed_color = 1; set_colormap(1); debug_colormap(fb); } else { /* * general case, we call it truecolor, but could be direct * color, static color, etc.... */ if (! quiet) { if (raw_fb) { rfbLog("\n"); rfbLog("Raw fb at addr %p is %dbpp depth=%d " "true color\n", raw_fb_addr, fb_bpp, fb_depth); } else if (! dpy) { ; } else if (have_masks == 2) { rfbLog("\n"); rfbLog("X display %s is %dbpp depth=%d indexed " "color (-8to24 mode)\n", DisplayString(dpy), fb->bits_per_pixel, fb->depth); } else { rfbLog("\n"); rfbLog("X display %s is %dbpp depth=%d true " "color\n", DisplayString(dpy), fb_bpp, fb_depth); } } indexed_color = 0; if (!have_masks) { int M, B = bits_per_color; if (3*B > fb_bpp) B--; if (B < 1) B = 1; M = (1 << B) - 1; rfbLog("WARNING: all TrueColor RGB masks are zero!\n"); rfbLog("WARNING: cooking something up for VNC fb... B=%d M=%d\n", B, M); main_red_mask = M; main_green_mask = M; main_blue_mask = M; main_red_mask = main_red_mask << (2*B); main_green_mask = main_green_mask << (1*B); main_blue_mask = main_blue_mask << (0*B); fprintf(stderr, " red_mask: 0x%08lx %s\n", main_red_mask, bitprint(main_red_mask, 32)); fprintf(stderr, " green_mask: 0x%08lx %s\n", main_green_mask, bitprint(main_green_mask, 32)); fprintf(stderr, " blue_mask: 0x%08lx %s\n", main_blue_mask, bitprint(main_blue_mask, 32)); } /* convert masks to bit shifts and max # colors */ if (main_red_mask) { while (! (main_red_mask & (1 << screen->serverFormat.redShift))) { screen->serverFormat.redShift++; } } if (main_green_mask) { while (! (main_green_mask & (1 << screen->serverFormat.greenShift))) { screen->serverFormat.greenShift++; } } if (main_blue_mask) { while (! (main_blue_mask & (1 << screen->serverFormat.blueShift))) { screen->serverFormat.blueShift++; } } screen->serverFormat.redMax = main_red_mask >> screen->serverFormat.redShift; screen->serverFormat.greenMax = main_green_mask >> screen->serverFormat.greenShift; screen->serverFormat.blueMax = main_blue_mask >> screen->serverFormat.blueShift; main_red_max = screen->serverFormat.redMax; main_green_max = screen->serverFormat.greenMax; main_blue_max = screen->serverFormat.blueMax; main_red_shift = screen->serverFormat.redShift; main_green_shift = screen->serverFormat.greenShift; main_blue_shift = screen->serverFormat.blueShift; } #if !SMALL_FOOTPRINT if (verbose) { fprintf(stderr, "\n"); fprintf(stderr, "FrameBuffer Info:\n"); fprintf(stderr, " width: %d\n", fb->width); fprintf(stderr, " height: %d\n", fb->height); fprintf(stderr, " scaled_width: %d\n", width); fprintf(stderr, " scaled_height: %d\n", height); fprintf(stderr, " indexed_color: %d\n", indexed_color); fprintf(stderr, " bits_per_pixel: %d\n", fb->bits_per_pixel); fprintf(stderr, " depth: %d\n", fb->depth); fprintf(stderr, " red_mask: 0x%08lx %s\n", fb->red_mask, bitprint(fb->red_mask, 32)); fprintf(stderr, " green_mask: 0x%08lx %s\n", fb->green_mask, bitprint(fb->green_mask, 32)); fprintf(stderr, " blue_mask: 0x%08lx %s\n", fb->blue_mask, bitprint(fb->blue_mask, 32)); if (cmap8to24) { fprintf(stderr, " 8to24 info:\n"); fprintf(stderr, " fb_bpp: %d\n", fb_bpp); fprintf(stderr, " fb_depth: %d\n", fb_depth); fprintf(stderr, " red_mask: 0x%08lx %s\n", main_red_mask, bitprint(main_red_mask, 32)); fprintf(stderr, " green_mask: 0x%08lx %s\n", main_green_mask, bitprint(main_green_mask, 32)); fprintf(stderr, " blue_mask: 0x%08lx %s\n", main_blue_mask, bitprint(main_blue_mask, 32)); } fprintf(stderr, " red: max: %3d shift: %2d\n", main_red_max, main_red_shift); fprintf(stderr, " green: max: %3d shift: %2d\n", main_green_max, main_green_shift); fprintf(stderr, " blue: max: %3d shift: %2d\n", main_blue_max, main_blue_shift); fprintf(stderr, " mainfb_bytes_per_line: %d\n", main_bytes_per_line); fprintf(stderr, " rfb_fb_bytes_per_line: %d\n", rfb_bytes_per_line); fprintf(stderr, " rot_fb_bytes_per_line: %d\n", rot_bytes_per_line); fprintf(stderr, " raw_fb_bytes_per_line: %d\n", raw_fb_bytes_per_line); switch(fb->format) { case XYBitmap: fprintf(stderr, " format: XYBitmap\n"); break; case XYPixmap: fprintf(stderr, " format: XYPixmap\n"); break; case ZPixmap: fprintf(stderr, " format: ZPixmap\n"); break; default: fprintf(stderr, " format: %d\n", fb->format); break; } switch(fb->byte_order) { case LSBFirst: fprintf(stderr, " byte_order: LSBFirst\n"); break; case MSBFirst: fprintf(stderr, " byte_order: MSBFirst\n"); break; default: fprintf(stderr, " byte_order: %d\n", fb->byte_order); break; } fprintf(stderr, " bitmap_pad: %d\n", fb->bitmap_pad); fprintf(stderr, " bitmap_unit: %d\n", fb->bitmap_unit); switch(fb->bitmap_bit_order) { case LSBFirst: fprintf(stderr, " bitmap_bit_order: LSBFirst\n"); break; case MSBFirst: fprintf(stderr, " bitmap_bit_order: MSBFirst\n"); break; default: fprintf(stderr, " bitmap_bit_order: %d\n", fb->bitmap_bit_order); break; } } if (overlay && ! quiet) { rfbLog("\n"); rfbLog("Overlay mode enabled: If you experience color\n"); rfbLog("problems when popup menus are on the screen, try\n"); rfbLog("disabling SaveUnders in your X server, one way is\n"); rfbLog("to start the X server with the '-su' option, e.g.:\n"); rfbLog("Xsun -su ... see Xserver(1), xinit(1) for more info.\n"); rfbLog("\n"); } #endif /* nofb is for pointer/keyboard only handling. */ if (nofb) { main_fb = NULL; rfb_fb = main_fb; cmap8to24_fb = NULL; rot_fb = NULL; screen->displayHook = nofb_hook; } else { main_fb = fb->data; rfb_fb = NULL; cmap8to24_fb = NULL; rot_fb = NULL; if (cmap8to24) { int n = main_bytes_per_line * fb->height; if (depth <= 8) { n *= 4; } else if (depth <= 16) { n *= 2; } cmap8to24_fb = (char *) malloc(n); memset(cmap8to24_fb, 0, n); } if (rotating) { int n = rot_bytes_per_line * height; rot_fb = (char *) malloc(n); memset(rot_fb, 0, n); } if (scaling) { int n = rfb_bytes_per_line * height; if (rotating && ! rotating_same) { n = rot_bytes_per_line * height; } rfb_fb = (char *) malloc(n); memset(rfb_fb, 0, n); } else if (cmap8to24) { rfb_fb = cmap8to24_fb; } else { rfb_fb = main_fb; } } if (rot_fb) { screen->frameBuffer = rot_fb; } else { screen->frameBuffer = rfb_fb; } if (verbose) { fprintf(stderr, " rfb_fb: %p\n", rfb_fb); fprintf(stderr, " main_fb: %p\n", main_fb); fprintf(stderr, " 8to24_fb: %p\n", cmap8to24_fb); fprintf(stderr, " rot_fb: %p\n", rot_fb); fprintf(stderr, " snap_fb: %p\n", snap_fb); fprintf(stderr, " raw_fb: %p\n", raw_fb); fprintf(stderr, " fake_fb: %p\n", fake_fb); fprintf(stderr, "\n"); } /* may need, bpp, main_red_max, etc. */ parse_wireframe(); parse_scroll_copyrect(); setup_cursors_and_push(); if (scaling || rotating || cmap8to24) { mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); } if (! create_screen) { rfbClientIteratorPtr iter; rfbClientPtr cl; /* * since bits_per_color above may have been approximate, * try to reset the individual translation tables... * we do not seem to need this with rfbGetScreen()... */ if (!quiet) rfbLog("calling setTranslateFunction()...\n"); iter = rfbGetClientIterator(screen); while ((cl = rfbClientIteratorNext(iter)) != NULL) { screen->setTranslateFunction(cl); } rfbReleaseClientIterator(iter); if (!quiet) rfbLog(" done.\n"); do_copy_screen = 1; /* done for framebuffer change case */ return; } /* * the rest is screen server initialization, etc, only needed * at screen creation time. */ /* event callbacks: */ screen->newClientHook = new_client; screen->kbdAddEvent = keyboard; screen->ptrAddEvent = pointer; screen->setXCutText = xcut_receive; screen->setTranslateFunction = set_xlate_wrapper; screen->kbdReleaseAllKeys = kbd_release_all_keys; screen->setSingleWindow = set_single_window; screen->setServerInput = set_server_input; screen->setTextChat = set_text_chat; screen->getFileTransferPermission = get_file_transfer_permitted; /* called from inetd, we need to treat stdio as our socket */ if (inetd && use_openssl) { /* accept_openssl() called later */ screen->port = 0; } else if (inetd) { int fd = dup(0); if (fd < 0) { rfbLogEnable(1); rfbErr("dup(0) = %d failed.\n", fd); rfbLogPerror("dup"); clean_up_exit(1); } fclose(stdin); fclose(stdout); /* we keep stderr for logging */ screen->inetdSock = fd; screen->port = 0; } else if (! got_rfbport && auto_port > 0) { int lport = find_free_port(auto_port, auto_port+200); screen->autoPort = FALSE; screen->port = lport; } else if (! got_rfbport) { screen->autoPort = TRUE; } else if (got_rfbport && got_rfbport_val == 0) { screen->autoPort = FALSE; screen->port = 0; } if (! got_nevershared && ! got_alwaysshared) { if (shared) { screen->alwaysShared = TRUE; } else { screen->neverShared = TRUE; } screen->dontDisconnect = TRUE; } if (! got_deferupdate) { screen->deferUpdateTime = defer_update; } else { defer_update = screen->deferUpdateTime; } rfbInitServer(screen); if (use_openssl) { openssl_port(); if (https_port_num >= 0) { https_port(); } } install_passwds(); } #define DO_AVAHI \ if (avahi) { \ avahi_initialise(); \ avahi_advertise(vnc_desktop_name, host, lport); \ usleep(1000*1000); \ } void announce(int lport, int ssl, char *iface) { char *host = this_host(); char *tvdt; if (! ssl) { tvdt = "The VNC desktop is: "; } else { if (enc_str && !strcmp(enc_str, "none")) { tvdt = "The VNC desktop is: "; } else if (enc_str) { tvdt = "The ENC VNC desktop is: "; } else { tvdt = "The SSL VNC desktop is: "; } } if (iface != NULL && *iface != '\0' && strcmp(iface, "any")) { host = iface; } if (host != NULL) { /* note that vncviewer special cases 5900-5999 */ int sz = 256; if (inetd) { ; /* should not occur (port) */ } else if (quiet) { if (lport >= 5900) { snprintf(vnc_desktop_name, sz, "%s:%d", host, lport - 5900); DO_AVAHI fprintf(stderr, "\n%s %s\n", tvdt, vnc_desktop_name); } else { snprintf(vnc_desktop_name, sz, "%s:%d", host, lport); DO_AVAHI fprintf(stderr, "\n%s %s\n", tvdt, vnc_desktop_name); } } else if (lport >= 5900) { snprintf(vnc_desktop_name, sz, "%s:%d", host, lport - 5900); DO_AVAHI fprintf(stderr, "\n%s %s\n", tvdt, vnc_desktop_name); if (lport >= 6000) { rfbLog("possible aliases: %s:%d, " "%s::%d\n", host, lport, host, lport); } } else { snprintf(vnc_desktop_name, sz, "%s:%d", host, lport); DO_AVAHI fprintf(stderr, "\n%s %s\n", tvdt, vnc_desktop_name); rfbLog("possible alias: %s::%d\n", host, lport); } } } static void announce_http(int lport, int ssl, char *iface) { char *host = this_host(); char *jvu; if (enc_str && !strcmp(enc_str, "none")) { jvu = "Java viewer URL: http"; } else if (ssl == 1) { jvu = "Java SSL viewer URL: https"; } else if (ssl == 2) { jvu = "Java SSL viewer URL: http"; } else { jvu = "Java viewer URL: http"; } if (iface != NULL && *iface != '\0' && strcmp(iface, "any")) { host = iface; } if (host != NULL) { if (! inetd) { fprintf(stderr, "%s://%s:%d/\n", jvu, host, lport); if (screen && enc_str && !strcmp(enc_str, "none")) { fprintf(stderr, "%s://%s:%d/\n", jvu, host, screen->port); } } } } void set_vnc_desktop_name(void) { sprintf(vnc_desktop_name, "unknown"); if (inetd) { sprintf(vnc_desktop_name, "%s/inetd-no-further-clients", this_host()); } if (screen->port) { if (! quiet) { if (screen->httpListenSock > -1 && screen->httpPort) { rfbLog("\n"); rfbLog("The URLs printed out below ('Java ... viewer URL') can\n"); rfbLog("be used for Java enabled Web browser connections.\n"); if (enc_str && !strcmp(enc_str, "none")) { ; } else if (use_openssl || stunnel_port) { rfbLog("Here are some additional possibilities:\n"); rfbLog("\n"); rfbLog("https://host:port/proxy.vnc (MUST be used if Web Proxy used)\n"); rfbLog("\n"); rfbLog("https://host:port/ultra.vnc (Use UltraVNC Java Viewer)\n"); rfbLog("https://host:port/ultraproxy.vnc (Web Proxy with UltraVNC)\n"); rfbLog("https://host:port/ultrasigned.vnc (Signed UltraVNC Filexfer)\n"); rfbLog("\n"); rfbLog("Where you replace \"host:port\" with that printed below, or\n"); rfbLog("whatever is needed to reach the host e.g. Internet IP number\n"); } } rfbLog("\n"); } if (use_openssl) { announce(screen->port, 1, listen_str); } else { announce(screen->port, 0, listen_str); } if (stunnel_port) { announce(stunnel_port, 1, NULL); } if (screen->httpListenSock > -1 && screen->httpPort) { if (use_openssl) { if (enc_str && !strcmp(enc_str, "none")) { ; } else { announce_http(screen->port, 1, listen_str); } if (https_port_num >= 0) { announce_http(https_port_num, 1, listen_str); } announce_http(screen->httpPort, 2, listen_str); } else if (use_stunnel) { announce_http(screen->httpPort, 2, listen_str); } else { announce_http(screen->httpPort, 0, listen_str); } } fflush(stderr); if (inetd) { ; /* should not occur (port != 0) */ } else { fprintf(stdout, "PORT=%d\n", screen->port); if (stunnel_port) { fprintf(stdout, "SSLPORT=%d\n", stunnel_port); } else if (use_openssl) { if (enc_str && !strcmp(enc_str, "none")) { ; } else if (enc_str) { fprintf(stdout, "ENCPORT=%d\n", screen->port); } else { fprintf(stdout, "SSLPORT=%d\n", screen->port); } } fflush(stdout); if (flagfile) { FILE *flag = fopen(flagfile, "w"); if (flag) { fprintf(flag, "PORT=%d\n",screen->port); if (stunnel_port) { fprintf(flag, "SSL_PORT=%d\n", stunnel_port); } fflush(flag); fclose(flag); } else { rfbLog("could not open flag file: %s\n", flagfile); } } if (rm_flagfile) { int create = 0; struct stat sb; if (strstr(rm_flagfile, "create:") == rm_flagfile) { char *s = rm_flagfile; create = 1; rm_flagfile = strdup(rm_flagfile + strlen("create:")); free(s); } if (strstr(rm_flagfile, "nocreate:") == rm_flagfile) { char *s = rm_flagfile; create = 0; rm_flagfile = strdup(rm_flagfile + strlen("nocreate:")); free(s); } else if (stat(rm_flagfile, &sb) != 0) { create = 1; } if (create) { FILE *flag = fopen(rm_flagfile, "w"); if (flag) { fprintf(flag, "%d\n", getpid()); fclose(flag); } } } } fflush(stdout); } } static void check_cursor_changes(void) { static double last_push = 0.0; if (unixpw_in_progress) return; cursor_changes += check_x11_pointer(); if (cursor_changes) { double tm, max_push = 0.125, multi_push = 0.01, wait = 0.02; int cursor_shape, dopush = 0, link, latency, netrate; if (! all_clients_initialized()) { /* play it safe */ return; } if (0) cursor_shape = cursor_shape_updates_clients(screen); dtime0(&tm); link = link_rate(&latency, &netrate); if (link == LR_DIALUP) { max_push = 0.2; wait = 0.05; } else if (link == LR_BROADBAND) { max_push = 0.075; wait = 0.05; } else if (link == LR_LAN) { max_push = 0.01; } else if (latency < 5 && netrate > 200) { max_push = 0.01; } if (tm > last_push + max_push) { dopush = 1; } else if (cursor_changes > 1 && tm > last_push + multi_push) { dopush = 1; } if (dopush) { mark_rect_as_modified(0, 0, 1, 1, 1); fb_push_wait(wait, FB_MOD); last_push = tm; } else { rfbPE(0); } } cursor_changes = 0; } /* * These watch_loop() releated were moved from x11vnc.c so we could try * to remove -O2 from its compilation. TDB new file, e.g. watch.c. */ static void check_filexfer(void) { static time_t last_check = 0; rfbClientIteratorPtr iter; rfbClientPtr cl; int transferring = 0; if (time(NULL) <= last_check) { return; } #if 0 if (getenv("NOFT")) { return; } #endif iter = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(iter)) ) { if (cl->fileTransfer.receiving) { transferring = 1; break; } if (cl->fileTransfer.sending) { transferring = 1; break; } } rfbReleaseClientIterator(iter); if (transferring) { double start = dnow(); while (dnow() < start + 0.5) { rfbCFD(5000); rfbCFD(1000); rfbCFD(0); } } else { last_check = time(NULL); } } static void record_last_fb_update(void) { static int rbs0 = -1; static time_t last_call = 0; time_t now = time(NULL); int rbs = -1; rfbClientIteratorPtr iter; rfbClientPtr cl; if (last_fb_bytes_sent == 0) { last_fb_bytes_sent = now; last_call = now; } if (now <= last_call + 1) { /* check every second or so */ return; } if (unixpw_in_progress) return; last_call = now; if (! screen) { return; } iter = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(iter)) ) { #if 0 rbs += cl->rawBytesEquivalent; #else #if LIBVNCSERVER_HAS_STATS rbs += rfbStatGetSentBytesIfRaw(cl); #endif #endif } rfbReleaseClientIterator(iter); if (rbs != rbs0) { rbs0 = rbs; if (debug_tiles > 1) { fprintf(stderr, "record_last_fb_update: %d %d\n", (int) now, (int) last_fb_bytes_sent); } last_fb_bytes_sent = now; } } static int choose_delay(double dt) { static double t0 = 0.0, t1 = 0.0, t2 = 0.0, now; static int x0, y0, x1, y1, x2, y2, first = 1; int dx0, dy0, dx1, dy1, dm, i, msec = waitms; double cut1 = 0.15, cut2 = 0.075, cut3 = 0.25; double bogdown_time = 0.25, bave = 0.0; int bogdown = 1, bcnt = 0; int ndt = 8, nave = 3; double fac = 1.0; static int db = 0, did_set_defer = 0; static double dts[8]; static int link = LR_UNSET, latency = -1, netrate = -1; static double last_link = 0.0; if (screen && did_set_defer) { /* reset defer in case we changed it */ screen->deferUpdateTime = defer_update; } if (waitms == 0) { return waitms; } if (nofb) { return waitms; } if (first) { for(i=0; i last_link + 30.0 || link == LR_UNSET) { link = link_rate(&latency, &netrate); last_link = now; } /* * first check for bogdown, e.g. lots of activity, scrolling text * from command output, etc. */ if (nap_ok) { dt = 0.0; } if (! wait_bog) { bogdown = 0; } else if (button_mask || now < last_keyboard_time + 2*bogdown_time) { /* * let scrolls & keyboard input through the normal way * otherwise, it will likely just annoy them. */ bogdown = 0; } else if (dt > 0.0) { /* * inspect recent dt's: * 0 1 2 3 4 5 6 7 dt * ^ ^ ^ */ for (i = ndt - (nave - 1); i < ndt; i++) { bave += dts[i]; bcnt++; if (dts[i] < bogdown_time) { bogdown = 0; break; } } bave += dt; bcnt++; bave = bave / bcnt; if (dt < bogdown_time) { bogdown = 0; } } else { bogdown = 0; } /* shift for next time */ for (i = 0; i < ndt-1; i++) { dts[i] = dts[i+1]; } dts[ndt-1] = dt; if (0 && dt > 0.0) fprintf(stderr, "dt: %.5f %.4f\n", dt, dnowx()); if (bogdown) { if (use_xdamage) { /* DAMAGE can queue ~1000 rectangles for a scroll */ clear_xdamage_mark_region(NULL, 0); } msec = (int) (1000 * 1.75 * bave); if (dts[ndt - nave - 1] > 0.75 * bave) { msec = 1.5 * msec; set_xdamage_mark(0, 0, dpy_x, dpy_y); } if (msec > 1500) { msec = 1500; } if (msec < waitms) { msec = waitms; } db = (db || debug_tiles); if (db) fprintf(stderr, "bogg[%d] %.3f %.3f %.3f %.3f\n", msec, dts[ndt-4], dts[ndt-3], dts[ndt-2], dts[ndt-1]); return msec; } /* next check for pointer motion, keystrokes, to speed up */ t2 = dnow(); x2 = cursor_x; y2 = cursor_y; dx0 = nabs(x1 - x0); dy0 = nabs(y1 - y0); dx1 = nabs(x2 - x1); dy1 = nabs(y2 - y1); /* bigger displacement for most recent dt: */ if (dx1 > dy1) { dm = dx1; } else { dm = dy1; } if ((dx0 || dy0) && (dx1 || dy1)) { /* if mouse moved the previous two times: */ if (t2 < t0 + cut1 || t2 < t1 + cut2 || dm > 20) { /* * if within 0.15s(0) or 0.075s(1) or mouse * moved > 20pixels, set and bump up the cut * down factor. */ fac = wait_ui * 1.5; } else if ((dx1 || dy1) && dm > 40) { fac = wait_ui; } else { /* still 1.0? */ if (db > 1) fprintf(stderr, "wait_ui: still 1.0\n"); } } else if ((dx1 || dy1) && dm > 40) { /* if mouse moved > 40 last time: */ fac = wait_ui; } if (fac == 1.0 && t2 < last_keyboard_time + cut3) { /* if typed in last 0.25s set wait_ui */ fac = wait_ui; } if (fac != 1.0) { if (link == LR_LAN || latency <= 3) { fac *= 1.5; } } msec = (int) (((double) waitms) / fac); if (msec == 0) { msec = 1; } if (set_defer && fac != 1.0 && screen) { /* this is wait_ui mode, set defer to match wait: */ if (set_defer >= 1) { screen->deferUpdateTime = msec; } else if (set_defer <= -1) { screen->deferUpdateTime = 0; } if (nabs(set_defer) == 2) { urgent_update = 1; } did_set_defer = 1; } x0 = x1; y0 = y1; t0 = t1; x1 = x2; y1 = y2; t1 = t2; if (db > 1) fprintf(stderr, "wait: %2d defer[%02d]: %2d\n", msec, defer_update, screen->deferUpdateTime); return msec; } /* * main x11vnc loop: polls, checks for events, iterate libvncserver, etc. */ void watch_loop(void) { int cnt = 0, tile_diffs = 0, skip_pe = 0, wait; double tm, dtr, dt = 0.0; time_t start = time(NULL); if (use_threads) { rfbRunEventLoop(screen, -1, TRUE); } while (1) { char msg[] = "new client: %s taking unixpw client off hold.\n"; got_user_input = 0; got_pointer_input = 0; got_local_pointer_input = 0; got_pointer_calls = 0; got_keyboard_input = 0; got_keyboard_calls = 0; urgent_update = 0; x11vnc_current = dnow(); if (! use_threads) { dtime0(&tm); if (! skip_pe) { if (unixpw_in_progress) { rfbClientPtr cl = unixpw_client; if (cl && cl->onHold) { rfbLog(msg, cl->host); unixpw_client->onHold = FALSE; } } else { measure_send_rates(1); } unixpw_in_rfbPE = 1; /* * do a few more since a key press may * have induced a small change we want to * see quickly (just 1 rfbPE will likely * only process the subsequent "up" event) */ if (tm < last_keyboard_time + 0.16) { rfbPE(0); rfbPE(0); rfbPE(-1); rfbPE(0); rfbPE(0); } else { rfbPE(-1); } unixpw_in_rfbPE = 0; if (unixpw_in_progress) { /* rfbPE loop until logged in. */ skip_pe = 0; check_new_clients(); continue; } else { measure_send_rates(0); fb_update_sent(NULL); } } else { if (unixpw_in_progress) { skip_pe = 0; check_new_clients(); continue; } } dtr = dtime(&tm); if (! cursor_shape_updates) { /* undo any cursor shape requests */ disable_cursor_shape_updates(screen); } if (screen && screen->clientHead) { int ret = check_user_input(dt, dtr, tile_diffs, &cnt); /* true: loop back for more input */ if (ret == 2) { skip_pe = 1; } if (ret) { if (debug_scroll) fprintf(stderr, "watch_loop: LOOP-BACK: %d\n", ret); continue; } } /* watch for viewonly input piling up: */ if ((got_pointer_calls > got_pointer_input) || (got_keyboard_calls > got_keyboard_input)) { eat_viewonly_input(10, 3); } } else { /* -threads here. */ if (wireframe && button_mask) { check_wireframe(); } } skip_pe = 0; if (shut_down) { clean_up_exit(0); } if (unixpw_in_progress) { check_new_clients(); continue; } if (! urgent_update) { if (do_copy_screen) { do_copy_screen = 0; copy_screen(); } check_new_clients(); check_ncache(0, 0); check_xevents(0); check_autorepeat(); check_pm(); check_filexfer(); check_keycode_state(); check_connect_inputs(); check_gui_inputs(); check_stunnel(); check_openssl(); check_https(); record_last_fb_update(); check_padded_fb(); check_fixscreen(); check_xdamage_state(); check_xrecord_reset(0); check_add_keysyms(); check_new_passwds(0); #ifdef ENABLE_GRABLOCAL if (grab_local) { check_local_grab(); } #endif if (started_as_root) { check_switched_user(); } if (first_conn_timeout < 0) { start = time(NULL); first_conn_timeout = -first_conn_timeout; } } if (rawfb_vnc_reflect) { static time_t lastone = 0; if (time(NULL) > lastone + 10) { lastone = time(NULL); vnc_reflect_process_client(); } } if (! screen || ! screen->clientHead) { /* waiting for a client */ if (first_conn_timeout) { if (time(NULL) - start > first_conn_timeout) { rfbLog("No client after %d secs.\n", first_conn_timeout); shut_down = 1; } } usleep(200 * 1000); continue; } if (first_conn_timeout && all_clients_initialized()) { first_conn_timeout = 0; } if (nofb) { /* no framebuffer polling needed */ if (cursor_pos_updates) { check_x11_pointer(); } #ifdef MACOSX else check_x11_pointer(); #endif continue; } if (button_mask && (!show_dragging || pointer_mode == 0)) { /* * if any button is pressed in this mode do * not update rfb screen, but do flush the * X11 display. */ X_LOCK; XFlush_wr(dpy); X_UNLOCK; dt = 0.0; } else { static double last_dt = 0.0; double xdamage_thrash = 0.4; check_cursor_changes(); /* for timing the scan to try to detect thrashing */ if (use_xdamage && last_dt > xdamage_thrash) { clear_xdamage_mark_region(NULL, 0); } if (unixpw_in_progress) continue; if (rawfb_vnc_reflect) { vnc_reflect_process_client(); } dtime0(&tm); #if !NO_X11 if (xrandr_present && !xrandr && xrandr_maybe) { int delay = 180; /* there may be xrandr right after xsession start */ if (tm < x11vnc_start + delay || tm < last_client + delay) { int tw = 20; if (auth_file != NULL) { tw = 120; } X_LOCK; if (tm < x11vnc_start + tw || tm < last_client + tw) { XSync(dpy, False); } else { XFlush_wr(dpy); } X_UNLOCK; } check_xrandr_event("before-scan"); } #endif if (use_snapfb) { int t, tries = 3; copy_snap(); for (t=0; t < tries; t++) { tile_diffs = scan_for_updates(0); } } else { tile_diffs = scan_for_updates(0); } dt = dtime(&tm); if (! nap_ok) { last_dt = dt; } if ((debug_tiles || debug_scroll > 1 || debug_wireframe > 1) && (tile_diffs > 4 || debug_tiles > 1)) { double rate = (tile_x * tile_y * bpp/8 * tile_diffs) / dt; fprintf(stderr, "============================= TILES: %d dt: %.4f" " t: %.4f %.2f MB/s nap_ok: %d\n", tile_diffs, dt, tm - x11vnc_start, rate/1000000.0, nap_ok); } } /* sleep a bit to lessen load */ wait = choose_delay(dt); if (urgent_update) { ; } else if (wait > 2*waitms) { /* bog case, break it up */ nap_sleep(wait, 10); } else { usleep(wait * 1000); } cnt++; } }