Home | History | Annotate | Download | only in x11vnc
      1 /*
      2    Copyright (C) 2002-2010 Karl J. Runge <runge (at) karlrunge.com>
      3    All rights reserved.
      4 
      5 This file is part of x11vnc.
      6 
      7 x11vnc is free software; you can redistribute it and/or modify
      8 it under the terms of the GNU General Public License as published by
      9 the Free Software Foundation; either version 2 of the License, or (at
     10 your option) any later version.
     11 
     12 x11vnc is distributed in the hope that it will be useful,
     13 but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with x11vnc; if not, write to the Free Software
     19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
     20 or see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, as a special exception, Karl J. Runge
     23 gives permission to link the code of its release of x11vnc with the
     24 OpenSSL project's "OpenSSL" library (or with modified versions of it
     25 that use the same license as the "OpenSSL" library), and distribute
     26 the linked executables.  You must obey the GNU General Public License
     27 in all respects for all of the code used other than "OpenSSL".  If you
     28 modify this file, you may extend this exception to your version of the
     29 file, but you are not obligated to do so.  If you do not wish to do
     30 so, delete this exception statement from your version.
     31 */
     32 
     33 /* -- rates.c -- */
     34 
     35 #include "x11vnc.h"
     36 #include "xwrappers.h"
     37 #include "scan.h"
     38 
     39 int measure_speeds = 1;
     40 int speeds_net_rate = 0;
     41 int speeds_net_rate_measured = 0;
     42 int speeds_net_latency = 0;
     43 int speeds_net_latency_measured = 0;
     44 int speeds_read_rate = 0;
     45 int speeds_read_rate_measured = 0;
     46 
     47 
     48 int get_cmp_rate(void);
     49 int get_raw_rate(void);
     50 void initialize_speeds(void);
     51 int get_read_rate(void);
     52 int link_rate(int *latency, int *netrate);
     53 int get_net_rate(void);
     54 int get_net_latency(void);
     55 void measure_send_rates(int init);
     56 
     57 
     58 static void measure_display_hook(rfbClientPtr cl);
     59 static int get_rate(int which);
     60 static int get_latency(void);
     61 
     62 
     63 static void measure_display_hook(rfbClientPtr cl) {
     64 	ClientData *cd = (ClientData *) cl->clientData;
     65 	if (! cd) {
     66 		return;
     67 	}
     68 	dtime0(&cd->timer);
     69 }
     70 
     71 static int get_rate(int which) {
     72 	rfbClientIteratorPtr iter;
     73 	rfbClientPtr cl;
     74 	int irate, irate_min = 1;	/* 1 KB/sec */
     75 	int irate_max = 100000;		/* 100 MB/sec */
     76 	int count = 0;
     77 	double slowest = -1.0, rate;
     78 	static double save_rate = 1000 * NETRATE0;
     79 
     80 	if (!screen) {
     81 		return 0;
     82 	}
     83 
     84 	iter = rfbGetClientIterator(screen);
     85 	while( (cl = rfbClientIteratorNext(iter)) ) {
     86 		ClientData *cd = (ClientData *) cl->clientData;
     87 
     88 		if (! cd) {
     89 			continue;
     90 		}
     91 		if (cl->state != RFB_NORMAL) {
     92 			continue;
     93 		}
     94 		if (cd->send_cmp_rate == 0.0 || cd->send_raw_rate == 0.0) {
     95 			continue;
     96 		}
     97 		count++;
     98 
     99 		if (which == 0) {
    100 			rate = cd->send_cmp_rate;
    101 		} else {
    102 			rate = cd->send_raw_rate;
    103 		}
    104 		if (slowest == -1.0 || rate < slowest) {
    105 			slowest = rate;
    106 		}
    107 
    108 	}
    109 	rfbReleaseClientIterator(iter);
    110 
    111 	if (! count) {
    112 		return NETRATE0;
    113 	}
    114 
    115 	if (slowest == -1.0) {
    116 		slowest = save_rate;
    117 	} else {
    118 		save_rate = slowest;
    119 	}
    120 
    121 	irate = (int) (slowest/1000.0);
    122 	if (irate < irate_min) {
    123 		irate = irate_min;
    124 	}
    125 	if (irate > irate_max) {
    126 		irate = irate_max;
    127 	}
    128 if (0) fprintf(stderr, "get_rate(%d) %d %.3f/%.3f\n", which, irate, save_rate, slowest);
    129 
    130 	return irate;
    131 }
    132 
    133 static int get_latency(void) {
    134 	rfbClientIteratorPtr iter;
    135 	rfbClientPtr cl;
    136 	int ilat, ilat_min = 1;	/* 1 ms */
    137 	int ilat_max = 2000;	/* 2 sec */
    138 	double slowest = -1.0, lat;
    139 	static double save_lat = ((double) LATENCY0)/1000.0;
    140 	int count = 0;
    141 
    142 	if (!screen) {
    143 		return 0;
    144 	}
    145 
    146 	iter = rfbGetClientIterator(screen);
    147 	while( (cl = rfbClientIteratorNext(iter)) ) {
    148 		ClientData *cd = (ClientData *) cl->clientData;
    149 
    150 		if (! cd) {
    151 			continue;
    152 		}
    153 		if (cl->state != RFB_NORMAL) {
    154 			continue;
    155 		}
    156 		if (cd->latency == 0.0) {
    157 			continue;
    158 		}
    159 		count++;
    160 
    161 		lat = cd->latency;
    162 		if (slowest == -1.0 || lat > slowest) {
    163 			slowest = lat;
    164 		}
    165 	}
    166 	rfbReleaseClientIterator(iter);
    167 
    168 	if (! count) {
    169 		return LATENCY0;
    170 	}
    171 
    172 	if (slowest == -1.0) {
    173 		slowest = save_lat;
    174 	} else {
    175 		save_lat = slowest;
    176 	}
    177 
    178 	ilat = (int) (slowest * 1000.0);
    179 	if (ilat < ilat_min) {
    180 		ilat = ilat_min;
    181 	}
    182 	if (ilat > ilat_max) {
    183 		ilat = ilat_max;
    184 	}
    185 
    186 	return ilat;
    187 }
    188 
    189 int get_cmp_rate(void) {
    190 	return get_rate(0);
    191 }
    192 
    193 int get_raw_rate(void) {
    194 	return get_rate(1);
    195 }
    196 
    197 void initialize_speeds(void) {
    198 	char *s, *s_in, *p;
    199 	int i;
    200 
    201 	speeds_read_rate = 0;
    202 	speeds_net_rate = 0;
    203 	speeds_net_latency = 0;
    204 	if (! speeds_str || *speeds_str == '\0') {
    205 		s_in = strdup("");
    206 	} else {
    207 		s_in = strdup(speeds_str);
    208 	}
    209 
    210 	if (!strcmp(s_in, "modem")) {
    211 		s = strdup("6,4,200");
    212 	} else if (!strcmp(s_in, "dsl")) {
    213 		s = strdup("6,100,50");
    214 	} else if (!strcmp(s_in, "lan")) {
    215 		s = strdup("6,5000,1");
    216 	} else {
    217 		s = strdup(s_in);
    218 	}
    219 
    220 	p = strtok(s, ",");
    221 	i = 0;
    222 	while (p) {
    223 		double val;
    224 		if (*p != '\0') {
    225 			val = atof(p);
    226 			if (i==0) {
    227 				speeds_read_rate = (int) 1000000 * val;
    228 			} else if (i==1) {
    229 				speeds_net_rate = (int) 1000 * val;
    230 			} else if (i==2) {
    231 				speeds_net_latency = (int) val;
    232 			}
    233 		}
    234 		i++;
    235 		p = strtok(NULL, ",");
    236 	}
    237 	free(s);
    238 	free(s_in);
    239 
    240 	if (! speeds_read_rate) {
    241 		int n = 0;
    242 		double dt, timer;
    243 #ifdef MACOSX
    244 		if (macosx_console && macosx_read_opengl && fullscreen) {
    245 			copy_image(fullscreen, 0, 0, 0, 0);
    246 			usleep(10 * 1000);
    247 		}
    248 #endif
    249 
    250 		dtime0(&timer);
    251 		if (fullscreen) {
    252 			copy_image(fullscreen, 0, 0, 0, 0);
    253 			n = fullscreen->bytes_per_line * fullscreen->height;
    254 		} else if (scanline) {
    255 			copy_image(scanline, 0, 0, 0, 0);
    256 			n = scanline->bytes_per_line * scanline->height;
    257 		}
    258 		dt = dtime(&timer);
    259 		if (n && dt > 0.0) {
    260 			double rate = ((double) n) / dt;
    261 			speeds_read_rate_measured = (int) (rate/1000000.0);
    262 			if (speeds_read_rate_measured < 1) {
    263 				speeds_read_rate_measured = 1;
    264 			} else {
    265 				rfbLog("fb read rate: %d MB/sec\n",
    266 				    speeds_read_rate_measured);
    267 			}
    268 		}
    269 	}
    270 }
    271 
    272 int get_read_rate(void) {
    273 	if (speeds_read_rate) {
    274 		return speeds_read_rate;
    275 	}
    276 	if (speeds_read_rate_measured) {
    277 		return speeds_read_rate_measured;
    278 	}
    279 	return 0;
    280 }
    281 
    282 int link_rate(int *latency, int *netrate) {
    283 	*latency = get_net_latency();
    284 	*netrate = get_net_rate();
    285 
    286 	if (speeds_str) {
    287 		if (!strcmp(speeds_str, "modem")) {
    288 			return LR_DIALUP;
    289 		} else if (!strcmp(speeds_str, "dsl")) {
    290 			return LR_BROADBAND;
    291 		} else if (!strcmp(speeds_str, "lan")) {
    292 			return LR_LAN;
    293 		}
    294 	}
    295 
    296 	if (*latency == LATENCY0 && *netrate == NETRATE0)  {
    297 		return LR_UNSET;
    298 	} else if (*latency > 150 || *netrate < 20) {
    299 		return LR_DIALUP;
    300 	} else if (*latency > 50 || *netrate < 150) {
    301 		return LR_BROADBAND;
    302 	} else if (*latency < 10 && *netrate > 300) {
    303 		return LR_LAN;
    304 	} else {
    305 		return LR_UNKNOWN;
    306 	}
    307 }
    308 
    309 int get_net_rate(void) {
    310 	int spm = speeds_net_rate_measured;
    311 	if (speeds_net_rate) {
    312 		return speeds_net_rate;
    313 	}
    314 	if (! spm || spm == NETRATE0) {
    315 		speeds_net_rate_measured = get_cmp_rate();
    316 	}
    317 	if (speeds_net_rate_measured) {
    318 		return speeds_net_rate_measured;
    319 	}
    320 	return 0;
    321 }
    322 
    323 int get_net_latency(void) {
    324 	int spm = speeds_net_latency_measured;
    325 	if (speeds_net_latency) {
    326 		return speeds_net_latency;
    327 	}
    328 	if (! spm || spm == LATENCY0) {
    329 		speeds_net_latency_measured = get_latency();
    330 	}
    331 	if (speeds_net_latency_measured) {
    332 		return speeds_net_latency_measured;
    333 	}
    334 	return 0;
    335 }
    336 
    337 void measure_send_rates(int init) {
    338 	double cmp_rate, raw_rate;
    339 	static double now, start = 0.0;
    340 	static rfbDisplayHookPtr orig_display_hook = NULL;
    341 	double cmp_max = 1.0e+08;	/* 100 MB/sec */
    342 	double cmp_min = 1000.0;	/* 9600baud */
    343 	double lat_max = 5.0;		/* 5 sec */
    344 	double lat_min = .0005;		/* 0.5 ms */
    345 	int min_cmp = 10000, nclients;
    346 	rfbClientIteratorPtr iter;
    347 	rfbClientPtr cl0, cl;
    348 	int msg = 0, clcnt0 = 0, cc;
    349 	int db = 0, ouch_db = 0, ouch = 0;
    350 
    351 	if (! measure_speeds) {
    352 		return;
    353 	}
    354 	if (speeds_net_rate && speeds_net_latency) {
    355 		return;
    356 	}
    357 	if (!client_count) {
    358 		return;
    359 	}
    360 
    361 	if (! orig_display_hook) {
    362 		orig_display_hook = screen->displayHook;
    363 	}
    364 
    365 	if (start == 0.0) {
    366 		dtime(&start);
    367 	}
    368 
    369 	dtime0(&now);
    370 	if (now < last_client_gone+4.0) {
    371 		return;
    372 	}
    373 	now = now - start;
    374 
    375 	nclients = 0;
    376 
    377 	if (!screen) {
    378 		return;
    379 	}
    380 
    381 	cl0 = NULL;
    382 	iter = rfbGetClientIterator(screen);
    383 	while( (cl = rfbClientIteratorNext(iter)) ) {
    384 		ClientData *cd = (ClientData *) cl->clientData;
    385 
    386 		if (! cd) {
    387 			continue;
    388 		}
    389 		if (cd->send_cmp_rate > 0.0) {
    390 			continue;
    391 		}
    392 		if (cl->onHold) {
    393 			continue;
    394 		}
    395 		nclients++;
    396 		if (cl0 == NULL)  {
    397 			cl0 = cl;
    398 		}
    399 	}
    400 	rfbReleaseClientIterator(iter);
    401 
    402 	cl = cl0;
    403 	cc = 0;
    404 
    405 	while (cl != NULL && cc++ == 0) {
    406 		int defer, i, cbs, rbs;
    407 		char *httpdir;
    408 		double dt, dt1 = 0.0, dt2, dt3;
    409 		double tm, spin_max = 15.0, spin_lat_max = 1.5;
    410 		int got_t2 = 0, got_t3 = 0;
    411 		ClientData *cd = (ClientData *) cl->clientData;
    412 
    413 #if 0
    414 		for (i=0; i<MAX_ENCODINGS; i++) {
    415 			cbs += cl->bytesSent[i];
    416 		}
    417 		rbs = cl->rawBytesEquivalent;
    418 #else
    419 #if LIBVNCSERVER_HAS_STATS
    420 		cbs = rfbStatGetSentBytes(cl);
    421 		rbs = rfbStatGetSentBytesIfRaw(cl);
    422 #endif
    423 #endif
    424 
    425 		if (init) {
    426 
    427 if (db) fprintf(stderr, "%d client num rects req: %d  mod: %d  cbs: %d  "
    428     "rbs: %d  dt1: %.3f  t: %.3f\n", init,
    429     (int) sraRgnCountRects(cl->requestedRegion),
    430     (int) sraRgnCountRects(cl->modifiedRegion), cbs, rbs, dt1, now);
    431 
    432 			cd->timer = dnow();
    433 			cd->cmp_bytes_sent = cbs;
    434 			cd->raw_bytes_sent = rbs;
    435 			continue;
    436 		}
    437 
    438 		/* first part of the bulk transfer of initial screen */
    439 		dt1 = dtime(&cd->timer);
    440 
    441 if (db) fprintf(stderr, "%d client num rects req: %d  mod: %d  cbs: %d  "
    442     "rbs: %d  dt1: %.3f  t: %.3f\n", init,
    443     (int) sraRgnCountRects(cl->requestedRegion),
    444     (int) sraRgnCountRects(cl->modifiedRegion), cbs, rbs, dt1, now);
    445 
    446 		if (dt1 <= 0.0) {
    447 			continue;
    448 		}
    449 
    450 		cbs = cbs - cd->cmp_bytes_sent;
    451 		rbs = rbs - cd->raw_bytes_sent;
    452 
    453 		if (cbs < min_cmp) {
    454 			continue;
    455 		}
    456 
    457 		if (ouch_db) fprintf(stderr, "START-OUCH: %d\n", client_count);
    458 		clcnt0 = client_count;
    459 #define OUCH ( ouch || (ouch = (!client_count || client_count != clcnt0 || dnow() < last_client_gone+4.0)) )
    460 
    461 		rfbPE(1000);
    462 		if (OUCH && ouch_db) fprintf(stderr, "***OUCH-A\n");
    463 		if (OUCH) continue;
    464 
    465 		if (use_threads) LOCK(cl->updateMutex);
    466 
    467 		if (sraRgnCountRects(cl->modifiedRegion)) {
    468 			rfbPE(1000);
    469 			if (OUCH && ouch_db) fprintf(stderr, "***OUCH-B\n");
    470 			if (use_threads) UNLOCK(cl->updateMutex);
    471 			if (OUCH) continue;
    472 		}
    473 
    474 		if (use_threads) UNLOCK(cl->updateMutex);
    475 
    476 		defer = screen->deferUpdateTime;
    477 		httpdir = screen->httpDir;
    478 		screen->deferUpdateTime = 0;
    479 		screen->httpDir = NULL;
    480 
    481 		/* mark a small rectangle: */
    482 		mark_rect_as_modified(0, 0, 16, 16, 1);
    483 
    484 		dtime0(&tm);
    485 
    486 		dt2 = 0.0;
    487 		dt3 = 0.0;
    488 
    489 		if (dt1 < 0.25) {
    490 			/* try to cut it down to avoid long pauses. */
    491 			spin_max = 5.0;
    492 		}
    493 
    494 		/* when req1 = 1 mod1 == 0, end of 2nd part of bulk transfer */
    495 		while (1) {
    496 			int req0, req1, mod0, mod1;
    497 
    498 			if (OUCH && ouch_db) fprintf(stderr, "***OUCH-C1\n");
    499 			if (OUCH) break;
    500 
    501 			if (use_threads) LOCK(cl->updateMutex);
    502 
    503 			req0 = sraRgnCountRects(cl->requestedRegion);
    504 			mod0 = sraRgnCountRects(cl->modifiedRegion);
    505 
    506 			if (use_threads) UNLOCK(cl->updateMutex);
    507 
    508 			if (use_threads) {
    509 				usleep(1000);
    510 			} else {
    511 				if (mod0) {
    512 					rfbPE(1000);
    513 				} else {
    514 					rfbCFD(1000);
    515 				}
    516 			}
    517 			dt = dtime(&tm);
    518 			dt2 += dt;
    519 			if (dt2 > spin_max) {
    520 				break;
    521 			}
    522 
    523 			if (OUCH && ouch_db) fprintf(stderr, "***OUCH-C2\n");
    524 			if (OUCH) break;
    525 
    526 			if (use_threads) LOCK(cl->updateMutex);
    527 
    528 			req1 = sraRgnCountRects(cl->requestedRegion);
    529 			mod1 = sraRgnCountRects(cl->modifiedRegion);
    530 
    531 			if (use_threads) UNLOCK(cl->updateMutex);
    532 
    533 if (db) fprintf(stderr, "dt2 calc: num rects req: %d/%d mod: %d/%d  "
    534     "fbu-sent: %d  dt: %.4f dt2: %.4f  tm: %.4f\n",
    535     req0, req1, mod0, mod1,
    536 #if 0
    537     cl->framebufferUpdateMessagesSent,
    538 #else
    539 #if LIBVNCSERVER_HAS_STATS
    540     rfbStatGetMessageCountSent(cl, rfbFramebufferUpdate),
    541 #endif
    542 #endif
    543     dt, dt2, tm);
    544 			if (req1 != 0 && mod1 == 0) {
    545 				got_t2 = 1;
    546 				break;
    547 			}
    548 		}
    549 		if (OUCH && ouch_db) fprintf(stderr, "***OUCH-D\n");
    550 		if (OUCH) goto ouch;
    551 
    552 		if (! got_t2) {
    553 			dt2 = 0.0;
    554 		} else {
    555 			int tr, trm = 3;
    556 			double dts[10];
    557 
    558 			/*
    559 			 * Note: since often select(2) cannot sleep
    560 			 * less than 1/HZ (e.g. 10ms), the resolution
    561 			 * of the latency may be messed up by something
    562 			 * of this order.  Effect may occur on both ends,
    563 			 * i.e. the viewer may not respond immediately.
    564 			 */
    565 
    566 			for (tr = 0; tr < trm; tr++) {
    567 				usleep(5000);
    568 
    569 				/* mark a 2nd small rectangle: */
    570 				mark_rect_as_modified(0, 0, 16, 16, 1);
    571 				i = 0;
    572 				dtime0(&tm);
    573 				dt3 = 0.0;
    574 
    575 				/*
    576 				 * when req1 > 0 and mod1 == 0, we say
    577 				 * that is the "ping" time.
    578 				 */
    579 				while (1) {
    580 					int req0, req1, mod0, mod1;
    581 
    582 					if (use_threads) LOCK(cl->updateMutex);
    583 
    584 					req0 = sraRgnCountRects(cl->requestedRegion);
    585 					mod0 = sraRgnCountRects(cl->modifiedRegion);
    586 
    587 					if (use_threads) UNLOCK(cl->updateMutex);
    588 
    589 					if (i == 0) {
    590 						rfbPE(0);
    591 					} else {
    592 						if (use_threads) {
    593 							usleep(1000);
    594 						} else {
    595 							/* try to get it all */
    596 							rfbCFD(1000*1000);
    597 						}
    598 					}
    599 					if (OUCH && ouch_db) fprintf(stderr, "***OUCH-E\n");
    600 					if (OUCH) goto ouch;
    601 					dt = dtime(&tm);
    602 					i++;
    603 
    604 					dt3 += dt;
    605 					if (dt3 > spin_lat_max) {
    606 						break;
    607 					}
    608 
    609 					if (use_threads) LOCK(cl->updateMutex);
    610 
    611 					req1 = sraRgnCountRects(cl->requestedRegion);
    612 					mod1 = sraRgnCountRects(cl->modifiedRegion);
    613 
    614 					if (use_threads) UNLOCK(cl->updateMutex);
    615 
    616 if (db) fprintf(stderr, "dt3 calc: num rects req: %d/%d mod: %d/%d  "
    617     "fbu-sent: %d  dt: %.4f dt3: %.4f  tm: %.4f\n",
    618     req0, req1, mod0, mod1,
    619 #if 0
    620     cl->framebufferUpdateMessagesSent,
    621 #else
    622 #if LIBVNCSERVER_HAS_STATS
    623     rfbStatGetMessageCountSent(cl, rfbFramebufferUpdate),
    624 #endif
    625 #endif
    626     dt, dt3, tm);
    627 
    628 					if (req1 != 0 && mod1 == 0) {
    629 						dts[got_t3++] = dt3;
    630 						break;
    631 					}
    632 				}
    633 			}
    634 
    635 			if (! got_t3) {
    636 				dt3 = 0.0;
    637 			} else {
    638 				if (got_t3 == 1) {
    639 					dt3 = dts[0];
    640 				} else if (got_t3 == 2) {
    641 					dt3 = dts[1];
    642 				} else {
    643 					if (dts[2] > 0.0) {
    644 						double rat = dts[1]/dts[2];
    645 						if (rat > 0.5 && rat < 2.0) {
    646 							dt3 = dts[1]+dts[2];
    647 							dt3 *= 0.5;
    648 						} else {
    649 							dt3 = dts[1];
    650 						}
    651 					} else {
    652 						dt3 = dts[1];
    653 					}
    654 				}
    655 			}
    656 		}
    657 
    658 		ouch:
    659 
    660 		screen->deferUpdateTime = defer;
    661 		screen->httpDir = httpdir;
    662 
    663 		if (OUCH && ouch_db) fprintf(stderr, "***OUCH-F\n");
    664 		if (OUCH) break;
    665 
    666 		dt = dt1 + dt2;
    667 
    668 
    669 		if (dt3 <= dt2/2.0) {
    670 			/* guess only 1/2 a ping for reply... */
    671 			dt = dt - dt3/2.0;
    672 		}
    673 
    674 		cmp_rate = cbs/dt;
    675 		raw_rate = rbs/dt;
    676 
    677 		if (cmp_rate > cmp_max) {
    678 			cmp_rate = cmp_max;
    679 		}
    680 		if (cmp_rate <= cmp_min) {
    681 			cmp_rate = cmp_min;
    682 		}
    683 
    684 		cd->send_cmp_rate = cmp_rate;
    685 		cd->send_raw_rate = raw_rate;
    686 
    687 		if (dt3 > lat_max) {
    688 			dt3 = lat_max;
    689 		}
    690 		if (dt3 <= lat_min) {
    691 			dt3 = lat_min;
    692 		}
    693 
    694 		cd->latency = dt3;
    695 
    696 		rfbLog("client %d network rate %.1f KB/sec (%.1f eff KB/sec)\n",
    697 		    cd->uid, cmp_rate/1000.0, raw_rate/1000.0);
    698 		rfbLog("client %d latency:  %.1f ms\n", cd->uid, 1000.0*dt3);
    699 		rfbLog("dt1: %.4f, dt2: %.4f dt3: %.4f bytes: %d\n",
    700 		    dt1, dt2, dt3, cbs);
    701 		msg = 1;
    702 	}
    703 
    704 	if (msg) {
    705 		int link, latency, netrate;
    706 		char *str = "error";
    707 
    708 		link = link_rate(&latency, &netrate);
    709 		if (link == LR_UNSET) {
    710 			str = "LR_UNSET";
    711 		} else if (link == LR_UNKNOWN) {
    712 			str = "LR_UNKNOWN";
    713 		} else if (link == LR_DIALUP) {
    714 			str = "LR_DIALUP";
    715 		} else if (link == LR_BROADBAND) {
    716 			str = "LR_BROADBAND";
    717 		} else if (link == LR_LAN) {
    718 			str = "LR_LAN";
    719 		}
    720 		rfbLog("link_rate: %s - %d ms, %d KB/s\n", str, latency,
    721 		    netrate);
    722 	}
    723 
    724 	if (init) {
    725 		if (nclients) {
    726 			screen->displayHook = measure_display_hook;
    727 		}
    728 	} else {
    729 		screen->displayHook = orig_display_hook;
    730 	}
    731 }
    732 
    733