x11vnc: -chatwindow, -scale WxH, -enc changes.

pull/1/head
runge 15 years ago
parent 16c7ea1b35
commit 1c03dd4d51

@ -739,7 +739,7 @@ if (db24 > 1) fprintf(stderr, "multivis: 0x%lx ms: %d j: %d no: %d nm: %d dep=%d
if (attr->map_state == IsViewable &&
windows_8bpp[j].map_state != IsViewable) {
now_vis = 1;
}
}
if (db24 > 1) fprintf(stderr, "multivis: STORE 0x%lx j: %3d ms: %d dep=%d\n", win, j, attr->map_state, attr->depth);
windows_8bpp[j].win = win;
windows_8bpp[j].top = top;

@ -1,3 +1,11 @@
2008-10-19 Karl Runge <runge@karlrunge.com>
* x11vnc: -chatwindow for chat window on X console using SSVNC
as a helper. Print suggestion for X_ShmAttach failure.
Allow -scale WxH for different X- and Y-scaling factors.
Workaround for missing -enc cipher EVP_aes_256_cfb. Modify
message digest and salt/IV parameters. Try to improve compile
time by breaking up large if blocks.
2008-09-21 Karl Runge <runge@karlrunge.com>
* x11vnc: Add symmetric key encryption -enc cipher:keyfile,
works with SSVNC. Make -remap work on MacOSX console.

File diff suppressed because it is too large Load Diff

@ -226,6 +226,26 @@ int trap_getimage_xerror(Display *d, XErrorEvent *error) {
static int Xerror(Display *d, XErrorEvent *error) {
X_UNLOCK;
if (getenv("X11VNC_PRINT_XERROR")) {
fprintf(stderr, "Xerror: major_opcode: %d minor_opcode: %d error_code: %d\n",
error->request_code, error->minor_code, error->error_code);
}
if (xshm_opcode > 0 && error->request_code == xshm_opcode) {
if (error->minor_code == X_ShmAttach) {
char *dstr = DisplayString(dpy);
fprintf(stderr, "\nX11 MIT Shared Memory Attach failed:\n");
fprintf(stderr, " Is your DISPLAY=%s on a remote machine?\n", dstr);
if (strstr(dstr, "localhost:")) {
fprintf(stderr, " Note: DISPLAY=localhost:N suggests a SSH X11 redir to a remote machine.\n");
} else if (dstr[0] != ':') {
fprintf(stderr, " Note: DISPLAY=hostname:N suggests a remote display.\n");
}
fprintf(stderr, " Suggestion, use: x11vnc -display :0 ... for local display :0\n\n");
}
}
interrupted(0);
if (d) {} /* unused vars warning: */

@ -40,6 +40,7 @@ void set_client_input(char *str);
void set_child_info(void);
int cmd_ok(char *cmd);
void client_gone(rfbClientPtr client);
void client_gone_chat_helper(rfbClientPtr client);
void reverse_connect(char *str);
void set_vnc_connect_prop(char *str);
void read_vnc_connect_prop(int);
@ -48,6 +49,8 @@ void read_x11vnc_remote_prop(int);
void check_connect_inputs(void);
void check_gui_inputs(void);
enum rfbNewClientAction new_client(rfbClientPtr client);
enum rfbNewClientAction new_client_chat_helper(rfbClientPtr client);
rfbBool password_check_chat_helper(rfbClientPtr cl, const char* response, int len);
void start_client_info_sock(char *host_port_cookie);
void send_client_info(char *str);
void adjust_grabs(int grab, int quiet);
@ -2469,7 +2472,7 @@ void reverse_connect(char *str) {
n = cnt;
if (n >= n_max) {
n = n_max;
}
}
t = sleep_max - sleep_min;
tot = sleep_min + ((n-1) * t) / (n_max-1);
@ -2788,6 +2791,40 @@ static void turn_off_truecolor_ad(rfbClientPtr client) {
}
}
/*
* some overrides for the local console text chat.
* could be useful in general for local helpers.
*/
rfbBool password_check_chat_helper(rfbClientPtr cl, const char* response, int len) {
if (cl != chat_window_client) {
rfbLog("invalid client during chat_helper login\n");
return FALSE;
} else {
if (!cl->host) {
rfbLog("empty cl->host during chat_helper login\n");
return FALSE;
}
if (strcmp(cl->host, "127.0.0.1")) {
rfbLog("invalid cl->host during chat_helper login: %s\n", cl->host);
return FALSE;
}
rfbLog("chat_helper login accepted\n");
return TRUE;
}
}
enum rfbNewClientAction new_client_chat_helper(rfbClientPtr client) {
client->clientGoneHook = client_gone_chat_helper;
rfbLog("new chat helper\n");
return(RFB_CLIENT_ACCEPT);
}
void client_gone_chat_helper(rfbClientPtr client) {
rfbLog("finished chat helper\n");
chat_window_client = NULL;
}
/*
* libvncserver callback for when a new client connects
*/
@ -3119,11 +3156,11 @@ void send_client_info(char *str) {
buf += n;
len -= n;
continue;
}
}
if (n < 0 && errno == EINTR) {
continue;
}
}
close(sock);
icon_mode_socks[i] = -1;
break;

@ -19,6 +19,7 @@ extern void set_client_input(char *str);
extern void set_child_info(void);
extern int cmd_ok(char *cmd);
extern void client_gone(rfbClientPtr client);
extern void client_gone_chat_helper(rfbClientPtr client);
extern void reverse_connect(char *str);
extern void set_vnc_connect_prop(char *str);
extern void read_vnc_connect_prop(int);
@ -27,6 +28,8 @@ extern void read_x11vnc_remote_prop(int);
extern void check_connect_inputs(void);
extern void check_gui_inputs(void);
extern enum rfbNewClientAction new_client(rfbClientPtr client);
extern enum rfbNewClientAction new_client_chat_helper(rfbClientPtr client);
extern rfbBool password_check_chat_helper(rfbClientPtr cl, const char* response, int len);
extern void start_client_info_sock(char *host_port_cookie);
extern void send_client_info(char *str);
extern void adjust_grabs(int grab, int quiet);

