diff options
Diffstat (limited to 'x11vnc/screen.c')
-rw-r--r-- | x11vnc/screen.c | 647 |
1 files changed, 613 insertions, 34 deletions
diff --git a/x11vnc/screen.c b/x11vnc/screen.c index d2eab06..8196f7d 100644 --- a/x11vnc/screen.c +++ b/x11vnc/screen.c @@ -26,6 +26,7 @@ #include "avahi.h" #include "solid.h" #include "inet.h" +#include "xrandr.h" #include <rfb/rfbclient.h> @@ -40,8 +41,8 @@ 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, int *scaling, int *blend, - int *nomult4, int *pad, int *interpolate, int *numer, int *denom); +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); @@ -54,6 +55,8 @@ 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); +void watch_loop(void); + static void debug_colormap(XImage *fb); static void set_visual(char *str); static void nofb_hook(rfbClientPtr cl); @@ -65,6 +68,11 @@ 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; @@ -2194,14 +2202,15 @@ if (0) fprintf(stderr, "DefaultDepth: %d visial_id: %d\n", depth, (int) visual_ #endif /* NO_X11 */ } -void parse_scale_string(char *str, double *factor, int *scaling, int *blend, - int *nomult4, int *pad, int *interpolate, int *numer, int *denom) { +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; + double f, f2; - *factor = 1.0; + *factor_x = 1.0; + *factor_y = 1.0; *scaling = 0; *blend = 1; *nomult4 = 0; @@ -2240,35 +2249,46 @@ void parse_scale_string(char *str, double *factor, int *scaling, int *blend, } *p = '\0'; } + if (strchr(tstr, '.') != NULL) { double test, diff, eps = 1.0e-7; - if (sscanf(tstr, "%lf", &f) != 1) { + 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; } - *factor = (double) f; /* look for common fractions from small ints: */ - for (n=2; n<=10; n++) { - for (m=1; m<n; m++) { - test = ((double) m)/ n; - diff = *factor - test; - if (-eps < diff && diff < eps) { - *numer = m; - *denom = n; + if (*factor_x == *factor_y) { + for (n=2; n<=10; n++) { + for (m=1; m<n; m++) { + test = ((double) m)/ n; + diff = *factor_x - test; + if (-eps < diff && diff < eps) { + *numer = m; + *denom = n; + break; + + } + } + if (*denom) { break; - } } - if (*denom) { - break; + if (*factor_x < 0.01) { + rfbLogEnable(1); + rfbLog("-scale factor too small: %f\n", *factor_x); + clean_up_exit(1); } } - if (*factor < 0.01) { - rfbLogEnable(1); - rfbLog("-scale factor too small: %f\n", scale_fac); - clean_up_exit(1); - } + } else if (sscanf(tstr, "%dx%d", &m, &n) == 2 && w_in > 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) { @@ -2285,18 +2305,19 @@ void parse_scale_string(char *str, double *factor, int *scaling, int *blend, rfbLog("invalid -scale arg: %s\n", tstr); clean_up_exit(1); } - *factor = ((double) m)/ n; - if (*factor < 0.01) { + *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); + rfbLog("-scale factor too small: %f\n", *factor_x); clean_up_exit(1); } *numer = m; *denom = n; } - if (*factor == 1.0) { + if (*factor_x == 1.0 && *factor_y == 1.0) { if (! quiet) { - rfbLog("scaling disabled for factor %f\n", *factor); + rfbLog("scaling disabled for factor %f %f\n", *factor_x, *factor_y); } } else { *scaling = 1; @@ -2347,13 +2368,13 @@ 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, &scaling, &scaling_blend, + parse_scale_string(scale_str, &scale_fac_x, &scale_fac_y, &scaling, &scaling_blend, &scaling_nomult4, &scaling_pad, &scaling_interpolate, - &scale_numer, &scale_denom); + &scale_numer, &scale_denom, *width_in, *height_in); if (scaling) { - width = scale_round(width, scale_fac); - height = scale_round(height, scale_fac); + 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, @@ -2487,8 +2508,8 @@ void initialize_screen(int *argc, char **argv, XImage *fb) { setup_scaling(&width, &height); if (scaling) { - rfbLog("scaling screen: %dx%d -> %dx%d scale_fac=%.5f\n", - fb->width, fb->height, scaled_x, scaled_y, scale_fac); + 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 { @@ -3240,4 +3261,562 @@ void set_vnc_desktop_name(void) { } } +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 + rbs += rfbStatGetSentBytesIfRaw(cl); +#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; + int db = 0; + static double dts[8]; + + if (waitms == 0) { + return waitms; + } + if (nofb) { + return waitms; + } + + if (first) { + for(i=0; i<ndt; i++) { + dts[i] = 0.0; + } + first = 0; + } + + now = dnow(); + + /* + * 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); + if (dx1 > dy1) { + dm = dx1; + } else { + dm = dy1; + } + + if ((dx0 || dy0) && (dx1 || dy1)) { + if (t2 < t0 + cut1 || t2 < t1 + cut2 || dm > 20) { + fac = wait_ui * 1.25; + } + } else if ((dx1 || dy1) && dm > 40) { + fac = wait_ui; + } + + if (fac == 1 && t2 < last_keyboard_time + cut3) { + fac = wait_ui; + } + msec = (int) ((double) waitms / fac); + if (msec == 0) { + msec = 1; + } + + x0 = x1; + y0 = y1; + t0 = t1; + + x1 = x2; + y1 = y2; + t1 = t2; + + 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; + 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 */ + if (! urgent_update) { + int wait = choose_delay(dt); + if (wait > 2*waitms) { + /* bog case, break it up */ + nap_sleep(wait, 10); + } else { + usleep(wait * 1000); + } + } + cnt++; + } +} + |