@ -555,6 +555,7 @@ static void setup_cursors(void) {
rfbCursorPtr rfb_curs;
char *scale = NULL;
int i, j, n = 0;
int w_in = 0, h_in = 0;
static int first = 1;
if (verbose) {
@ -689,17 +690,27 @@ static void setup_cursors(void) {
} else if (scaling && scale_str) {
scale = scale_str;
}
if (scale && sscanf(scale, "%dx%d", &i, &j) == 2) {
if (wdpy_x > 0) {
w_in = wdpy_x;
h_in = wdpy_y;
} else {
w_in = dpy_x;
h_in = dpy_y;
}
}
/* scale = NULL zeroes everything */
parse_scale_string(scale, &scale_cursor_fac, &scaling_cursor,
parse_scale_string(scale, &scale_cursor_fac_x, &scale_cursor_fac_y, &scaling_cursor,
&scaling_cursor_blend, &j, &j, &scaling_cursor_interpolate,
&scale_cursor_numer, &scale_cursor_denom);
&scale_cursor_numer, &scale_cursor_denom, w_in, h_in);
for (i=0; i<n; i++) {
/* create rfbCursors for the special cursors: */
cursor_info_t *ci = cursors[i];
if (scaling_cursor && scale_cursor_fac != 1.0) {
if (scaling_cursor && (scale_cursor_fac_x != 1.0 || scale_cursor_fac_y != 1.0)) {
int w, h, x, y, k;
unsigned long *pixels;
@ -982,7 +993,7 @@ static rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h,
}
}
if (scaling_cursor && scale_cursor_fac != 1.0) {
if (scaling_cursor && (scale_cursor_fac_x != 1.0 || scale_cursor_fac_y != 1.0)) {
int W, H;
char *pixels_use = (char *) pixels;
unsigned int *pixels32 = NULL;
@ -990,8 +1001,8 @@ static rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h,
W = w;
H = h;
w = scale_round(W, scale_cursor_fac);
h = scale_round(H, scale_cursor_fac);
w = scale_round(W, scale_cursor_fac_x);
h = scale_round(H, scale_cursor_fac_y);
pixels_new = (char *) malloc(4*w*h);
@ -1011,7 +1022,7 @@ static rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h,
pixels_use = (char *) pixels32;
}
scale_rect(scale_cursor_fac, scaling_cursor_blend,
scale_rect(scale_cursor_fac_x, scale_cursor_fac_y, scaling_cursor_blend,
scaling_cursor_interpolate,
4, pixels_use, 4*W, pixels_new, 4*w,
W, H, w, h, 0, 0, W, H, 0);
@ -1040,8 +1051,8 @@ static rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h,
pixels = (unsigned long *) pixels_new;
xhot = scale_round(xhot, scale_cursor_fac);
yhot = scale_round(yhot, scale_cursor_fac);
xhot = scale_round(xhot, scale_cursor_fac_x);
yhot = scale_round(yhot, scale_cursor_fac_y);
}
len = w * h;

@ -9,6 +9,7 @@
/*
* ultravnc_dsm_helper.c unix/openssl UltraVNC encryption encoder/decoder.
* (also a generic symmetric encryption tunnel)
*
* compile via:
@ -30,6 +31,7 @@
* any-client <=> ultravnc_dsm_helper <--network--> ultravnc_dsm_helper(reverse mode) <=> any-server
*
* e.g. to connect a non-ultra-dsm-vnc viewer to a non-ultra-dsm-vnc server
* without using SSH or SSL.
*
* -----------------------------------------------------------------------
* Copyright (c) 2008 Karl J. Runge <runge@karlrunge.com>
@ -58,7 +60,7 @@ static char *usage =
"e.g.: ultravnc_dsm_helper arc4 ./arc4.key 5901 snoopy.com:5900\n"
"\n"
" cipher: specify 'msrc4', 'msrc4_sc', 'arc4', 'aesv2',\n"
" 'aes-cfb', 'blowfish', or '3des'.\n"
" 'aes-cfb', 'aes256', 'blowfish', or '3des'.\n"
"\n"
" 'msrc4_sc' enables a workaround for UVNC SC -plugin use.\n"
"\n"
@ -84,7 +86,11 @@ static char *usage =
"\n"
"\n"
" Also: cipher may be cipher@n,m where n is the salt size and m is the\n"
" initialization vector size. E.g. aesv2@8,16\n"
" initialization vector size. E.g. aesv2@8,16 Use n=-1 to disable salt\n"
" and the MD5 hash (i.e. insert the keydata directly into the cipher.)\n"
"\n"
" Use cipher@md+n,m to change the message digest. E.g. arc4@sha+8,16\n"
" Supported: 'md5', 'sha', 'sha1', 'ripemd160'.\n"
;
/*
@ -93,8 +99,8 @@ static char *usage =
*
* Note that when running as a module we still assume we have been
* forked off of the parent process and are communicating back to it
* via a socket. So we still exit(3) at the end or on error. And
* the globals would not work.
* via a socket. So we *still* exit(3) at the end or on error. And
* the global settings won't work.
*/
#ifdef ENC_MODULE
# define main __enc_main
@ -124,21 +130,25 @@ static char *prog = "ultravnc_dsm_helper";
#include <netdb.h>
/* Solaris (sysv?) needs INADDR_NONE */
#ifndef INADDR_NONE
#define INADDR_NONE ((in_addr_t) 0xffffffff)
#endif
#if ENC_HAVE_OPENSSL
/* openssl includes */
#if ENC_HAVE_OPENSSL
#include <openssl/evp.h>
#include <openssl/rand.h>
static const EVP_CIPHER *Cipher;
static const EVP_MD *Digest;
#endif
static char *cipher = NULL; /* name of cipher, e.g. "aesv2" */
static int reverse = 0; /* listening connection */
static int msrc4_sc = 0; /* enables workaround for SC I/II */
static int noultra = 0; /* manage salt and iv differently than ultradsm */
static int noultra = 0; /* manage salt/iv differently from ultradsm */
static int nomd = 0; /* use the keydata directly, no md5 or salt */
static int pw_in = 0; /* pw=.... read in */
/* The data that was read in from key file (or pw=password) */
@ -157,6 +167,7 @@ static int ivec_size = IVEC;
/* To track parent and child pids */
static pid_t parent, child;
/* transfer buffer size */
#define BSIZE 8192
/* Some very verbose debugging stuff I enable for testing */
@ -170,6 +181,9 @@ static pid_t parent, child;
# define PRINT_IVEC
# define PRINT_KEYDATA
# define PRINT_KEYSTR_AND_FRIENDS
# define PRINT_LOOP_DBG1
# define PRINT_LOOP_DBG2
# define PRINT_LOOP_DBG3
#endif
static void enc_connections(int, char*, int);
@ -185,6 +199,15 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
#else
#if defined(NO_EVP_aes_256_cfb) || (defined (__SVR4) && defined (__sun) && !defined(EVP_aes_256_cfb) && !defined(ASSUME_EVP_aes_256_cfb))
/*
* For Solaris 10 missing 192 & 256 bit crypto.
* Note that EVP_aes_256_cfb is a macro.
*/
#undef EVP_aes_256_cfb
#define EVP_aes_256_cfb() EVP_aes_128_cfb(); {fprintf(stderr, "Not compiled with EVP_aes_256_cfb() 'aes256' support.\n"); exit(1);}
#endif
/* If we are a module, enc_do() is the only interface we export. */
@ -197,8 +220,9 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
char tmp[16];
int fd, len, listen_port, connect_port, mbits;
/* check for noultra mode: */
q = ciph;
/* check for noultra mode: */
if (strstr(q, "noultra:") == q) {
noultra = 1;
q += strlen("noultra:");
@ -227,13 +251,16 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
} else if (strstr(q, "aes-cfb") == q) {
Cipher = EVP_aes_128_cfb(); cipher = "aes-cfb";
} else if (strstr(q, "aes256") == q) {
Cipher = EVP_aes_256_cfb(); cipher = "aes256";
} else if (strstr(q, "blowfish") == q) {
Cipher = EVP_bf_cfb(); cipher = "blowfish";
} else if (strstr(q, "3des") == q) {
Cipher = EVP_des_ede3_ofb(); cipher = "3des";
Cipher = EVP_des_ede3_cfb(); cipher = "3des";
} else {
} else if (strstr(q, ".") == q) {
/* otherwise, try to guess cipher from key filename: */
if (strstr(keyfile, "arc4.key")) {
Cipher = EVP_rc4(); cipher = "arc4";
@ -247,34 +274,77 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
} else if (strstr(keyfile, "aes-cfb.key")) {
Cipher = EVP_aes_128_cfb(); cipher = "aes-cfb";
} else if (strstr(keyfile, "aes256.key")) {
Cipher = EVP_aes_256_cfb(); cipher = "aes256";
} else if (strstr(keyfile, "blowfish.key")) {
Cipher = EVP_bf_cfb(); cipher = "blowfish";
} else if (strstr(keyfile, "3des.key")) {
Cipher = EVP_des_ede3_ofb(); cipher = "3des";
Cipher = EVP_des_ede3_cfb(); cipher = "3des";
} else {
fprintf(stderr, "cannot figure out cipher, supply 'msrc4', 'arc4', or 'aesv2' ...\n");
exit(1);
}
} else {
fprintf(stderr, "cannot figure out cipher, supply 'msrc4', 'arc4', or 'aesv2' ...\n");
exit(1);
}
/* look for user specified salt and IV sizes at the end: */
/* set the default message digest (md5) */
Digest = EVP_md5();
/*
* Look for user specified salt and IV sizes at the end
* ( ciph@salt,iv and ciph@[md+]salt,iv ):
*/
p = strchr(q, '@');
if (p) {
int s, v;
if (sscanf(p+1, "%d,%d", &s, &v) == 2) {
if (0 <= s && s <= SALT) {
p++;
if (strstr(p, "md5+") == p) {
Digest = EVP_md5(); p += strlen("md5+");
} else if (strstr(p, "sha+") == p) {
Digest = EVP_sha(); p += strlen("sha+");
} else if (strstr(p, "sha1+") == p) {
Digest = EVP_sha1(); p += strlen("sha1+");
} else if (strstr(p, "ripe+") == p) {
Digest = EVP_ripemd160(); p += strlen("ripe+");
} else if (strstr(p, "ripemd160+") == p) {
Digest = EVP_ripemd160(); p += strlen("ripemd160+");
}
if (sscanf(p, "%d,%d", &s, &v) == 2) {
/* cipher@n,m */
if (-1 <= s && s <= SALT) {
salt_size = s;
} else {
fprintf(stderr, "%s: invalid salt size: %d\n",
prog, s);
exit(1);
}
if (0 <= v && v <= EVP_MAX_IV_LENGTH) {
ivec_size = v;
} else {
fprintf(stderr, "%s: invalid IV size: %d\n",
prog, v);
exit(1);
}
} else if (sscanf(p+1, "%d", &s) == 1) {
if (0 <= s && s <= SALT) {
} else if (sscanf(p, "%d", &s) == 1) {
/* cipher@n */
if (-1 <= s && s <= SALT) {
salt_size = s;
} else {
fprintf(stderr, "%s: invalid salt size: %d\n",
prog, s);
exit(1);
}
}
if (salt_size == -1) {
/* let salt = -1 mean skip both MD5 and salt */
nomd = 1;
salt_size = 0;
}
}
/* port to listen on (0 => stdio, negative => localhost) */
@ -292,20 +362,24 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
connect_host = strdup(rhp);
/* check for and read in the key file */
memset(keydata, 0, sizeof(keydata));
if (stat(keyfile, &sb) != 0) {
if (strstr(keyfile, "pw=") == keyfile) {
/* user specified key/password on cmdline */
int i;
len = 0;
pw_in = 1;
for (i=0; i < strlen(keyfile); i++) {
/* load the string to keydata: */
int n = i + strlen("pw=");
keydata[i] = keyfile[n];
if (keyfile[n] == '\0') break;
len++;
if (i > 100) break;
}
goto readed_in;
}
/* otherwise invalid file */
perror("stat");
exit(1);
}
@ -330,6 +404,7 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
readed_in:
/* check for ultravnc msrc4 format 'rc4.key' */
mbits = 0;
if (strstr(keydata, "128 bit") == keydata) {
@ -373,7 +448,6 @@ extern void enc_do(char *ciph, char *keyfile, char *lport, char *rhp) {
* encrypt or decrypt.
*/
static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
/*
* We keep both E and D aspects in case we revert back to a
* single process calling select(2) on all fds...
@ -388,9 +462,9 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
unsigned char salt[SALT+1];
unsigned char ivec[EVP_MAX_IV_LENGTH];
int i, cnt, len, n = 0, m, vb = 0, pa = 1, first = 1;
int i, cnt, len, m, n = 0, vb = 0, pa = 1, first = 1;
int whoops = 1; /* for the msrc4 problem */
char *encstr;
char *encstr, *encsym;
/* zero the buffers */
memset(buf, 0, BSIZE);
@ -404,6 +478,10 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
salt_size = MSRC4_SALT; /* 11 vs. 16 */
}
if (msrc4_sc) {
whoops = 1; /* force workaround in SC mode */
}
if (getenv("ENCRYPT_VERBOSE")) {
vb = 1; /* let user turn on some debugging via env. var. */
}
@ -415,10 +493,7 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
encrypt = (!encrypt);
}
encstr = encrypt ? "encrypt" : "decrypt"; /* string for messages */
if (msrc4_sc) {
whoops = 1;
}
encsym = encrypt ? "+" : "-";
if (encrypt) {
/* encrypter initializes the salt and initialization vector */
@ -426,7 +501,8 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
/*
* Our salt is 16 bytes but I believe only the first 8
* bytes are used by EVP_BytesToKey(3). Since we send it
* to the other "plugin" we need to keep it 16.
* to the other "plugin" we need to keep it 16. Also,
* the IV size can depend on the cipher type. Again, 16.
*/
RAND_bytes(salt, salt_size);
RAND_bytes(ivec, ivec_size);
@ -452,20 +528,25 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
tv.tv_usec = 100 * 1000;
select(1, NULL, NULL, NULL, &tv);
n = read(sock_fr, buf, salt_size+ivec_size+96);
if (salt_size+ivec_size == 0) {
n = 0; /* no salt or iv, skip reading. */
} else {
n = read(sock_fr, buf, salt_size+ivec_size+96);
}
if (n == 0 && salt_size+ivec_size > 0) {
fprintf(stderr, "%s: decrypt finished.\n", prog);
goto finished;
}
if (n < salt_size+ivec_size) {
if (msrc4_sc && n == 12) {
fprintf(stderr, "%s: only %d bytes read. Assuming UVNC Single Click server.\n", prog, n);
} else {
if (n < 0) perror("read");
fprintf(stderr, "%s: could not read enough for salt and ivec: n=%d\n", prog, n);
goto finished;
}
if (msrc4_sc && n == 12) {
fprintf(stderr, "%s: only %d bytes read. Assuming "
"UVNC Single Click server.\n", prog, n);
} else {
if (n < 0) perror("read");
fprintf(stderr, "%s: could not read enough for salt "
"and ivec: n=%d\n", prog, n);
goto finished;
}
}
DEC_CT_DBG(buf, n);
@ -482,7 +563,10 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
psrc = buf + salt_size + ivec_size;
if (n > 0) {
/* copy it down to the start of buf for sending below */
/*
* copy it down to the start of buf for
* sending below:
*/
for (i=0; i < n; i++) {
buf[i] = psrc[i];
}
@ -505,37 +589,74 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
fprintf(stderr, "%s: %s - WARNING: MSRC4 mode and IGNORING random salt\n", prog, encstr);
fprintf(stderr, "%s: %s - WARNING: and initialization vector!!\n", prog, encstr);
EVP_CIPHER_CTX_init(ctx);
EVP_CipherInit_ex(ctx, Cipher, NULL, (unsigned char *) keydata, NULL, encrypt);
if (pw_in) {
/* for pw=xxxx a md5 hash is used */
EVP_BytesToKey(Cipher, Digest, NULL, keydata,
keydata_len, 1, keystr, NULL);
EVP_CipherInit_ex(ctx, Cipher, NULL, keystr, NULL,
encrypt);
} else {
/* otherwise keydata as is */
EVP_CipherInit_ex(ctx, Cipher, NULL,
(unsigned char *) keydata, NULL, encrypt);
}
} else {
/* XXX might not be correct */
exit(1);
EVP_BytesToKey(Cipher, EVP_md5(), NULL, keydata, keydata_len, 1, keystr, ivec);
EVP_BytesToKey(Cipher, Digest, NULL, keydata,
keydata_len, 1, keystr, ivec);
EVP_CIPHER_CTX_init(ctx);
EVP_CipherInit_ex(ctx, Cipher, NULL, keystr, ivec, encrypt);
EVP_CipherInit_ex(ctx, Cipher, NULL, keystr, ivec,
encrypt);
}
} else {
unsigned char *in_salt;
/* check salt and IV source and size. */
if (salt_size <= 0) {
/* let salt_size = 0 mean keep it out of the MD5 */
fprintf(stderr, "%s: %s - WARNING: no salt\n", prog, encstr);
fprintf(stderr, "%s: %s - WARNING: no salt\n",
prog, encstr);
in_salt = NULL;
} else {
in_salt = salt;
}
if (ivec_size < Cipher->iv_len) {
fprintf(stderr, "%s: %s - WARNING: short IV %d < %d\n", prog, encstr, ivec_size, Cipher->iv_len);
fprintf(stderr, "%s: %s - WARNING: short IV %d < %d\n",
prog, encstr, ivec_size, Cipher->iv_len);
}
/* make the hashed value and place in keystr */
/* XXX N.B.: DSM plugin had count=0, and overwrote ivec by not passing NULL iv */
/*
* XXX N.B.: DSM plugin had count=0, and overwrote ivec
* by not passing NULL iv.
*/
if (nomd) {
/* special mode: no salt or md5, use keydata directly */
int sz = keydata_len < EVP_MAX_KEY_LENGTH ?
keydata_len : EVP_MAX_KEY_LENGTH;
fprintf(stderr, "%s: %s - WARNING: no-md5 specified: ignoring salt & hash\n", prog, encstr);
memcpy(keystr, keydata, sz);
} else if (noultra && ivec_size > 0) {
/* "normal" mode, don't overwrite ivec. */
EVP_BytesToKey(Cipher, Digest, in_salt, keydata,
keydata_len, 1, keystr, NULL);
if (noultra && ivec_size > 0) {
EVP_BytesToKey(Cipher, EVP_md5(), in_salt, keydata, keydata_len, 1, keystr, NULL);
} else {
/* even under noultra we overwrite ivec if ivec_size = 0 */
EVP_BytesToKey(Cipher, EVP_md5(), in_salt, keydata, keydata_len, 1, keystr, ivec);
/*
* Ultra DSM compatibility mode. Note that this
* clobbers the ivec we set up above! Under
* noultra we overwrite ivec only if ivec_size=0.
*/
EVP_BytesToKey(Cipher, Digest, in_salt, keydata,
keydata_len, 1, keystr, ivec);
}
@ -545,7 +666,10 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
/* set the cipher & initialize */
/* XXX N.B.: DSM plugin had encrypt=1 for both (i.e. perfectly symmetric) */
/*
* XXX N.B.: DSM plugin had encrypt=1 for both
* (i.e. perfectly symmetric)
*/
EVP_CipherInit_ex(ctx, Cipher, NULL, keystr, ivec, encrypt);
}
@ -562,27 +686,28 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
/* skip sending salt+iv */
first = 0;
continue;
} else {
/* use that first block of data placed in buf */
}
/* use that first block of data placed in buf above */
} else if (first && n == 0 && salt_size + ivec_size == 0) {
first = 0;
continue;
} else {
/* general case of loop, read some in: */
n = read(sock_fr, buf, BSIZE);
}
/* debug output: */
if (vb) fprintf(stderr, "%s%d/%d ", encrypt ? "+" : "-", n, errno);
if (n <= 0) {} else if (encrypt) {ENC_PT_DBG(buf, n);} else {DEC_CT_DBG(buf, n);}
if (vb) fprintf(stderr, "%s%d/%d ", encsym, n, errno);
PRINT_LOOP_DBG1;
if (n == 0 || (n < 0 && errno != EINTR)) {
/* failure to read any data... it is EOF or fatal error. */
/* failure to read any data, it is EOF or fatal error */
int err = errno;
/* debug output: */
char tmp[32]; int err = errno;
if (encrypt) {ENC_PT_DBG("--EOF--", 7);} else {DEC_CT_DBG("--EOF--", 7);}
sprintf(tmp, "err=%d,n=%d", err, n);
PRINT_LOOP_DBG2;
fprintf(stderr, "%s: %s - input stream finished: n=%d, err=%d", prog, encstr, n, err);
if (encrypt) {ENC_PT_DBG(tmp, strlen(tmp));} else {DEC_CT_DBG(tmp, strlen(tmp));}
/* EOF or fatal error */
break;
@ -602,8 +727,8 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
}
/* debug output: */
if (vb) fprintf(stderr, "c%d/%d ", cnt, n);
if (encrypt) {ENC_CT_DBG(out, cnt);} else {DEC_PT_DBG(out, cnt);}
if (vb) fprintf(stderr, "%sc%d/%d ", encsym, cnt, n);
PRINT_LOOP_DBG3;
/* write transformed data to the other end: */
len = cnt;
@ -613,7 +738,7 @@ static void enc_xfer(int sock_fr, int sock_to, int encrypt) {
m = write(sock_to, psrc, len);
/* debug output: */
if (vb) fprintf(stderr, "m%s%d/%d ", encrypt ? "+" : "-", m, errno);
if (vb) fprintf(stderr, "m%s%d/%d ", encsym, m, errno);
if (m > 0) {
/* scoot them by how much was written: */
@ -697,7 +822,8 @@ static void enc_connections(int listen_port, char *connect_host, int connect_por
exit(1);
}
ret = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
ret = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR,
(char *)&one, sizeof(one));
if (ret < 0) {
perror("setsockopt");
exit(1);
@ -715,7 +841,8 @@ static void enc_connections(int listen_port, char *connect_host, int connect_por
exit(1);
}
fprintf(stderr, "%s: waiting for connection on port: %d\n", prog, listen_port);
fprintf(stderr, "%s: waiting for connection on port: %d\n",
prog, listen_port);
/* wait for a connection: */
clen = sizeof(client);
@ -769,6 +896,8 @@ static void enc_connections(int listen_port, char *connect_host, int connect_por
if (child == (pid_t) -1) {
/* couldn't fork... */
perror("fork");
close(conn1);
close(conn2);
exit(1);
}
@ -797,7 +926,7 @@ extern int main (int argc, char *argv[]) {
q = strstr(argv[2], "pw=");
if (q) {
while (*q != '\0') {
*q = '\0';
*q = '\0'; /* now ps(1) won't show it */
q++;
}
}

@ -296,6 +296,10 @@ void print_help(int mode) {
" the notation \"m/n\" may be used to denote fractions\n"
" exactly, e.g. -scale 2/3\n"
"\n"
" To scale asymmetrically in the horizontal and vertical\n"
" directions, specify a WxH geometry to stretch to:\n"
" e.g. '-scale 1024x768', or also '-scale 0.9x0.75'\n"
"\n"
" Scaling Options: can be added after \"fraction\" via\n"
" \":\", to supply multiple \":\" options use commas.\n"
" If you just want a quick, rough scaling without\n"
@ -316,6 +320,8 @@ void print_help(int mode) {
" and height to be multiples of scaling denominator\n"
" (e.g. 3 for 2/3).\n"
"\n"
"-geometry WxH Same as -scale WxH\n"
"\n"
"-scale_cursor frac By default if -scale is supplied the cursor shape is\n"
" scaled by the same factor. Depending on your usage,\n"
" you may want to scale the cursor independently of the\n"
@ -1734,9 +1740,9 @@ void print_help(int mode) {
" choice of implementation).\n"
"\n"
" cipher can be one of: arc4, aesv2, aes-cfb, blowfish,\n"
" or 3des. See the OpenSSL documentation for more info.\n"
" The keysize is 128 bits. Here is one way to make a\n"
" keyfile with that many bits:\n"
" aes256, or 3des. See the OpenSSL documentation for\n"
" more info. The keysize is 128 bits (except for aes256).\n"
" Here is one way to make a keyfile with that many bits:\n"
"\n"
" dd if=/dev/random of=./my.key bs=16 count=1\n"
"\n"
@ -1774,6 +1780,18 @@ void print_help(int mode) {
" although you may be forced to if the other side of the\n"
" tunnel is not under your control.\n"
"\n"
" To skip the salt and EVP_BytesToKey MD5 entirely (no\n"
" hashing is done: the keydata is directly inserted into\n"
" the cipher) specify \"-1\" for the salt, e.g.\n"
"\n"
" -enc blowfish@-1,16:./my.key\n"
"\n"
" The message digest can also be changed to something\n"
" besides the default MD5. Use cipher@md+n,m where \"md\"\n"
" can be one of sha, sha1, md5, or ripe. For example:\n"
"\n"
" -enc arc4@sha+8,16:./my.key\n"
"\n"
" The SSVNC vnc viewer project supplies a symmetric\n"
" encryption tool named \"ultravnc_dsm_helper\" that can\n"
" be used on the viewer side. For example:\n"
@ -1783,14 +1801,20 @@ void print_help(int mode) {
" where h:p is the hostname and port of the x11vnc server.\n"
" ultravnc_dsm_helper may also be used standalone to\n"
" provide a symmetric encryption tunnel for any viewer\n"
" or server (VNC or otherwise.)\n"
" or server (VNC or otherwise.) The cipher (1st arg)\n"
" is basically the same syntax as we use above.\n"
"\n"
" Also see the 'Non-Ultra DSM' SSVNC option for the\n"
" 'UltraVNC DSM Encryption Plugin' advanced option.\n"
"\n"
"-https [port] Choose a separate HTTPS port (-ssl mode only).\n"
" For both ways of using the viewer, you can specify the\n"
" salt,ivec sizes (in GUI or, e.g. arc4@8,16).\n"
"\n"
" In -ssl mode, it turns out you can use the\n"
"-https [port] Use a special, separate HTTPS port (-ssl mode only)\n"
" for HTTPS Java viewer applet downloading. I.e. not 5900\n"
" and not 5800 (the defaults.)\n"
"\n"
" BACKGROUND: In -ssl mode, it turns out you can use the\n"
" single VNC port (e.g. 5900) for both VNC and HTTPS\n"
" connections. (HTTPS is used to retrieve a SSL-aware\n"
" VncViewer.jar applet that is provided with x11vnc).\n"
@ -1808,14 +1832,15 @@ void print_help(int mode) {
" or VNC Viewer applet. That's right 3 separate \"Are\n"
" you sure you want to connect?\" dialogs!)\n"
"\n"
" So use the -https option to provide a separate, more\n"
" reliable HTTPS port that x11vnc will listen on. If\n"
" USAGE: So use the -https option to provide a separate,\n"
" more reliable HTTPS port that x11vnc will listen on. If\n"
" [port] is not provided (or is 0), one is autoselected.\n"
" The URL to use is printed out at startup.\n"
"\n"
" The SSL Java applet directory is specified via the\n"
" -httpdir option. If not supplied it will try to guess\n"
" the directory as though the -http option was supplied.\n"
" -httpdir option. If not supplied, -https will try\n"
" to guess the directory as though the -http option\n"
" was supplied.\n"
"\n"
"-httpsredir [port] In -ssl mode with the Java applet retrieved via HTTPS,\n"
" when the HTML file containing applet parameters\n"
@ -3261,6 +3286,25 @@ void print_help(int mode) {
" and ServerInput. The others managed by libvncserver\n"
" (textchat, 1/n scaling, rfbEncodingUltra) are not.\n"
"\n"
"-chatwindow Place a local UltraVNC chat window on the X11 display\n"
" that x11vnc is polling. That way the person on the VNC\n"
" viewer-side can chat with the person at the physical\n"
" X11 console. (e.g. helpdesk w/o telephone)\n"
"\n"
" For this to work the SSVNC package (version 1.0.21 or\n"
" later) MUST BE installed on the system where x11vnc runs\n"
" and the 'ssvnc' command must be available in $PATH.\n"
" The ssvncviewer is used as a chat window helper.\n"
" See http://www.karlrunge.com/x11vnc/ssvnc.html\n"
"\n"
" This option implies '-rfbversion 3.6' so as to trick\n"
" UltraVNC viewers, otherwise they assume chat is not\n"
" available. To specify a different rfbversion, place\n"
" it after the -chatwindow option on the cmdline.\n"
"\n"
" See also the remote control 'chaton' and 'chatoff'\n"
" actions. These can also be set from the tkx11vnc GUI.\n"
"\n"
"-noxdamage Do not use the X DAMAGE extension to detect framebuffer\n"
" changes even if it is available. Use -xdamage if your\n"
" default is to have it off.\n"
@ -4158,6 +4202,10 @@ void print_help(int mode) {
" serverdpms disable -noserverdpms mode.\n"
" noultraext enable -noultraext mode.\n"
" ultraext disable -noultraext mode.\n"
" chatwindow enable local chatwindow mode.\n"
" nochatwindow disable local chatwindow mode.\n"
" chaton begin chat using local window.\n"
" chatoff end chat using local window.\n"
" xdamage enable xdamage polling hints.\n"
" noxdamage disable xdamage polling hints.\n"
" xd_area:A set -xd_area max pixel area to \"A\"\n"
@ -4293,16 +4341,17 @@ void print_help(int mode) {
" nowfl wirecopyrect wcr nowirecopyrect nowcr scr_area\n"
" scr_skip scr_inc scr_keys scr_term scr_keyrepeat\n"
" scr_parms scrollcopyrect scr noscrollcopyrect noscr\n"
" fixscreen noxrecord xrecord reset_record pointer_mode pm\n"
" input_skip allinput noallinput input grabkbd nograbkbd\n"
" grabptr nograbptr grabalways nograbalways grablocal\n"
" client_input ssltimeout speeds wmdt debug_pointer dp\n"
" nodebug_pointer nodp debug_keyboard dk nodebug_keyboard\n"
" nodk keycode deferupdate defer wait_ui wait_bog\n"
" nowait_bog slow_fb xrefresh wait readtimeout nap nonap\n"
" sb screen_blank fbpm nofbpm dpms nodpms clientdpms\n"
" noclientdpms forcedpms noforcedpms noserverdpms\n"
" serverdpms noultraext ultraext fs gaps grow fuzz snapfb\n"
" fixscreen noxrecord xrecord reset_record pointer_mode\n"
" pm input_skip allinput noallinput input grabkbd\n"
" nograbkbd grabptr nograbptr grabalways nograbalways\n"
" grablocal client_input ssltimeout speeds wmdt\n"
" debug_pointer dp nodebug_pointer nodp debug_keyboard\n"
" dk nodebug_keyboard nodk keycode deferupdate defer\n"
" wait_ui wait_bog nowait_bog slow_fb xrefresh wait\n"
" readtimeout nap nonap sb screen_blank fbpm nofbpm dpms\n"
" nodpms clientdpms noclientdpms forcedpms noforcedpms\n"
" noserverdpms serverdpms noultraext ultraext chatwindow\n"
" nochatwindow chaton chatoff fs gaps grow fuzz snapfb\n"
" nosnapfb rawfb uinput_accel uinput_thresh uinput_reset\n"
" uinput_always progressive rfbport http nohttp httpport\n"
" httpdir enablehttpproxy noenablehttpproxy alwaysshared\n"
@ -4319,9 +4368,9 @@ void print_help(int mode) {
" macnomenu nomacmenu macuskbd nomacuskbd noremote\n"
"\n"
" aro= noop display vncdisplay desktopname guess_desktop\n"
" http_url auth xauth users rootshift clipshift\n"
" scale_str scaled_x scaled_y scale_numer scale_denom\n"
" scale_fac scaling_blend scaling_nomult4 scaling_pad\n"
" http_url auth xauth users rootshift clipshift scale_str\n"
" scaled_x scaled_y scale_numer scale_denom scale_fac_x\n"
" scale_fac_y scaling_blend scaling_nomult4 scaling_pad\n"
" scaling_interpolate inetd privremote unsafe safer\n"
" nocmds passwdfile unixpw unixpw_nis unixpw_list ssl\n"
" ssl_pem sslverify stunnel stunnel_pem https httpsredir\n"

@ -2700,7 +2700,7 @@ static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym,
X_LOCK;
XTestFakeKeyEvent_wr(dpy, k, (Bool) down, CurrentTime);
X_UNLOCK;
}
}
if ( tweak ) {
tweak_mod(modifiers[keysym], False);

@ -97,7 +97,7 @@ char *console_guess(char *str, int *fd) {
if (sscanf(in, "console%d", &n) != 1) {
tty = n;
have_uinput = 0;
}
}
}
if (! atparms) {

@ -388,6 +388,8 @@ int no_ultra_dpms = 0;
int no_ultra_ext = 0;
int saw_ultra_chat = 0;
int saw_ultra_file = 0;
int chat_window = 0;
rfbClientPtr chat_window_client = NULL;
int watch_selection = 1; /* normal selection/cutbuffer maintenance */
int watch_primary = 1; /* more dicey, poll for changes in PRIMARY */

@ -287,6 +287,8 @@ extern int no_ultra_dpms;
extern int no_ultra_ext;
extern int saw_ultra_chat;
extern int saw_ultra_file;
extern int chat_window;
extern rfbClientPtr chat_window_client;
extern int watch_selection;
extern int watch_primary;

File diff suppressed because it is too large Load Diff

@ -24,7 +24,7 @@ void free_tiles(void);
void shm_delete(XShmSegmentInfo *shm);
void shm_clean(XShmSegmentInfo *shm, XImage *xim);
void initialize_polling_images(void);
void scale_rect(double factor, int blend, int interpolate, int Bpp,
void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);
void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);
@ -428,7 +428,7 @@ void initialize_polling_images(void) {
if (! shm_create(&fullscreen_shm, &fullscreen, dpy_x,
dpy_y/fs_factor, "fullscreen")) {
clean_up_exit(1);
}
}
}
if (use_snapfb) {
if (! fs_factor) {
@ -437,7 +437,7 @@ void initialize_polling_images(void) {
} else if (! shm_create(&snaprect_shm, &snaprect, dpy_x,
dpy_y/fs_factor, "snaprect")) {
clean_up_exit(1);
}
}
}
/*
@ -737,7 +737,7 @@ weights for this scaled pixel are:
* the loop over the 4 pixels.
*/
void scale_rect(double factor, int blend, int interpolate, int Bpp,
void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark) {
/*
@ -773,7 +773,7 @@ void scale_rect(double factor, int blend, int interpolate, int Bpp,
int b, k;
double pixave[4]; /* for averaging pixel values */
if (factor <= 1.0) {
if (factor_x <= 1.0 && factor_y <= 1.0) {
shrink = 1;
} else {
shrink = 0;
@ -787,8 +787,8 @@ void scale_rect(double factor, int blend, int interpolate, int Bpp,
* This new way is probably the best we can do, take the inverse
* of the scaling factor to double precision.
*/
dx = 1.0/factor;
dy = 1.0/factor;
dx = 1.0/factor_x;
dy = 1.0/factor_y;
/*
* There is some speedup if the pixel weights are constant, so
@ -797,15 +797,18 @@ void scale_rect(double factor, int blend, int interpolate, int Bpp,
* If scale = 1/n and n divides Nx and Ny, the pixel weights
* are constant (e.g. 1/2 => equal on 2x2 square).
*/
if (factor != last_factor || Nx != last_Nx || Ny != last_Ny) {
if (factor_x != last_factor || Nx != last_Nx || Ny != last_Ny) {
constant_weights = -1;
mag_int = -1;
last_Nx = Nx;
last_Ny = Ny;
last_factor = factor;
last_factor = factor_x;
}
if (constant_weights < 0 && factor_x != factor_y) {
constant_weights = 0;
mag_int = 0;
if (constant_weights < 0) {
} else if (constant_weights < 0) {
int n = 0;
constant_weights = 0;
@ -814,7 +817,7 @@ void scale_rect(double factor, int blend, int interpolate, int Bpp,
for (i = 2; i<=128; i++) {
double test = ((double) 1)/ i;
double diff, eps = 1.0e-7;
diff = factor - test;
diff = factor_x - test;
if (-eps < diff && diff < eps) {
n = i;
break;
@ -839,18 +842,18 @@ void scale_rect(double factor, int blend, int interpolate, int Bpp,
for (i = 2; i<=32; i++) {
double test = (double) i;
double diff, eps = 1.0e-7;
diff = factor - test;
diff = factor_x - test;
if (-eps < diff && diff < eps) {
n = i;
break;
}
}
if (! blend && factor > 1.0 && n) {
if (! blend && factor_x > 1.0 && n) {
mag_int = n;
}
}
if (mark && factor > 1.0 && blend) {
if (mark && factor_x > 1.0 && blend) {
/*
* kludge: correct for interpolating blurring leaking
* up or left 1 destination pixel.
@ -1296,7 +1299,7 @@ void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark) {
dst_fb = rfb_fb;
dst_bpl = rfb_bytes_per_line;
scale_rect(scale_fac, scaling_blend, scaling_interpolate, fac * Bpp,
scale_rect(scale_fac_x, scale_fac_y, scaling_blend, scaling_interpolate, fac * Bpp,
src_fb, fac * main_bytes_per_line, dst_fb, dst_bpl, dpy_x, dpy_y,
scaled_x, scaled_y, X1, Y1, X2, Y2, mark);
}
@ -2623,7 +2626,7 @@ static void nap_set(int tile_cnt) {
if (! nap_ok && client_count) {
if(now > last_fb_bytes_sent + no_fbu_blank) {
if (debug_tiles > 1) {
printf("nap_set: nap_ok=1: now: %d last: %d\n",
fprintf(stderr, "nap_set: nap_ok=1: now: %d last: %d\n",
(int) now, (int) last_fb_bytes_sent);
}
nap_ok = 1;
@ -2683,10 +2686,16 @@ static void nap_check(int tile_cnt) {
dt_fbu = (int) (now - last_fb_bytes_sent);
if (dt_fbu > screen_blank) {
/* sleep longer for no fb requests */
if (debug_tiles > 1) {
fprintf(stderr, "screen blank sleep1: %d ms / 16\n", 2 * ms);
}
nap_sleep(2 * ms, 16);
return;
}
if (dt_ev > screen_blank) {
if (debug_tiles > 1) {
fprintf(stderr, "screen blank sleep2: %d ms / 8\n", ms);
}
nap_sleep(ms, 8);
return;
}
@ -2699,6 +2708,9 @@ static void nap_check(int tile_cnt) {
} else if (now - last_local_input <= 3) {
nap_ok = 0;
} else {
if (debug_tiles > 1) {
fprintf(stderr, "nap_check sleep: %d ms / 1\n", ms);
}
nap_sleep(ms, 1);
}
}

@ -11,7 +11,7 @@ extern void free_tiles(void);
extern void shm_delete(XShmSegmentInfo *shm);
extern void shm_clean(XShmSegmentInfo *shm, XImage *xim);
extern void initialize_polling_images(void);
extern void scale_rect(double factor, int blend, int interpolate, int Bpp,
extern void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);
extern void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

@ -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</