summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvi Kivity <avi@redhat.com>2010-08-01 17:29:21 +0300
committerAvi Kivity <avi@redhat.com>2010-08-01 17:29:21 +0300
commitab8c54b5a08ed8ad3d7bb930c306785b471b2d7e (patch)
tree7adefa1a3150575571da77f6e382aa8a2a6bdd3b
parentf229c5df0796c02fc39279db24b114cc04757c4a (diff)
parentfd2f659ee3f6f991f4f194f3fde5c9f957fd663d (diff)
Merge commit 'fd2f659ee3f6f991f4f194f3fde5c9f957fd663d' into upstream-merge
* commit 'fd2f659ee3f6f991f4f194f3fde5c9f957fd663d': (58 commits) Update version for 0.13.0-rc0 vnc: better default values for VNC options vnc: tight: split send_sub_rect vnc: tight: fix rgb_prepare_row vnc: add missing lock for vnc_cursor_define() vnc: threaded VNC server qemu-thread: add qemu_mutex/cond_destroy and qemu_mutex_exit vnc: fix tight png memory leak vnc: encapsulate encoding members vnc: tight: stop using qdict for palette stuff vnc: tight: specific zlib level and filters for each compression level vnc: tight add PNG encoding vnc: tight: remove a memleak in send_jpeg_rect() vnc: tight: don't forget do at the last color vnc: rename vnc-encoding-* vnc-enc-* ui: move all ui components in ui/ vnc: add lossy option vnc: JPEG should be disabled if the client don't set tight quality vnc: tight: add JPEG and gradient subencoding with smooth image detection Initial documentation for migration ... Merge 0.13 branch point. Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r--.gitignore1
-rw-r--r--Makefile38
-rw-r--r--Makefile.objs29
-rw-r--r--Makefile.target2
-rw-r--r--VERSION2
-rw-r--r--block/raw-posix.c2
-rwxr-xr-xconfigure85
-rw-r--r--cpu-exec.c15
-rw-r--r--cpus.c58
-rw-r--r--cpus.h2
-rw-r--r--docs/migration.txt303
-rw-r--r--exec-all.h5
-rw-r--r--exec.c4
-rw-r--r--hw/e1000.c29
-rw-r--r--hw/etraxfs_eth.c1
-rw-r--r--hw/hw.h6
-rw-r--r--hw/ide/core.c72
-rw-r--r--hw/ide/pci.c38
-rw-r--r--hw/mips_int.c32
-rw-r--r--hw/pc_piix.c6
-rw-r--r--hw/ppc440_bamboo.c2
-rw-r--r--hw/scsi-bus.c12
-rw-r--r--hw/scsi.h1
-rw-r--r--hw/vhost.c21
-rw-r--r--kvm-all.c2
-rw-r--r--linux-user/main.c2
-rw-r--r--linux-user/signal.c26
-rw-r--r--migration-tcp.c5
-rw-r--r--qemu-barrier.h3
-rw-r--r--qemu-options.hx7
-rw-r--r--qemu-thread.c22
-rw-r--r--qemu-thread.h4
-rw-r--r--savevm.c86
-rw-r--r--slirp/cksum.c20
-rw-r--r--slirp/ip.h40
-rw-r--r--slirp/ip_icmp.h2
-rw-r--r--slirp/ip_input.c2
-rw-r--r--slirp/ip_output.c22
-rw-r--r--slirp/main.h2
-rw-r--r--slirp/misc.h20
-rw-r--r--slirp/slirp.h39
-rw-r--r--slirp/slirp_config.h6
-rw-r--r--slirp/socket.c2
-rw-r--r--slirp/socket.h10
-rw-r--r--slirp/tcp.h14
-rw-r--r--slirp/tcp_input.c4
-rw-r--r--slirp/tcp_output.c10
-rw-r--r--slirp/tcp_subr.c12
-rw-r--r--slirp/tcp_var.h14
-rw-r--r--slirp/tftp.c8
-rw-r--r--slirp/tftp.h14
-rw-r--r--slirp/udp.c8
-rw-r--r--slirp/udp.h10
-rw-r--r--target-i386/translate.c60
-rw-r--r--target-microblaze/translate.c79
-rw-r--r--target-mips/cpu.h2
-rw-r--r--target-mips/helper.h1
-rw-r--r--target-mips/op_helper.c27
-rw-r--r--target-mips/translate.c12
-rw-r--r--tests/sha1.c24
-rw-r--r--ui/cocoa.m (renamed from cocoa.m)0
-rw-r--r--ui/curses.c (renamed from curses.c)0
-rw-r--r--ui/curses_keys.h (renamed from curses_keys.h)0
-rw-r--r--ui/d3des.c (renamed from d3des.c)0
-rw-r--r--ui/d3des.h (renamed from d3des.h)0
-rw-r--r--ui/keymaps.c (renamed from keymaps.c)0
-rw-r--r--ui/keymaps.h (renamed from keymaps.h)0
-rw-r--r--ui/sdl.c (renamed from sdl.c)0
-rw-r--r--ui/sdl_keysym.h (renamed from sdl_keysym.h)0
-rw-r--r--ui/sdl_zoom.c (renamed from sdl_zoom.c)0
-rw-r--r--ui/sdl_zoom.h (renamed from sdl_zoom.h)0
-rw-r--r--ui/sdl_zoom_template.h (renamed from sdl_zoom_template.h)0
-rw-r--r--ui/vnc-auth-sasl.c (renamed from vnc-auth-sasl.c)0
-rw-r--r--ui/vnc-auth-sasl.h (renamed from vnc-auth-sasl.h)0
-rw-r--r--ui/vnc-auth-vencrypt.c (renamed from vnc-auth-vencrypt.c)0
-rw-r--r--ui/vnc-auth-vencrypt.h (renamed from vnc-auth-vencrypt.h)0
-rw-r--r--ui/vnc-enc-hextile-template.h (renamed from vnchextile.h)0
-rw-r--r--ui/vnc-enc-hextile.c (renamed from vnc-encoding-hextile.c)26
-rw-r--r--ui/vnc-enc-tight.c1715
-rw-r--r--ui/vnc-enc-tight.h (renamed from vnc-encoding-tight.h)21
-rw-r--r--ui/vnc-enc-zlib.c (renamed from vnc-encoding-zlib.c)34
-rw-r--r--ui/vnc-jobs-async.c331
-rw-r--r--ui/vnc-jobs-sync.c73
-rw-r--r--ui/vnc-jobs.h87
-rw-r--r--ui/vnc-palette.c136
-rw-r--r--ui/vnc-palette.h63
-rw-r--r--ui/vnc-tls.c (renamed from vnc-tls.c)0
-rw-r--r--ui/vnc-tls.h (renamed from vnc-tls.h)0
-rw-r--r--ui/vnc.c (renamed from vnc.c)167
-rw-r--r--ui/vnc.h (renamed from vnc.h)115
-rw-r--r--ui/vnc_keysym.h (renamed from vnc_keysym.h)0
-rw-r--r--ui/x_keymap.c (renamed from x_keymap.c)0
-rw-r--r--ui/x_keymap.h (renamed from x_keymap.h)0
-rw-r--r--vl.c2
-rw-r--r--vnc-encoding-tight.c959
95 files changed, 3630 insertions, 1486 deletions
diff --git a/.gitignore b/.gitignore
index 26eba2050..c6466df6e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,3 +55,4 @@ pc-bios/optionrom/multiboot.raw
pc-bios/optionrom/extboot.bin
pc-bios/optionrom/vapic.bin
.stgit-*
+cscope.*
diff --git a/Makefile b/Makefile
index 104dfc799..3cd07e013 100644
--- a/Makefile
+++ b/Makefile
@@ -96,42 +96,14 @@ audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
QEMU_CFLAGS+=$(CURL_CFLAGS)
-cocoa.o: cocoa.m
+ui/cocoa.o: ui/cocoa.m
-keymaps.o: keymaps.c keymaps.h
+ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
-sdl_zoom.o: sdl_zoom.c sdl_zoom.h sdl_zoom_template.h
-
-sdl.o: sdl.c keymaps.h sdl_keysym.h sdl_zoom.h
-
-sdl.o audio/sdlaudio.o sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
-
-acl.o: acl.h acl.c
-
-vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h
-
-vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h acl.h
-
-vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
-
-vnc-tls.o: vnc-tls.c vnc.h
-
-vnc-auth-vencrypt.o: vnc-auth-vencrypt.c vnc.h
-
-vnc-auth-sasl.o: vnc-auth-sasl.c vnc.h
-
-vnc-encoding-zlib.o: vnc-encoding-zlib.c vnc.h
-
-vnc-encoding-hextile.o: vnc-encoding-hextile.c vnc.h
-
-vnc-encoding-tight.o: vnc-encoding-tight.c vnc.h vnc-encoding-tight.h
-
-curses.o: curses.c keymaps.h curses_keys.h
+ui/vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS)
-iov.o: iov.c iov.h
-
######################################################################
qemu-img.o: qemu-img-cmds.h
@@ -159,7 +131,7 @@ clean:
# avoid old build problems by removing potentially incorrect old files
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
rm -f *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~
- rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d
+ rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d
rm -f qemu-img-cmds.h
$(MAKE) -C tests clean
for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \
@@ -353,4 +325,4 @@ tarbin:
$(mandir)/man8/qemu-nbd.8
# Include automatically generated dependency files
--include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d)
+-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d)
diff --git a/Makefile.objs b/Makefile.objs
index cc067063b..dbee21020 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -103,17 +103,24 @@ audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
audio-obj-y += wavcapture.o
common-obj-y += $(addprefix audio/, $(audio-obj-y))
-common-obj-y += keymaps.o
-common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
-common-obj-$(CONFIG_CURSES) += curses.o
-common-obj-y += vnc.o acl.o d3des.o
-common-obj-y += vnc-encoding-zlib.o vnc-encoding-hextile.o
-common-obj-y += vnc-encoding-tight.o
-common-obj-y += iov.o
-common-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
-common-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
-common-obj-$(CONFIG_COCOA) += cocoa.o
-common-obj-$(CONFIG_IOTHREAD) += qemu-thread.o
+ui-obj-y += keymaps.o
+ui-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
+ui-obj-$(CONFIG_CURSES) += curses.o
+ui-obj-y += vnc.o d3des.o
+ui-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
+ui-obj-y += vnc-enc-tight.o vnc-palette.o
+ui-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
+ui-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
+ui-obj-$(CONFIG_COCOA) += cocoa.o
+ifdef CONFIG_VNC_THREAD
+ui-obj-y += vnc-jobs-async.o
+else
+ui-obj-y += vnc-jobs-sync.o
+endif
+common-obj-y += $(addprefix ui/, $(ui-obj-y))
+
+common-obj-y += iov.o acl.o
+common-obj-$(CONFIG_THREAD) += qemu-thread.o
common-obj-y += notify.o event_notifier.o
common-obj-y += qemu-timer.o
diff --git a/Makefile.target b/Makefile.target
index 2c9244863..9e13d99cb 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -192,6 +192,8 @@ LIBS+=-lz
QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
+QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
+QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
# xen backend driver support
obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
diff --git a/VERSION b/VERSION
index d9e30b8d3..bc4cbfa42 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.12.50
+0.12.90
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 3a3cfc050..b72a08256 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -218,7 +218,7 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags)
}
#endif
#ifdef CONFIG_COCOA
- u_int32_t blockSize = 512;
+ uint32_t blockSize = 512;
if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) {
bufsize = blockSize;
}
diff --git a/configure b/configure
index af5060789..b85590fcc 100755
--- a/configure
+++ b/configure
@@ -278,6 +278,9 @@ uuid=""
vde=""
vnc_tls=""
vnc_sasl=""
+vnc_jpeg=""
+vnc_png=""
+vnc_thread="no"
xen=""
linux_aio=""
attr=""
@@ -595,6 +598,18 @@ for opt do
;;
--enable-vnc-sasl) vnc_sasl="yes"
;;
+ --disable-vnc-jpeg) vnc_jpeg="no"
+ ;;
+ --enable-vnc-jpeg) vnc_jpeg="yes"
+ ;;
+ --disable-vnc-png) vnc_png="no"
+ ;;
+ --enable-vnc-png) vnc_png="yes"
+ ;;
+ --disable-vnc-thread) vnc_thread="no"
+ ;;
+ --enable-vnc-thread) vnc_thread="yes"
+ ;;
--disable-slirp) slirp="no"
;;
--disable-uuid) uuid="no"
@@ -855,6 +870,12 @@ echo " --disable-vnc-tls disable TLS encryption for VNC server"
echo " --enable-vnc-tls enable TLS encryption for VNC server"
echo " --disable-vnc-sasl disable SASL encryption for VNC server"
echo " --enable-vnc-sasl enable SASL encryption for VNC server"
+echo " --disable-vnc-jpeg disable JPEG lossy compression for VNC server"
+echo " --enable-vnc-jpeg enable JPEG lossy compression for VNC server"
+echo " --disable-vnc-png disable PNG compression for VNC server (default)"
+echo " --enable-vnc-png enable PNG compression for VNC server"
+echo " --disable-vnc-thread disable threaded VNC server"
+echo " --enable-vnc-thread enable threaded VNC server"
echo " --disable-curses disable curses output"
echo " --enable-curses enable curses output"
echo " --disable-curl disable curl connectivity"
@@ -1281,6 +1302,52 @@ EOF
fi
##########################################
+# VNC JPEG detection
+if test "$vnc_jpeg" != "no" ; then
+cat > $TMPC <<EOF
+#include <stdio.h>
+#include <jpeglib.h>
+int main(void) { struct jpeg_compress_struct s; jpeg_create_compress(&s); return 0; }
+EOF
+ vnc_jpeg_cflags=""
+ vnc_jpeg_libs="-ljpeg"
+ if compile_prog "$vnc_jpeg_cflags" "$vnc_jpeg_libs" ; then
+ vnc_jpeg=yes
+ libs_softmmu="$vnc_jpeg_libs $libs_softmmu"
+ else
+ if test "$vnc_jpeg" = "yes" ; then
+ feature_not_found "vnc-jpeg"
+ fi
+ vnc_jpeg=no
+ fi
+fi
+
+##########################################
+# VNC PNG detection
+if test "$vnc_png" != "no" ; then
+cat > $TMPC <<EOF
+//#include <stdio.h>
+#include <png.h>
+int main(void) {
+ png_structp png_ptr;
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ return 0;
+}
+EOF
+ vnc_png_cflags=""
+ vnc_png_libs="-lpng"
+ if compile_prog "$vnc_png_cflags" "$vnc_png_libs" ; then
+ vnc_png=yes
+ libs_softmmu="$vnc_png_libs $libs_softmmu"
+ else
+ if test "$vnc_png" = "yes" ; then
+ feature_not_found "vnc-png"
+ fi
+ vnc_png=no
+ fi
+fi
+
+##########################################
# fnmatch() probe, used for ACL routines
fnmatch="no"
cat > $TMPC << EOF
@@ -2230,6 +2297,9 @@ echo "Block whitelist $block_drv_whitelist"
echo "Mixer emulation $mixemu"
echo "VNC TLS support $vnc_tls"
echo "VNC SASL support $vnc_sasl"
+echo "VNC JPEG support $vnc_jpeg"
+echo "VNC PNG support $vnc_png"
+echo "VNC thread $vnc_thread"
if test -n "$sparc_cpu"; then
echo "Target Sparc Arch $sparc_cpu"
fi
@@ -2370,6 +2440,18 @@ if test "$vnc_sasl" = "yes" ; then
echo "CONFIG_VNC_SASL=y" >> $config_host_mak
echo "VNC_SASL_CFLAGS=$vnc_sasl_cflags" >> $config_host_mak
fi
+if test "$vnc_jpeg" != "no" ; then
+ echo "CONFIG_VNC_JPEG=y" >> $config_host_mak
+ echo "VNC_JPEG_CFLAGS=$vnc_jpeg_cflags" >> $config_host_mak
+fi
+if test "$vnc_png" != "no" ; then
+ echo "CONFIG_VNC_PNG=y" >> $config_host_mak
+ echo "VNC_PNG_CFLAGS=$vnc_png_cflags" >> $config_host_mak
+fi
+if test "$vnc_thread" != "no" ; then
+ echo "CONFIG_VNC_THREAD=y" >> $config_host_mak
+ echo "CONFIG_THREAD=y" >> $config_host_mak
+fi
if test "$fnmatch" = "yes" ; then
echo "CONFIG_FNMATCH=y" >> $config_host_mak
fi
@@ -2446,6 +2528,7 @@ if test "$xen" = "yes" ; then
fi
if test "$io_thread" = "yes" ; then
echo "CONFIG_IOTHREAD=y" >> $config_host_mak
+ echo "CONFIG_THREAD=y" >> $config_host_mak
fi
if test "$linux_aio" = "yes" ; then
echo "CONFIG_LINUX_AIO=y" >> $config_host_mak
@@ -2987,7 +3070,7 @@ done # for target in $targets
if test "$source_path_used" = "yes" ; then
DIRS="tests tests/cris slirp audio block net pc-bios/optionrom"
DIRS="$DIRS roms/seabios roms/vgabios"
- DIRS="$DIRS fsdev"
+ DIRS="$DIRS fsdev ui"
FILES="Makefile tests/Makefile"
FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit"
FILES="$FILES tests/test-mmap.c"
diff --git a/cpu-exec.c b/cpu-exec.c
index 240bc287d..0a030da7d 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -23,6 +23,7 @@
#include "tcg.h"
#endif
#include "kvm.h"
+#include "qemu-barrier.h"
#if !defined(CONFIG_SOFTMMU)
#undef EAX
@@ -237,12 +238,11 @@ int cpu_exec(CPUState *env1)
use it. */
QEMU_BUILD_BUG_ON (sizeof (saved_env_reg) != sizeof (env));
saved_env_reg = (host_reg_t) env;
- asm("");
+ barrier();
env = env1;
- if (exit_request) {
+ if (unlikely(exit_request)) {
env->exit_request = 1;
- exit_request = 0;
}
#if defined(TARGET_I386)
@@ -606,8 +606,9 @@ int cpu_exec(CPUState *env1)
TB, but before it is linked into a potentially
infinite loop and becomes env->current_tb. Avoid
starting execution if there is a pending interrupt. */
- if (!unlikely (env->exit_request)) {
- env->current_tb = tb;
+ env->current_tb = tb;
+ barrier();
+ if (likely(!env->exit_request)) {
tc_ptr = tb->tc_ptr;
/* execute the generated code */
#if defined(__sparc__) && !defined(CONFIG_SOLARIS)
@@ -616,7 +617,6 @@ int cpu_exec(CPUState *env1)
#define env cpu_single_env
#endif
next_tb = tcg_qemu_tb_exec(tc_ptr);
- env->current_tb = NULL;
if ((next_tb & 3) == 2) {
/* Instruction counter expired. */
int insns_left;
@@ -645,6 +645,7 @@ int cpu_exec(CPUState *env1)
}
}
}
+ env->current_tb = NULL;
/* reset soft MMU for next block (it can currently
only be set by a memory fault) */
} /* for(;;) */
@@ -677,7 +678,7 @@ int cpu_exec(CPUState *env1)
#endif
/* restore global registers */
- asm("");
+ barrier();
env = (void *) saved_env_reg;
/* fail safe : never use cpu_single_env outside cpu_exec() */
diff --git a/cpus.c b/cpus.c
index 2e40814c6..8319d4eb8 100644
--- a/cpus.c
+++ b/cpus.c
@@ -40,7 +40,6 @@
#define SIG_IPI SIGUSR1
#endif
-static CPUState *cur_cpu;
static CPUState *next_cpu;
/***********************************************************/
@@ -132,7 +131,7 @@ static int cpu_has_work(CPUState *env)
return 0;
}
-static int tcg_has_work(void)
+static int any_cpu_has_work(void)
{
CPUState *env;
@@ -142,6 +141,13 @@ static int tcg_has_work(void)
return 0;
}
+static void cpu_debug_handler(CPUState *env)
+{
+ gdb_set_stop_cpu(env);
+ debug_requested = EXCP_DEBUG;
+ vm_stop(EXCP_DEBUG);
+}
+
#ifndef _WIN32
static int io_thread_fd = -1;
@@ -237,6 +243,8 @@ static void qemu_event_increment(void)
#ifndef CONFIG_IOTHREAD
int qemu_init_main_loop(void)
{
+ cpu_set_debug_excp_handler(cpu_debug_handler);
+
return qemu_event_init();
}
@@ -334,11 +342,14 @@ int qemu_init_main_loop(void)
{
int ret;
+ cpu_set_debug_excp_handler(cpu_debug_handler);
+
ret = qemu_event_init();
if (ret)
return ret;
qemu_cond_init(&qemu_pause_cond);
+ qemu_cond_init(&qemu_system_cond);
qemu_mutex_init(&qemu_fair_mutex);
qemu_mutex_init(&qemu_global_mutex);
qemu_mutex_lock(&qemu_global_mutex);
@@ -409,10 +420,12 @@ static void qemu_wait_io_event_common(CPUState *env)
flush_queued_work(env);
}
-static void qemu_wait_io_event(CPUState *env)
+static void qemu_tcg_wait_io_event(void)
{
- while (!tcg_has_work())
- qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000);
+ CPUState *env;
+
+ while (!any_cpu_has_work())
+ qemu_cond_timedwait(tcg_halt_cond, &qemu_global_mutex, 1000);
qemu_mutex_unlock(&qemu_global_mutex);
@@ -425,7 +438,10 @@ static void qemu_wait_io_event(CPUState *env)
qemu_mutex_unlock(&qemu_fair_mutex);
qemu_mutex_lock(&qemu_global_mutex);
- qemu_wait_io_event_common(env);
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ qemu_wait_io_event_common(env);
+ }
}
static void qemu_kvm_eat_signal(CPUState *env, int timeout)
@@ -509,8 +525,8 @@ static void *tcg_cpu_thread_fn(void *arg)
qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100);
while (1) {
- tcg_cpu_exec();
- qemu_wait_io_event(cur_cpu);
+ cpu_exec_all();
+ qemu_tcg_wait_io_event();
}
return NULL;
@@ -770,32 +786,28 @@ static int qemu_cpu_exec(CPUState *env)
return ret;
}
-bool tcg_cpu_exec(void)
+bool cpu_exec_all(void)
{
- int ret = 0;
-
if (next_cpu == NULL)
next_cpu = first_cpu;
- for (; next_cpu != NULL; next_cpu = next_cpu->next_cpu) {
- CPUState *env = cur_cpu = next_cpu;
+ for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) {
+ CPUState *env = next_cpu;
qemu_clock_enable(vm_clock,
- (cur_cpu->singlestep_enabled & SSTEP_NOTIMER) == 0);
+ (env->singlestep_enabled & SSTEP_NOTIMER) == 0);
if (qemu_alarm_pending())
break;
- if (cpu_can_run(env))
- ret = qemu_cpu_exec(env);
- else if (env->stop)
- break;
-
- if (ret == EXCP_DEBUG) {
- gdb_set_stop_cpu(env);
- debug_requested = EXCP_DEBUG;
+ if (cpu_can_run(env)) {
+ if (qemu_cpu_exec(env) == EXCP_DEBUG) {
+ break;
+ }
+ } else if (env->stop) {
break;
}
}
- return tcg_has_work();
+ exit_request = 0;
+ return any_cpu_has_work();
}
void set_numa_modes(void)
diff --git a/cpus.h b/cpus.h
index 774150a76..af267ea07 100644
--- a/cpus.h
+++ b/cpus.h
@@ -13,7 +13,7 @@ extern int smp_threads;
extern int debug_requested;
extern int vmstop_requested;
void vm_state_notify(int running, int reason);
-bool tcg_cpu_exec(void);
+bool cpu_exec_all(void);
void set_numa_modes(void);
void set_cpu_log(const char *optarg);
void list_cpus(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
diff --git a/docs/migration.txt b/docs/migration.txt
new file mode 100644
index 000000000..69d53837c
--- /dev/null
+++ b/docs/migration.txt
@@ -0,0 +1,303 @@
+= Migration =
+
+QEMU has code to load/save the state of the guest that it is running.
+This are two complementary operations. Saving the state just does
+that, saves the state for each device that the guest is running.
+Restoring a guest is just the opposite operation: we need to load the
+state of each device.
+
+For this to work, QEMU has to be launch with the same arguments the
+two times. I.e. it can only restore the state in one guest that has
+the same devices that the one it was saved (this last requirement can
+be relaxed a bit, but for now we can consider that configuration have
+to be exactly the same).
+
+Once that we are able to save/restore a guest, a new functionality is
+requested: migration. This means that QEMU is able to start in one
+machine and being "migrated" to other machine. I.e. being moved to
+other machine.
+
+Next was the "live migration" functionality. This is important
+because some guests run with a lot of state (specially RAM), and it
+can take a while to move all state from one machine to another. Live
+migration allows the guest to continue running while the state is
+transferred. Only while the last part of the state is transferred has
+the guest to be stopped. Typically the time that the guest is
+unresponsive during live migration is the low hundred of milliseconds
+(notice that this depends on lot of things).
+
+=== Types of migration ===
+
+Now that we have talked about live migration, there are several ways
+to do migration:
+
+- tcp migration: do the migration using tcp sockets
+- unix migration: do the migration using unix sockets
+- exec migration: do the migration using the stdin/stdout through a process.
+- fd migration: do the migration using an file descriptor that is
+ passed to QEMU. QEMU don't cares how this file descriptor is opened.
+
+All this four migration protocols use the same infrastructure to
+save/restore state devices. This infrastructure is shared with the
+savevm/loadvm functionality.
+
+=== State Live Migration ==
+
+This is used for RAM and block devices. It is not yet ported to vmstate.
+<Fill more information here>
+
+=== What is the common infrastructure ===
+
+QEMU uses a QEMUFile abstraction to be able to do migration. Any type
+of migration that what to use QEMU infrastructure has to create a
+QEMUFile with:
+
+QEMUFile *qemu_fopen_ops(void *opaque,
+ QEMUFilePutBufferFunc *put_buffer,
+ QEMUFileGetBufferFunc *get_buffer,
+ QEMUFileCloseFunc *close,
+ QEMUFileRateLimit *rate_limit,
+ QEMUFileSetRateLimit *set_rate_limit,
+ QEMUFileGetRateLimit *get_rate_limit);
+
+The functions have the following functionality:
+
+This function writes a chunk of data to a file at the given position.
+The pos argument can be ignored if the file is only being used for
+streaming. The handler should try to write all of the data it can.
+
+typedef int (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf,
+ int64_t pos, int size);
+
+Read a chunk of data from a file at the given position. The pos argument
+can be ignored if the file is only be used for streaming. The number of
+bytes actually read should be returned.
+
+typedef int (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf,
+ int64_t pos, int size);
+
+Close a file and return an error code
+
+typedef int (QEMUFileCloseFunc)(void *opaque);
+
+Called to determine if the file has exceeded it's bandwidth allocation. The
+bandwidth capping is a soft limit, not a hard limit.
+
+typedef int (QEMUFileRateLimit)(void *opaque);
+
+Called to change the current bandwidth allocation. This function must return
+the new actual bandwidth. It should be new_rate if everything goes OK, and
+the old rate otherwise
+
+typedef size_t (QEMUFileSetRateLimit)(void *opaque, size_t new_rate);
+typedef size_t (QEMUFileGetRateLimit)(void *opaque);
+
+You can use any internal state that you need using the opaque void *
+pointer that is passed to all functions.
+
+The rate limiting functions are used to limit the bandwidth used by
+QEMU migration.
+
+The important functions for us are put_buffer()/get_buffer() that
+allow to write/read a buffer into the QEMUFile.
+
+=== How to save the state of one device ==
+
+The state of a device is saved using intermediate buffers. There are
+some helper functions to assist this saving.
+
+There is a new concept that we have to explain here: device state
+version. When we migrate a device, we save/load the state as a series
+of fields. Some times, due to bugs or new functionality, we need to
+change the state to store more/different information. We use the
+version to identify each time that we do a change. Each version is
+associated with a series of fields saved. The save_state always save
+the state as the newer version. But load_state some times is able to
+load state from an older version.
+
+ === Legacy way ===
+
+This way is going to disappear as soon as all current users are ported to VMSTATE.
+
+Each device has to register two functions, one to save the state and
+another to load the state back.
+
+int register_savevm(DeviceState *dev,
+ const char *idstr,
+ int instance_id,
+ int version_id,
+ SaveStateHandler *save_state,
+ LoadStateHandler *load_state,
+ void *opaque);
+
+typedef void SaveStateHandler(QEMUFile *f, void *opaque);
+typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
+
+The important functions for the device state format are the save_state
+and load_state. Notice that load_state receives a version_id
+parameter to know what state format is receiving. save_state don't
+have a version_id parameter because it uses always the latest version.
+
+=== VMState ===
+
+The legacy way of saving/loading state of the device had the problem
+that we have to maintain in sync two functions. If we did one change
+in one of them and not on the other, we got a failed migration.
+
+VMState changed the way that state is saved/loaded. Instead of using
+a function to save the state and another to load it, it was changed to
+a declarative way of what the state consisted of. Now VMState is able
+to interpret that definition to be able to load/save the state. As
+the state is declared only once, it can't go out of sync in the
+save/load functions.
+
+An example (from hw/pckbd.c)
+
+static const VMStateDescription vmstate_kbd = {
+ .name = "pckbd",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(write_cmd, KBDState),
+ VMSTATE_UINT8(status, KBDState),
+ VMSTATE_UINT8(mode, KBDState),
+ VMSTATE_UINT8(pending, KBDState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+We are declaring the state with name "pckbd".
+The version_id is 3, and the fields are 4 uint8_t in a KBDState structure.
+We registered this with:
+
+ vmstate_register(NULL, 0, &vmstate_kbd, s);
+
+Note: talk about how vmstate <-> qdev interact, and what the instance id's mean.
+
+You can search for VMSTATE_* macros for lots of types used in QEMU in
+hw/hw.h.
+
+=== More about versions ==
+
+You can see that there are several version fields:
+
+- version_id: the maximum version_id supported by VMState for that device
+- minimum_version_id: the minimum version_id that VMState is able to understand
+ for that device.
+- minimum_version_id_old: For devices that were not able to port to vmstate, we can
+ assign a function that knows how to read this old state.
+
+So, VMState is able to read versions from minimum_version_id to
+version_id. And the function load_state_old() is able to load state
+from minimum_version_id_old to minimum_version_id. This function is
+deprecated and will be removed when no more users are left.
+
+=== Massaging functions ===
+
+Some times, it is not enough to be able to save the state directly
+from one structure, we need to fill the correct values there. One
+example is when we are using kvm. Before saving the cpu state, we
+need to ask kvm to copy to QEMU the state that it is using. And the
+opposite when we are loading the state, we need a way to tell kvm to
+load the state for the cpu that we have just loaded from the QEMUFile.
+
+The functions to do that are inside a vmstate definition, and are called:
+
+- int (*pre_load)(void *opaque);
+
+ This function is called before we load the state of one device.
+
+- int (*post_load)(void *opaque, int version_id);
+
+ This function is called after we load the state of one device.
+
+- void (*pre_save)(void *opaque);
+
+ This function is called before we save the state of one device.
+
+Example: You can look at hpet.c, that uses the three function to
+ massage the state that is transferred.
+
+=== Subsections ===
+
+The use of version_id allows to be able to migrate from older versions
+to newer versions of a device. But not the other way around. This
+makes very complicated to fix bugs in stable branches. If we need to
+add anything to the state to fix a bug, we have to disable migration
+to older versions that don't have that bug-fix (i.e. a new field).
+
+But some time, that bug-fix is only needed sometimes, not always. For
+instance, if the device is in the middle of a DMA operation, it is
+using a specific functionality, ....
+
+It is impossible to create a way to make migration from any version to
+any other version to work. But we can do better that only allowing
+migration from older versions no newer ones. For that fields that are
+only needed sometimes, we add the idea of subsections. a subsection
+is "like" a device vmstate, but with a particularity, it has a Boolean
+function that tells if that values are needed to be sent or not. If
+this functions returns false, the subsection is not sent.
+
+On the receiving side, if we found a subsection for a device that we
+don't understand, we just fail the migration. If we understand all
+the subsections, then we load the state with success.
+
+One important note is that the post_load() function is called "after"
+loading all subsections, because a newer subsection could change same
+value that it uses.
+
+Example:
+
+static bool ide_drive_pio_state_needed(void *opaque)
+{
+ IDEState *s = opaque;
+
+ return (s->status & DRQ_STAT) != 0;
+}
+
+const VMStateDescription vmstate_ide_drive_pio_state = {
+ .name = "ide_drive/pio_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = ide_drive_pio_pre_save,
+ .post_load = ide_drive_pio_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(req_nb_sectors, IDEState),
+ VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
+ vmstate_info_uint8, uint8_t),
+ VMSTATE_INT32(cur_io_buffer_offset, IDEState),
+ VMSTATE_INT32(cur_io_buffer_len, IDEState),
+ VMSTATE_UINT8(end_transfer_fn_idx, IDEState),
+ VMSTATE_INT32(elementary_transfer_size, IDEState),
+ VMSTATE_INT32(packet_transfer_size, IDEState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+const VMStateDescription vmstate_ide_drive = {
+ .name = "ide_drive",
+ .version_id = 3,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = ide_drive_post_load,
+ .fields = (VMStateField []) {
+ .... several fields ....
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection []) {
+ {
+ .vmsd = &vmstate_ide_drive_pio_state,
+ .needed = ide_drive_pio_state_needed,
+ }, {
+ /* empty */
+ }
+ }
+};
+
+Here we have a subsection for the pio state. We only need to
+save/send this state when we are in the middle of a pio operation
+(that is what ide_drive_pio_state_needed() checks). If DRQ_STAT is
+not enabled, the values on that fields are garbage and don't need to
+be sent.
diff --git a/exec-all.h b/exec-all.h
index a775582be..3a53fe64c 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -86,9 +86,6 @@ int cpu_gen_code(CPUState *env, struct TranslationBlock *tb,
int cpu_restore_state(struct TranslationBlock *tb,
CPUState *env, unsigned long searched_pc,
void *puc);
-int cpu_restore_state_copy(struct TranslationBlock *tb,
- CPUState *env, unsigned long searched_pc,
- void *puc);
void cpu_resume_from_signal(CPUState *env1, void *puc);
void cpu_io_recompile(CPUState *env, void *retaddr);
TranslationBlock *tb_gen_code(CPUState *env,
@@ -191,8 +188,6 @@ void tb_link_page(TranslationBlock *tb,
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr);
extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
-extern uint8_t *code_gen_ptr;
-extern int code_gen_max_blocks;
#if defined(USE_DIRECT_JUMP)
diff --git a/exec.c b/exec.c
index 5e9a5b777..b14bad93f 100644
--- a/exec.c
+++ b/exec.c
@@ -86,7 +86,7 @@
#define SMC_BITMAP_USE_THRESHOLD 10
static TranslationBlock *tbs;
-int code_gen_max_blocks;
+static int code_gen_max_blocks;
TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
static int nb_tbs;
/* any access to the tbs or the page table must use this lock */
@@ -113,7 +113,7 @@ static uint8_t *code_gen_buffer;
static unsigned long code_gen_buffer_size;
/* threshold to flush the translated code buffer */
static unsigned long code_gen_buffer_max_size;
-uint8_t *code_gen_ptr;
+static uint8_t *code_gen_ptr;
#if !defined(CONFIG_USER_ONLY)
int phys_ram_fd;
diff --git a/hw/e1000.c b/hw/e1000.c
index 0da65f9a3..80b78bc61 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -262,21 +262,20 @@ set_eecd(E1000State *s, int index, uint32_t val)
s->eecd_state.old_eecd = val & (E1000_EECD_SK | E1000_EECD_CS |
E1000_EECD_DI|E1000_EECD_FWE_MASK|E1000_EECD_REQ);
+ if (!(E1000_EECD_CS & val)) // CS inactive; nothing to do
+ return;
+ if (E1000_EECD_CS & (val ^ oldval)) { // CS rise edge; reset state
+ s->eecd_state.val_in = 0;
+ s->eecd_state.bitnum_in = 0;
+ s->eecd_state.bitnum_out = 0;
+ s->eecd_state.reading = 0;
+ }
if (!(E1000_EECD_SK & (val ^ oldval))) // no clock edge
return;
if (!(E1000_EECD_SK & val)) { // falling edge
s->eecd_state.bitnum_out++;
return;
}
- if (!(val & E1000_EECD_CS)) { // rising, no CS (EEPROM reset)
- memset(&s->eecd_state, 0, sizeof s->eecd_state);
- /*
- * restore old_eecd's E1000_EECD_SK (known to be on)
- * to avoid false detection of a clock edge
- */
- s->eecd_state.old_eecd = E1000_EECD_SK;
- return;
- }
s->eecd_state.val_in <<= 1;
if (val & E1000_EECD_DI)
s->eecd_state.val_in |= 1;
@@ -344,6 +343,15 @@ is_vlan_txd(uint32_t txd_lower)
return ((txd_lower & E1000_TXD_CMD_VLE) != 0);
}
+/* FCS aka Ethernet CRC-32. We don't get it from backends and can't
+ * fill it in, just pad descriptor length by 4 bytes unless guest
+ * told us to trip it off the packet. */
+static inline int
+fcs_len(E1000State *s)
+{
+ return (s->mac_reg[RCTL] & E1000_RCTL_SECRC) ? 0 : 4;
+}
+
static void
xmit_seg(E1000State *s)
{
@@ -649,7 +657,6 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
}
rdh_start = s->mac_reg[RDH];
- size += 4; // for the header
do {
if (s->mac_reg[RDH] == s->mac_reg[RDT] && s->check_rxov) {
set_ics(s, 0, E1000_ICS_RXO);
@@ -663,7 +670,7 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
if (desc.buffer_addr) {
cpu_physical_memory_write(le64_to_cpu(desc.buffer_addr),
(void *)(buf + vlan_offset), size);
- desc.length = cpu_to_le16(size);
+ desc.length = cpu_to_le16(size + fcs_len(s));
desc.status |= E1000_RXD_STAT_EOP|E1000_RXD_STAT_IXSM;
} else // as per intel docs; skip descriptors with null buf addr
DBGOUT(RX, "Null RX descriptor!!\n");
diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c
index 187ece19e..b897c9c16 100644
--- a/hw/etraxfs_eth.c
+++ b/hw/etraxfs_eth.c
@@ -437,6 +437,7 @@ eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
eth_validate_duplex(eth);
}
eth->mdio_bus.mdc = !!(value & 4);
+ eth->regs[addr] = value;
break;
case RW_REC_CTRL:
diff --git a/hw/hw.h b/hw/hw.h
index cb553a6e8..ec6985d3b 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -313,6 +313,11 @@ typedef struct {
bool (*field_exists)(void *opaque, int version_id);
} VMStateField;
+typedef struct VMStateSubsection {
+ const VMStateDescription *vmsd;
+ bool (*needed)(void *opaque);
+} VMStateSubsection;
+
struct VMStateDescription {
const char *name;
int version_id;
@@ -323,6 +328,7 @@ struct VMStateDescription {
int (*post_load)(void *opaque, int version_id);
void (*pre_save)(void *opaque);
VMStateField *fields;
+ const VMStateSubsection *subsections;
};
extern const VMStateInfo vmstate_info_int8;
diff --git a/hw/ide/core.c b/hw/ide/core.c
index e20f2e7cb..db000831e 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -2733,6 +2733,7 @@ static EndTransferFunc* transfer_end_table[] = {
ide_transfer_stop,
ide_atapi_cmd_reply_end,
ide_atapi_cmd,
+ ide_dummy_transfer_stop,
};
static int transfer_end_table_idx(EndTransferFunc *fn)
@@ -2756,26 +2757,29 @@ static int ide_drive_post_load(void *opaque, int version_id)
s->cdrom_changed = 1;
}
}
+ return 0;
+}
- if (s->cur_io_buffer_len) {
- s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx];
- s->data_ptr = s->io_buffer + s->cur_io_buffer_offset;
- s->data_end = s->data_ptr + s->cur_io_buffer_len;
+static int ide_drive_pio_post_load(void *opaque, int version_id)
+{
+ IDEState *s = opaque;
+
+ if (s->end_transfer_fn_idx < 0 ||
+ s->end_transfer_fn_idx > ARRAY_SIZE(transfer_end_table)) {
+ return -EINVAL;
}
-
+ s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx];
+ s->data_ptr = s->io_buffer + s->cur_io_buffer_offset;
+ s->data_end = s->data_ptr + s->cur_io_buffer_len;
+
return 0;
}
-static void ide_drive_pre_save(void *opaque)
+static void ide_drive_pio_pre_save(void *opaque)
{
IDEState *s = opaque;
int idx;
- s->cur_io_buffer_len = 0;
-
- if (!(s->status & DRQ_STAT))
- return;
-
s->cur_io_buffer_offset = s->data_ptr - s->io_buffer;
s->cur_io_buffer_len = s->data_end - s->data_ptr;
@@ -2789,12 +2793,38 @@ static void ide_drive_pre_save(void *opaque)
}
}
+static bool ide_drive_pio_state_needed(void *opaque)
+{
+ IDEState *s = opaque;
+
+ return (s->status & DRQ_STAT) != 0;
+}
+
+const VMStateDescription vmstate_ide_drive_pio_state = {
+ .name = "ide_drive/pio_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = ide_drive_pio_pre_save,
+ .post_load = ide_drive_pio_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(req_nb_sectors, IDEState),
+ VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
+ vmstate_info_uint8, uint8_t),
+ VMSTATE_INT32(cur_io_buffer_offset, IDEState),
+ VMSTATE_INT32(cur_io_buffer_len, IDEState),
+ VMSTATE_UINT8(end_transfer_fn_idx, IDEState),
+ VMSTATE_INT32(elementary_transfer_size, IDEState),
+ VMSTATE_INT32(packet_transfer_size, IDEState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
const VMStateDescription vmstate_ide_drive = {
.name = "ide_drive",
- .version_id = 4,
+ .version_id = 3,
.minimum_version_id = 0,
.minimum_version_id_old = 0,
- .pre_save = ide_drive_pre_save,
.post_load = ide_drive_post_load,
.fields = (VMStateField []) {
VMSTATE_INT32(mult_sectors, IDEState),
@@ -2817,15 +2847,15 @@ const VMStateDescription vmstate_ide_drive = {
VMSTATE_UINT8(sense_key, IDEState),
VMSTATE_UINT8(asc, IDEState),
VMSTATE_UINT8_V(cdrom_changed, IDEState, 3),
- VMSTATE_INT32_V(req_nb_sectors, IDEState, 4),
- VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 4,
- vmstate_info_uint8, uint8_t),
- VMSTATE_INT32_V(cur_io_buffer_offset, IDEState, 4),
- VMSTATE_INT32_V(cur_io_buffer_len, IDEState, 4),
- VMSTATE_UINT8_V(end_transfer_fn_idx, IDEState, 4),
- VMSTATE_INT32_V(elementary_transfer_size, IDEState, 4),
- VMSTATE_INT32_V(packet_transfer_size, IDEState, 4),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection []) {
+ {
+ .vmsd = &vmstate_ide_drive_pio_state,
+ .needed = ide_drive_pio_state_needed,
+ }, {
+ /* empty */
+ }
}
};
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index 4d95cc5e2..4331d7723 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -121,9 +121,31 @@ void bmdma_addr_writel(void *opaque, uint32_t addr, uint32_t val)
bm->cur_addr = bm->addr;
}
+static bool ide_bmdma_current_needed(void *opaque)
+{
+ BMDMAState *bm = opaque;
+
+ return (bm->cur_prd_len != 0);
+}
+
+static const VMStateDescription vmstate_bmdma_current = {
+ .name = "ide bmdma_current",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(cur_addr, BMDMAState),
+ VMSTATE_UINT32(cur_prd_last, BMDMAState),
+ VMSTATE_UINT32(cur_prd_addr, BMDMAState),
+ VMSTATE_UINT32(cur_prd_len, BMDMAState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
static const VMStateDescription vmstate_bmdma = {
.name = "ide bmdma",
- .version_id = 4,
+ .version_id = 3,
.minimum_version_id = 0,
.minimum_version_id_old = 0,
.fields = (VMStateField []) {
@@ -133,11 +155,15 @@ static const VMStateDescription vmstate_bmdma = {
VMSTATE_INT64(sector_num, BMDMAState),
VMSTATE_UINT32(nsector, BMDMAState),
VMSTATE_UINT8(unit, BMDMAState),
- VMSTATE_UINT32_V(cur_addr, BMDMAState, 4),
- VMSTATE_UINT32_V(cur_prd_last, BMDMAState, 4),
- VMSTATE_UINT32_V(cur_prd_addr, BMDMAState, 4),
- VMSTATE_UINT32_V(cur_prd_len, BMDMAState, 4),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection []) {
+ {
+ .vmsd = &vmstate_bmdma_current,
+ .needed = ide_bmdma_current_needed,
+ }, {
+ /* empty */
+ }
}
};
@@ -156,7 +182,7 @@ static int ide_pci_post_load(void *opaque, int version_id)
const VMStateDescription vmstate_ide_pci = {
.name = "ide",
- .version_id = 4,
+ .version_id = 3,
.minimum_version_id = 0,
.minimum_version_id_old = 0,
.post_load = ide_pci_post_load,
diff --git a/hw/mips_int.c b/hw/mips_int.c
index c30954caa..477f6abf9 100644
--- a/hw/mips_int.c
+++ b/hw/mips_int.c
@@ -24,22 +24,6 @@
#include "mips_cpudevs.h"
#include "cpu.h"
-/* Raise IRQ to CPU if necessary. It must be called every time the active
- IRQ may change */
-void cpu_mips_update_irq(CPUState *env)
-{
- if ((env->CP0_Status & (1 << CP0St_IE)) &&
- !(env->CP0_Status & (1 << CP0St_EXL)) &&
- !(env->CP0_Status & (1 << CP0St_ERL)) &&
- !(env->hflags & MIPS_HFLAG_DM)) {
- if ((env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask) &&
- !(env->interrupt_request & CPU_INTERRUPT_HARD)) {
- cpu_interrupt(env, CPU_INTERRUPT_HARD);
- }
- } else
- cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
-}
-
static void cpu_mips_irq_request(void *opaque, int irq, int level)
{
CPUState *env = (CPUState *)opaque;
@@ -52,7 +36,12 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level)
} else {
env->CP0_Cause &= ~(1 << (irq + CP0Ca_IP));
}
- cpu_mips_update_irq(env);
+
+ if (env->CP0_Cause & CP0Ca_IP_mask) {
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+ }
}
void cpu_mips_irq_init_cpu(CPUState *env)
@@ -65,3 +54,12 @@ void cpu_mips_irq_init_cpu(CPUState *env)
env->irq[i] = qi[i];
}
}
+
+void cpu_mips_soft_irq(CPUState *env, int irq, int level)
+{
+ if (irq < 0 || irq > 2) {
+ return;
+ }
+
+ qemu_set_irq(env->irq[irq], level);
+}
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 3a1c67023..9e4bac867 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -245,7 +245,7 @@ static QEMUMachine pc_machine_v0_12 = {
.compat_props = (GlobalProperty[]) {
{
.driver = "virtio-serial-pci",
- .property = "max_nr_ports",
+ .property = "max_ports",
.value = stringify(1),
},{
.driver = "virtio-serial-pci",
@@ -268,7 +268,7 @@ static QEMUMachine pc_machine_v0_11 = {
.value = stringify(0),
},{
.driver = "virtio-serial-pci",
- .property = "max_nr_ports",
+ .property = "max_ports",
.value = stringify(1),
},{
.driver = "virtio-serial-pci",
@@ -307,7 +307,7 @@ static QEMUMachine pc_machine_v0_10 = {
.value = stringify(PCI_CLASS_DISPLAY_OTHER),
},{
.driver = "virtio-serial-pci",
- .property = "max_nr_ports",
+ .property = "max_ports",
.value = stringify(1),
},{
.driver = "virtio-serial-pci",
diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c
index 79eb0b357..74cc56a34 100644
--- a/hw/ppc440_bamboo.c
+++ b/hw/ppc440_bamboo.c
@@ -187,7 +187,7 @@ static QEMUMachine bamboo_machine_v0_12 = {
.compat_props = (GlobalProperty[]) {
{
.driver = "virtio-serial-pci",
- .property = "max_nr_ports",
+ .property = "max_ports",
.value = stringify(1),
},{
.driver = "virtio-serial-pci",
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index d69c74c4e..b860a09ed 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -142,6 +142,7 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l
req->tag = tag;
req->lun = lun;
req->status = -1;
+ req->enqueued = true;
QTAILQ_INSERT_TAIL(&d->requests, req, next);
return req;
}
@@ -158,9 +159,17 @@ SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag)
return NULL;
}
+static void scsi_req_dequeue(SCSIRequest *req)
+{
+ if (req->enqueued) {
+ QTAILQ_REMOVE(&req->dev->requests, req, next);
+ req->enqueued = false;
+ }
+}
+
void scsi_req_free(SCSIRequest *req)
{
- QTAILQ_REMOVE(&req->dev->requests, req, next);
+ scsi_req_dequeue(req);
qemu_free(req);
}
@@ -512,6 +521,7 @@ void scsi_req_print(SCSIRequest *req)
void scsi_req_complete(SCSIRequest *req)
{
assert(req->status != -1);
+ scsi_req_dequeue(req);
req->bus->complete(req->bus, SCSI_REASON_DONE,
req->tag,
req->status);
diff --git a/hw/scsi.h b/hw/scsi.h
index 4fbf1d5df..cb06d6d82 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -43,6 +43,7 @@ typedef struct SCSIRequest {
enum SCSIXferMode mode;
} cmd;
BlockDriverAIOCB *aiocb;
+ bool enqueued;
QTAILQ_ENTRY(SCSIRequest) next;
} SCSIRequest;
diff --git a/hw/vhost.c b/hw/vhost.c
index d37a66e0e..65709d005 100644
--- a/hw/vhost.c
+++ b/hw/vhost.c
@@ -659,6 +659,16 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
r = -errno;
goto fail;
}
+ for (i = 0; i < hdev->nvqs; ++i) {
+ r = vhost_virtqueue_init(hdev,
+ vdev,
+ hdev->vqs + i,
+ i);
+ if (r < 0) {
+ goto fail_vq;
+ }
+ }
+
if (hdev->log_enabled) {
hdev->log_size = vhost_get_log_size(hdev);
hdev->log = hdev->log_size ?
@@ -667,19 +677,10 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
(uint64_t)(unsigned long)hdev->log);
if (r < 0) {
r = -errno;
- goto fail;
- }
- }
-
- for (i = 0; i < hdev->nvqs; ++i) {
- r = vhost_virtqueue_init(hdev,
- vdev,
- hdev->vqs + i,
- i);
- if (r < 0) {
goto fail_vq;
}
}
+
hdev->started = true;
return 0;
diff --git a/kvm-all.c b/kvm-all.c
index fec6d0521..0b5ce9281 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -943,8 +943,6 @@ int kvm_cpu_exec(CPUState *env)
DPRINTF("kvm_exit_debug\n");
#ifdef KVM_CAP_SET_GUEST_DEBUG
if (kvm_arch_debug(&run->debug.arch)) {
- gdb_set_stop_cpu(env);
- vm_stop(EXCP_DEBUG);
env->exception_index = EXCP_DEBUG;
return 0;
}
diff --git a/linux-user/main.c b/linux-user/main.c
index 403c8d3b9..fa29d7773 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -2800,6 +2800,8 @@ int main(int argc, char **argv, char **envp)
/* XXX: implement xxx_cpu_list for targets that still miss it */
#if defined(cpu_list_id)
cpu_list_id(stdout, &fprintf, "");
+#elif defined(cpu_list)
+ cpu_list(stdout, &fprintf); /* deprecated */
#endif
exit(1);
}
diff --git a/linux-user/signal.c b/linux-user/signal.c
index cc0cb10cd..77683f753 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -3064,9 +3064,23 @@ struct target_sigcontext {
uint32_t oldmask;
};
+struct target_stack_t {
+ abi_ulong ss_sp;
+ int ss_flags;
+ unsigned int ss_size;
+};
+
+struct target_ucontext {
+ abi_ulong uc_flags;
+ abi_ulong uc_link;
+ struct target_stack_t uc_stack;
+ struct target_sigcontext sc;
+ uint32_t extramask[TARGET_NSIG_WORDS - 1];
+};
+
/* Signal frames. */
struct target_signal_frame {
- struct target_sigcontext sc;
+ struct target_ucontext uc;
uint32_t extramask[TARGET_NSIG_WORDS - 1];
uint32_t tramp[2];
};
@@ -3175,7 +3189,7 @@ static void setup_frame(int sig, struct target_sigaction *ka,
goto badframe;
/* Save the mask. */
- err |= __put_user(set->sig[0], &frame->sc.oldmask);
+ err |= __put_user(set->sig[0], &frame->uc.sc.oldmask);
if (err)
goto badframe;
@@ -3184,7 +3198,7 @@ static void setup_frame(int sig, struct target_sigaction *ka,
goto badframe;
}
- setup_sigcontext(&frame->sc, env);
+ setup_sigcontext(&frame->uc.sc, env);
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
@@ -3214,7 +3228,7 @@ static void setup_frame(int sig, struct target_sigaction *ka,
/* Signal handler args: */
env->regs[5] = sig; /* Arg 0: signum */
env->regs[6] = 0;
- env->regs[7] = (unsigned long) &frame->sc; /* arg 1: sigcontext */
+ env->regs[7] = (unsigned long) &frame->uc; /* arg 1: sigcontext */
/* Offset of 4 to handle microblaze rtid r14, 0 */
env->sregs[SR_PC] = (unsigned long)ka->_sa_handler;
@@ -3247,7 +3261,7 @@ long do_sigreturn(CPUState *env)
goto badframe;
/* Restore blocked signals */
- if (__get_user(target_set.sig[0], &frame->sc.oldmask))
+ if (__get_user(target_set.sig[0], &frame->uc.sc.oldmask))
goto badframe;
for(i = 1; i < TARGET_NSIG_WORDS; i++) {
if (__get_user(target_set.sig[i], &frame->extramask[i - 1]))
@@ -3256,7 +3270,7 @@ long do_sigreturn(CPUState *env)
target_to_host_sigset_internal(&set, &target_set);
sigprocmask(SIG_SETMASK, &set, NULL);
- restore_sigcontext(&frame->sc, env);
+ restore_sigcontext(&frame->uc.sc, env);
/* We got here through a sigreturn syscall, our path back is via an
rtb insn so setup r14 for that. */
env->regs[14] = env->sregs[SR_PC];
diff --git a/migration-tcp.c b/migration-tcp.c
index 78b56dc3f..b55f419b6 100644
--- a/migration-tcp.c
+++ b/migration-tcp.c
@@ -151,7 +151,7 @@ static void tcp_accept_incoming_migration(void *opaque)
if (c == -1) {
fprintf(stderr, "could not accept migration connection\n");
- return;
+ goto out2;
}
f = qemu_fopen_socket(c);
@@ -163,9 +163,10 @@ static void tcp_accept_incoming_migration(void *opaque)
process_incoming_migration(f);
qemu_fclose(f);
out:
+ close(c);
+out2:
qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL);
close(s);
- close(c);
}
int tcp_start_incoming_migration(const char *host_port)
diff --git a/qemu-barrier.h b/qemu-barrier.h
index 3bd1075d6..b77fce23a 100644
--- a/qemu-barrier.h
+++ b/qemu-barrier.h
@@ -4,4 +4,7 @@
/* FIXME: arch dependant, x86 version */
#define smp_wmb() asm volatile("" ::: "memory")
+/* Compiler barrier */
+#define barrier() asm volatile("" ::: "memory")
+
#endif
diff --git a/qemu-options.hx b/qemu-options.hx
index 346e2e387..a0e3059fd 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -837,6 +837,13 @@ empty, with a @code{deny} policy. Thus no one will be allowed to
use the VNC server until the ACLs have been loaded. This can be
achieved using the @code{acl} monitor command.
+@item lossy
+
+Enable lossy compression methods (gradient, JPEG, ...). If this
+option is set, VNC client may receive lossy framebuffer updates
+depending on its encoding settings. Enabling this option can save
+a lot of bandwidth at the expense of quality.
+
@end table
ETEXI
diff --git a/qemu-thread.c b/qemu-thread.c
index faf406142..fbc78fef4 100644
--- a/qemu-thread.c
+++ b/qemu-thread.c
@@ -34,6 +34,15 @@ void qemu_mutex_init(QemuMutex *mutex)
error_exit(err, __func__);
}
+void qemu_mutex_destroy(QemuMutex *mutex)
+{
+ int err;
+
+ err = pthread_mutex_destroy(&mutex->lock);
+ if (err)
+ error_exit(err, __func__);
+}
+
void qemu_mutex_lock(QemuMutex *mutex)
{
int err;
@@ -90,6 +99,15 @@ void qemu_cond_init(QemuCond *cond)
error_exit(err, __func__);
}
+void qemu_cond_destroy(QemuCond *cond)
+{
+ int err;
+
+ err = pthread_cond_destroy(&cond->cond);
+ if (err)
+ error_exit(err, __func__);
+}
+
void qemu_cond_signal(QemuCond *cond)
{
int err;
@@ -168,3 +186,7 @@ int qemu_thread_equal(QemuThread *thread1, QemuThread *thread2)
return pthread_equal(thread1->thread, thread2->thread);
}
+void qemu_thread_exit(void *retval)
+{
+ pthread_exit(retval);
+}
diff --git a/qemu-thread.h b/qemu-thread.h
index 5ef4a3aed..19bb30c94 100644
--- a/qemu-thread.h
+++ b/qemu-thread.h
@@ -20,12 +20,14 @@ typedef struct QemuCond QemuCond;
typedef struct QemuThread QemuThread;
void qemu_mutex_init(QemuMutex *mutex);
+void qemu_mutex_destroy(QemuMutex *mutex);
void qemu_mutex_lock(QemuMutex *mutex);
int qemu_mutex_trylock(QemuMutex *mutex);
int qemu_mutex_timedlock(QemuMutex *mutex, uint64_t msecs);
void qemu_mutex_unlock(QemuMutex *mutex);
void qemu_cond_init(QemuCond *cond);
+void qemu_cond_destroy(QemuCond *cond);
void qemu_cond_signal(QemuCond *cond);
void qemu_cond_broadcast(QemuCond *cond);
void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex);
@@ -37,4 +39,6 @@ void qemu_thread_create(QemuThread *thread,
void qemu_thread_signal(QemuThread *thread, int sig);
void qemu_thread_self(QemuThread *thread);
int qemu_thread_equal(QemuThread *thread1, QemuThread *thread2);
+void qemu_thread_exit(void *retval);
+
#endif
diff --git a/savevm.c b/savevm.c
index 84065174e..459a3aaf2 100644
--- a/savevm.c
+++ b/savevm.c
@@ -551,6 +551,19 @@ int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
return size1 - size;
}
+static int qemu_peek_byte(QEMUFile *f)
+{
+ if (f->is_write)
+ abort();
+
+ if (f->buf_index >= f->buf_size) {
+ qemu_fill_buffer(f);
+ if (f->buf_index >= f->buf_size)
+ return 0;
+ }
+ return f->buf[f->buf_index];
+}
+
int qemu_get_byte(QEMUFile *f)
{
if (f->is_write)
@@ -1221,10 +1234,16 @@ void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd,
}
}
+static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque);
+static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque);
+
int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
void *opaque, int version_id)
{
VMStateField *field = vmsd->fields;
+ int ret;
if (version_id > vmsd->version_id) {
return -EINVAL;
@@ -1246,7 +1265,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
(!field->field_exists &&
field->version_id <= version_id)) {
void *base_addr = opaque + field->offset;
- int ret, i, n_elems = 1;
+ int i, n_elems = 1;
int size = field->size;
if (field->flags & VMS_VBUFFER) {
@@ -1284,6 +1303,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
}
field++;
}
+ ret = vmstate_subsection_load(f, vmsd, opaque);
+ if (ret != 0) {
+ return ret;
+ }
if (vmsd->post_load) {
return vmsd->post_load(opaque, version_id);
}
@@ -1336,6 +1359,7 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
}
field++;
}
+ vmstate_subsection_save(f, vmsd, opaque);
}
static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
@@ -1364,6 +1388,7 @@ static void vmstate_save(QEMUFile *f, SaveStateEntry *se)
#define QEMU_VM_SECTION_PART 0x02
#define QEMU_VM_SECTION_END 0x03
#define QEMU_VM_SECTION_FULL 0x04
+#define QEMU_VM_SUBSECTION 0x05
int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable,
int shared)
@@ -1552,6 +1577,65 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id)
return NULL;
}
+static const VMStateDescription *vmstate_get_subsection(const VMStateSubsection *sub, char *idstr)
+{
+ while(sub && sub->needed) {
+ if (strcmp(idstr, sub->vmsd->name) == 0) {
+ return sub->vmsd;
+ }
+ sub++;
+ }
+ return NULL;
+}
+
+static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque)
+{
+ while (qemu_peek_byte(f) == QEMU_VM_SUBSECTION) {
+ char idstr[256];
+ int ret;
+ uint8_t version_id, subsection, len;
+ const VMStateDescription *sub_vmsd;
+
+ subsection = qemu_get_byte(f);
+ len = qemu_get_byte(f);
+ qemu_get_buffer(f, (uint8_t *)idstr, len);
+ idstr[len] = 0;
+ version_id = qemu_get_be32(f);
+
+ sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
+ if (sub_vmsd == NULL) {
+ return -ENOENT;
+ }
+ ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
+ if (ret) {
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque)
+{
+ const VMStateSubsection *sub = vmsd->subsections;
+
+ while (sub && sub->needed) {
+ if (sub->needed(opaque)) {
+ const VMStateDescription *vmsd = sub->vmsd;
+ uint8_t len;
+
+ qemu_put_byte(f, QEMU_VM_SUBSECTION);
+ len = strlen(vmsd->name);
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, (uint8_t *)vmsd->name, len);
+ qemu_put_be32(f, vmsd->version_id);
+ vmstate_save_state(f, vmsd, opaque);
+ }
+ sub++;
+ }
+}
+
typedef struct LoadStateEntry {
QLIST_ENTRY(LoadStateEntry) entry;
SaveStateEntry *se;
diff --git a/slirp/cksum.c b/slirp/cksum.c
index 48a179219..e43867da3 100644
--- a/slirp/cksum.c
+++ b/slirp/cksum.c
@@ -47,23 +47,23 @@
int cksum(struct mbuf *m, int len)
{
- register u_int16_t *w;
+ register uint16_t *w;
register int sum = 0;
register int mlen = 0;
int byte_swapped = 0;
union {
- u_int8_t c[2];
- u_int16_t s;
+ uint8_t c[2];
+ uint16_t s;
} s_util;
union {
- u_int16_t s[2];
- u_int32_t l;
+ uint16_t s[2];
+ uint32_t l;
} l_util;
if (m->m_len == 0)
goto cont;
- w = mtod(m, u_int16_t *);
+ w = mtod(m, uint16_t *);
mlen = m->m_len;
@@ -78,8 +78,8 @@ int cksum(struct mbuf *m, int len)
if ((1 & (long) w) && (mlen > 0)) {
REDUCE;
sum <<= 8;
- s_util.c[0] = *(u_int8_t *)w;
- w = (u_int16_t *)((int8_t *)w + 1);
+ s_util.c[0] = *(uint8_t *)w;
+ w = (uint16_t *)((int8_t *)w + 1);
mlen--;
byte_swapped = 1;
}
@@ -111,14 +111,14 @@ int cksum(struct mbuf *m, int len)
REDUCE;
sum <<= 8;
if (mlen == -1) {
- s_util.c[1] = *(u_int8_t *)w;
+ s_util.c[1] = *(uint8_t *)w;
sum += s_util.s;
mlen = 0;
} else
mlen = -1;
} else if (mlen == -1)
- s_util.c[0] = *(u_int8_t *)w;
+ s_util.c[0] = *(uint8_t *)w;
cont:
#ifdef DEBUG
diff --git a/slirp/ip.h b/slirp/ip.h
index 8d185a199..48ea38e5e 100644
--- a/slirp/ip.h
+++ b/slirp/ip.h
@@ -51,17 +51,17 @@
# define NTOHL(d) ((d) = ntohl((d)))
# endif
# ifndef NTOHS
-# define NTOHS(d) ((d) = ntohs((u_int16_t)(d)))
+# define NTOHS(d) ((d) = ntohs((uint16_t)(d)))
# endif
# ifndef HTONL
# define HTONL(d) ((d) = htonl((d)))
# endif
# ifndef HTONS
-# define HTONS(d) ((d) = htons((u_int16_t)(d)))
+# define HTONS(d) ((d) = htons((uint16_t)(d)))
# endif
#endif
-typedef u_int32_t n_long; /* long as received from the net */
+typedef uint32_t n_long; /* long as received from the net */
/*
* Definitions for internet protocol version 4.
@@ -80,16 +80,16 @@ struct ip {
u_int ip_hl:4, /* header length */
ip_v:4; /* version */
#endif
- u_int8_t ip_tos; /* type of service */
- u_int16_t ip_len; /* total length */
- u_int16_t ip_id; /* identification */
- u_int16_t ip_off; /* fragment offset field */
+ uint8_t ip_tos; /* type of service */
+ uint16_t ip_len; /* total length */
+ uint16_t ip_id; /* identification */
+ uint16_t ip_off; /* fragment offset field */
#define IP_DF 0x4000 /* don't fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
- u_int8_t ip_ttl; /* time to live */
- u_int8_t ip_p; /* protocol */
- u_int16_t ip_sum; /* checksum */
+ uint8_t ip_ttl; /* time to live */
+ uint8_t ip_p; /* protocol */
+ uint16_t ip_sum; /* checksum */
struct in_addr ip_src,ip_dst; /* source and dest address */
} __attribute__((packed));
@@ -136,9 +136,9 @@ struct ip {
* Time stamp option structure.
*/
struct ip_timestamp {
- u_int8_t ipt_code; /* IPOPT_TS */
- u_int8_t ipt_len; /* size of structure (variable) */
- u_int8_t ipt_ptr; /* index of current entry */
+ uint8_t ipt_code; /* IPOPT_TS */
+ uint8_t ipt_len; /* size of structure (variable) */
+ uint8_t ipt_ptr; /* index of current entry */
#ifdef HOST_WORDS_BIGENDIAN
u_int ipt_oflw:4, /* overflow counter */
ipt_flg:4; /* flags, see below */
@@ -198,9 +198,9 @@ struct qlink {
*/
struct ipovly {
struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */
- u_int8_t ih_x1; /* (unused) */
- u_int8_t ih_pr; /* protocol */
- u_int16_t ih_len; /* protocol length */
+ uint8_t ih_x1; /* (unused) */
+ uint8_t ih_pr; /* protocol */
+ uint16_t ih_len; /* protocol length */
struct in_addr ih_src; /* source internet address */
struct in_addr ih_dst; /* destination internet address */
} __attribute__((packed));
@@ -215,9 +215,9 @@ struct ipovly {
struct ipq {
struct qlink frag_link; /* to ip headers of fragments */
struct qlink ip_link; /* to other reass headers */
- u_int8_t ipq_ttl; /* time for reass q to live */
- u_int8_t ipq_p; /* protocol of this fragment */
- u_int16_t ipq_id; /* sequence id for reassembly */
+ uint8_t ipq_ttl; /* time for reass q to live */
+ uint8_t ipq_p; /* protocol of this fragment */
+ uint16_t ipq_id; /* sequence id for reassembly */
struct in_addr ipq_src,ipq_dst;
} __attribute__((packed));
@@ -235,7 +235,7 @@ struct ipasfrag {
#define ipf_tos ipf_ip.ip_tos
#define ipf_len ipf_ip.ip_len
#define ipf_next ipf_link.next
-#define ipf_prev ipf_link.prev
+#define ipf_prev ipf_link.prev
/*
* Structure stored in mbuf in inpcb.ip_options
diff --git a/slirp/ip_icmp.h b/slirp/ip_icmp.h
index e793990d2..2692822f8 100644
--- a/slirp/ip_icmp.h
+++ b/slirp/ip_icmp.h
@@ -38,7 +38,7 @@
* Per RFC 792, September 1981.
*/
-typedef u_int32_t n_time;
+typedef uint32_t n_time;
/*
* Structure of an icmp header.
diff --git a/slirp/ip_input.c b/slirp/ip_input.c
index bb101da1a..0fe0ff779 100644
--- a/slirp/ip_input.c
+++ b/slirp/ip_input.c
@@ -477,7 +477,7 @@ ip_dooptions(m)
register struct in_ifaddr *ia;
int opt, optlen, cnt, off, code, type, forward = 0;
struct in_addr *sin, dst;
-typedef u_int32_t n_time;
+typedef uint32_t n_time;
n_time ntime;
dst = ip->ip_dst;
diff --git a/slirp/ip_output.c b/slirp/ip_output.c
index dba278478..542f3180b 100644
--- a/slirp/ip_output.c
+++ b/slirp/ip_output.c
@@ -75,9 +75,9 @@ ip_output(struct socket *so, struct mbuf *m0)
/*
* If small enough for interface, can just send directly.
*/
- if ((u_int16_t)ip->ip_len <= IF_MTU) {
- ip->ip_len = htons((u_int16_t)ip->ip_len);
- ip->ip_off = htons((u_int16_t)ip->ip_off);
+ if ((uint16_t)ip->ip_len <= IF_MTU) {
+ ip->ip_len = htons((uint16_t)ip->ip_len);
+ ip->ip_off = htons((uint16_t)ip->ip_off);
ip->ip_sum = 0;
ip->ip_sum = cksum(m, hlen);
@@ -110,7 +110,7 @@ ip_output(struct socket *so, struct mbuf *m0)
*/
m0 = m;
mhlen = sizeof (struct ip);
- for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) {
+ for (off = hlen + len; off < (uint16_t)ip->ip_len; off += len) {
register struct ip *mhip;
m = m_get(slirp);
if (m == NULL) {
@@ -125,18 +125,18 @@ ip_output(struct socket *so, struct mbuf *m0)
mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);
if (ip->ip_off & IP_MF)
mhip->ip_off |= IP_MF;
- if (off + len >= (u_int16_t)ip->ip_len)
- len = (u_int16_t)ip->ip_len - off;
+ if (off + len >= (uint16_t)ip->ip_len)
+ len = (uint16_t)ip->ip_len - off;
else
mhip->ip_off |= IP_MF;
- mhip->ip_len = htons((u_int16_t)(len + mhlen));
+ mhip->ip_len = htons((uint16_t)(len + mhlen));
if (m_copy(m, m0, off, len) < 0) {
error = -1;
goto sendorfree;
}
- mhip->ip_off = htons((u_int16_t)mhip->ip_off);
+ mhip->ip_off = htons((uint16_t)mhip->ip_off);
mhip->ip_sum = 0;
mhip->ip_sum = cksum(m, mhlen);
*mnext = m;
@@ -147,9 +147,9 @@ ip_output(struct socket *so, struct mbuf *m0)
* and updating header, then send each fragment (in order).
*/
m = m0;
- m_adj(m, hlen + firstlen - (u_int16_t)ip->ip_len);
- ip->ip_len = htons((u_int16_t)m->m_len);
- ip->ip_off = htons((u_int16_t)(ip->ip_off | IP_MF));
+ m_adj(m, hlen + firstlen - (uint16_t)ip->ip_len);
+ ip->ip_len = htons((uint16_t)m->m_len);
+ ip->ip_off = htons((uint16_t)(ip->ip_off | IP_MF));
ip->ip_sum = 0;
ip->ip_sum = cksum(m, hlen);
sendorfree:
diff --git a/slirp/main.h b/slirp/main.h
index 8d09df9d4..0dd8d81ce 100644
--- a/slirp/main.h
+++ b/slirp/main.h
@@ -14,7 +14,7 @@
extern int slirp_socket;
extern int slirp_socket_unit;
extern int slirp_socket_port;
-extern u_int32_t slirp_socket_addr;
+extern uint32_t slirp_socket_addr;
extern char *slirp_socket_passwd;
extern int ctty_closed;
diff --git a/slirp/misc.h b/slirp/misc.h
index da68d0995..ed40a103c 100644
--- a/slirp/misc.h
+++ b/slirp/misc.h
@@ -37,24 +37,24 @@ void do_wait(int);
#define EMU_NOCONNECT 0x10 /* Don't connect */
struct tos_t {
- u_int16_t lport;
- u_int16_t fport;
- u_int8_t tos;
- u_int8_t emu;
+ uint16_t lport;
+ uint16_t fport;
+ uint8_t tos;
+ uint8_t emu;
};
struct emu_t {
- u_int16_t lport;
- u_int16_t fport;
- u_int8_t tos;
- u_int8_t emu;
- struct emu_t *next;
+ uint16_t lport;
+ uint16_t fport;
+ uint8_t tos;
+ uint8_t emu;
+ struct emu_t *next;
};
extern int x_port, x_server, x_display;
int show_x(char *, struct socket *);
-void redir_x(u_int32_t, int, int, int);
+void redir_x(uint32_t, int, int, int);
void slirp_insque(void *, void *);
void slirp_remque(void *);
int add_exec(struct ex_list **, int, char *, struct in_addr, int);
diff --git a/slirp/slirp.h b/slirp/slirp.h
index 98a26442a..3a5d592fb 100644
--- a/slirp/slirp.h
+++ b/slirp/slirp.h
@@ -7,10 +7,6 @@
#ifdef _WIN32
# include <inttypes.h>
-typedef uint8_t u_int8_t;
-typedef uint16_t u_int16_t;
-typedef uint32_t u_int32_t;
-typedef uint64_t u_int64_t;
typedef char *caddr_t;
# include <windows.h>
@@ -38,35 +34,6 @@ typedef char *caddr_t;
#include <sys/time.h>
-#ifdef NEED_TYPEDEFS
-typedef char int8_t;
-typedef unsigned char u_int8_t;
-
-# if SIZEOF_SHORT == 2
- typedef short int16_t;
- typedef unsigned short u_int16_t;
-# else
-# if SIZEOF_INT == 2
- typedef int int16_t;
- typedef unsigned int u_int16_t;
-# else
- #error Cannot find a type with sizeof() == 2
-# endif
-# endif
-
-# if SIZEOF_SHORT == 4
- typedef short int32_t;
- typedef unsigned short u_int32_t;
-# else
-# if SIZEOF_INT == 4
- typedef int int32_t;
- typedef unsigned int u_int32_t;
-# else
- #error Cannot find a type with sizeof() == 4
-# endif
-# endif
-#endif /* NEED_TYPEDEFS */
-
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
@@ -233,7 +200,7 @@ struct Slirp {
/* ip states */
struct ipq ipq; /* ip reass. queue */
- u_int16_t ip_id; /* ip packet ctr, for ids */
+ uint16_t ip_id; /* ip packet ctr, for ids */
/* bootp/dhcp states */
BOOTPClient bootp_clients[NB_BOOTP_CLIENTS];
@@ -243,7 +210,7 @@ struct Slirp {
struct socket tcb;
struct socket *tcp_last_so;
tcp_seq tcp_iss; /* tcp initial send seq # */
- u_int32_t tcp_now; /* for RFC 1323 timestamps */
+ uint32_t tcp_now; /* for RFC 1323 timestamps */
/* udp states */
struct socket udb;
@@ -339,7 +306,7 @@ void tcp_sockclosed(struct tcpcb *);
int tcp_fconnect(struct socket *);
void tcp_connect(struct socket *);
int tcp_attach(struct socket *);
-u_int8_t tcp_tos(struct socket *);
+uint8_t tcp_tos(struct socket *);
int tcp_emu(struct socket *, struct mbuf *);
int tcp_ctl(struct socket *);
struct tcpcb *tcp_drop(struct tcpcb *tp, int err);
diff --git a/slirp/slirp_config.h b/slirp/slirp_config.h
index a40248eb7..f19c7034c 100644
--- a/slirp/slirp_config.h
+++ b/slirp/slirp_config.h
@@ -133,12 +133,6 @@
/* Define if your compiler doesn't like prototypes */
#undef NO_PROTOTYPES
-/* Define if you don't have u_int32_t etc. typedef'd */
-#undef NEED_TYPEDEFS
-#ifdef __sun__
-#define NEED_TYPEDEFS
-#endif
-
/* Define to sizeof(char) */
#define SIZEOF_CHAR 1
diff --git a/slirp/socket.c b/slirp/socket.c
index eaad77af8..611923424 100644
--- a/slirp/socket.c
+++ b/slirp/socket.c
@@ -580,7 +580,7 @@ sosendto(struct socket *so, struct mbuf *m)
* Listen for incoming TCP connections
*/
struct socket *
-tcp_listen(Slirp *slirp, u_int32_t haddr, u_int hport, u_int32_t laddr,
+tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
u_int lport, int flags)
{
struct sockaddr_in addr;
diff --git a/slirp/socket.h b/slirp/socket.h
index 6e85d0358..857b0da31 100644
--- a/slirp/socket.h
+++ b/slirp/socket.h
@@ -31,11 +31,11 @@ struct socket {
int so_urgc;
struct in_addr so_faddr; /* foreign host table entry */
struct in_addr so_laddr; /* local host table entry */
- u_int16_t so_fport; /* foreign port */
- u_int16_t so_lport; /* local port */
+ uint16_t so_fport; /* foreign port */
+ uint16_t so_lport; /* local port */
- u_int8_t so_iptos; /* Type of service */
- u_int8_t so_emu; /* Is the socket emulated? */
+ uint8_t so_iptos; /* Type of service */
+ uint8_t so_emu; /* Is the socket emulated? */
u_char so_type; /* Type of socket, UDP or TCP */
int so_state; /* internal state flags SS_*, below */
@@ -83,7 +83,7 @@ int sosendoob(struct socket *);
int sowrite(struct socket *);
void sorecvfrom(struct socket *);
int sosendto(struct socket *, struct mbuf *);
-struct socket * tcp_listen(Slirp *, u_int32_t, u_int, u_int32_t, u_int,
+struct socket * tcp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
int);
void soisfconnecting(register struct socket *);
void soisfconnected(register struct socket *);
diff --git a/slirp/tcp.h b/slirp/tcp.h
index c7e3457b7..9d0683662 100644
--- a/slirp/tcp.h
+++ b/slirp/tcp.h
@@ -33,7 +33,7 @@
#ifndef _TCP_H_
#define _TCP_H_
-typedef u_int32_t tcp_seq;
+typedef uint32_t tcp_seq;
#define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */
#define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */
@@ -46,8 +46,8 @@ typedef u_int32_t tcp_seq;
* Per RFC 793, September, 1981.
*/
struct tcphdr {
- u_int16_t th_sport; /* source port */
- u_int16_t th_dport; /* destination port */
+ uint16_t th_sport; /* source port */
+ uint16_t th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
#ifdef HOST_WORDS_BIGENDIAN
@@ -57,16 +57,16 @@ struct tcphdr {
u_int th_x2:4, /* (unused) */
th_off:4; /* data offset */
#endif
- u_int8_t th_flags;
+ uint8_t th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
- u_int16_t th_win; /* window */
- u_int16_t th_sum; /* checksum */
- u_int16_t th_urp; /* urgent pointer */
+ uint16_t th_win; /* window */
+ uint16_t th_sum; /* checksum */
+ uint16_t th_urp; /* urgent pointer */
};
#include "tcp_var.h"
diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c
index 2808e3e4e..e4a77310d 100644
--- a/slirp/tcp_input.c
+++ b/slirp/tcp_input.c
@@ -280,7 +280,7 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
memset(&ti->ti_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
ti->ti_x1 = 0;
- ti->ti_len = htons((u_int16_t)tlen);
+ ti->ti_len = htons((uint16_t)tlen);
len = sizeof(struct ip ) + tlen;
if(cksum(m, len)) {
goto drop;
@@ -1289,7 +1289,7 @@ drop:
static void
tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti)
{
- u_int16_t mss;
+ uint16_t mss;
int opt, optlen;
DEBUG_CALL("tcp_dooptions");
diff --git a/slirp/tcp_output.c b/slirp/tcp_output.c
index 0d6011ac6..779314bf9 100644
--- a/slirp/tcp_output.c
+++ b/slirp/tcp_output.c
@@ -263,11 +263,11 @@ send:
if (flags & TH_SYN) {
tp->snd_nxt = tp->iss;
if ((tp->t_flags & TF_NOOPT) == 0) {
- u_int16_t mss;
+ uint16_t mss;
opt[0] = TCPOPT_MAXSEG;
opt[1] = 4;
- mss = htons((u_int16_t) tcp_mss(tp, 0));
+ mss = htons((uint16_t) tcp_mss(tp, 0));
memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss));
optlen = 4;
}
@@ -364,10 +364,10 @@ send:
win = (long)TCP_MAXWIN << tp->rcv_scale;
if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
win = (long)(tp->rcv_adv - tp->rcv_nxt);
- ti->ti_win = htons((u_int16_t) (win>>tp->rcv_scale));
+ ti->ti_win = htons((uint16_t) (win>>tp->rcv_scale));
if (SEQ_GT(tp->snd_up, tp->snd_una)) {
- ti->ti_urp = htons((u_int16_t)(tp->snd_up - ntohl(ti->ti_seq)));
+ ti->ti_urp = htons((uint16_t)(tp->snd_up - ntohl(ti->ti_seq)));
ti->ti_flags |= TH_URG;
} else
/*
@@ -383,7 +383,7 @@ send:
* checksum extended header and data.
*/
if (len + optlen)
- ti->ti_len = htons((u_int16_t)(sizeof (struct tcphdr) +
+ ti->ti_len = htons((uint16_t)(sizeof (struct tcphdr) +
optlen + len));
ti->ti_sum = cksum(m, (int)(hdrlen + len));
diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c
index 0a370f101..b661d2623 100644
--- a/slirp/tcp_subr.c
+++ b/slirp/tcp_subr.c
@@ -134,8 +134,8 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
m->m_len = sizeof (struct tcpiphdr);
tlen = 0;
#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
- xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_int32_t);
- xchg(ti->ti_dport, ti->ti_sport, u_int16_t);
+ xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t);
+ xchg(ti->ti_dport, ti->ti_sport, uint16_t);
#undef xchg
}
ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen));
@@ -150,9 +150,9 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
ti->ti_off = sizeof (struct tcphdr) >> 2;
ti->ti_flags = flags;
if (tp)
- ti->ti_win = htons((u_int16_t) (win >> tp->rcv_scale));
+ ti->ti_win = htons((uint16_t) (win >> tp->rcv_scale));
else
- ti->ti_win = htons((u_int16_t)win);
+ ti->ti_win = htons((uint16_t)win);
ti->ti_urp = 0;
ti->ti_sum = 0;
ti->ti_sum = cksum(m, tlen);
@@ -491,7 +491,7 @@ static struct emu_t *tcpemu = NULL;
/*
* Return TOS according to the above table
*/
-u_int8_t
+uint8_t
tcp_tos(struct socket *so)
{
int i = 0;
@@ -548,7 +548,7 @@ tcp_emu(struct socket *so, struct mbuf *m)
Slirp *slirp = so->slirp;
u_int n1, n2, n3, n4, n5, n6;
char buff[257];
- u_int32_t laddr;
+ uint32_t laddr;
u_int lport;
char *bptr;
diff --git a/slirp/tcp_var.h b/slirp/tcp_var.h
index 4ffbe04a1..004193fb6 100644
--- a/slirp/tcp_var.h
+++ b/slirp/tcp_var.h
@@ -75,9 +75,9 @@ struct tcpcb {
tcp_seq snd_wl1; /* window update seg seq number */
tcp_seq snd_wl2; /* window update seg ack number */
tcp_seq iss; /* initial send sequence number */
- u_int32_t snd_wnd; /* send window */
+ uint32_t snd_wnd; /* send window */
/* receive sequence variables */
- u_int32_t rcv_wnd; /* receive window */
+ uint32_t rcv_wnd; /* receive window */
tcp_seq rcv_nxt; /* receive next */
tcp_seq rcv_up; /* receive urgent pointer */
tcp_seq irs; /* initial receive sequence number */
@@ -91,8 +91,8 @@ struct tcpcb {
* used to recognize retransmits
*/
/* congestion control (for slow start, source quench, retransmit after loss) */
- u_int32_t snd_cwnd; /* congestion-controlled window */
- u_int32_t snd_ssthresh; /* snd_cwnd size threshold for
+ uint32_t snd_cwnd; /* congestion-controlled window */
+ uint32_t snd_ssthresh; /* snd_cwnd size threshold for
* for slow start exponential to
* linear switch
*/
@@ -106,7 +106,7 @@ struct tcpcb {
short t_srtt; /* smoothed round-trip time */
short t_rttvar; /* variance in round-trip time */
u_short t_rttmin; /* minimum rtt allowed */
- u_int32_t max_sndwnd; /* largest window peer has offered */
+ uint32_t max_sndwnd; /* largest window peer has offered */
/* out-of-band data */
char t_oobflags; /* have some */
@@ -120,8 +120,8 @@ struct tcpcb {
u_char rcv_scale; /* window scaling for recv window */
u_char request_r_scale; /* pending window scaling */
u_char requested_s_scale;
- u_int32_t ts_recent; /* timestamp echo data */
- u_int32_t ts_recent_age; /* when last updated */
+ uint32_t ts_recent; /* timestamp echo data */
+ uint32_t ts_recent_age; /* when last updated */
tcp_seq last_ack_sent;
};
diff --git a/slirp/tftp.c b/slirp/tftp.c
index 67e9f2b9d..55e4692ac 100644
--- a/slirp/tftp.c
+++ b/slirp/tftp.c
@@ -92,8 +92,8 @@ static int tftp_session_find(Slirp *slirp, struct tftp_t *tp)
return -1;
}
-static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr,
- u_int8_t *buf, int len)
+static int tftp_read_data(struct tftp_session *spt, uint16_t block_nr,
+ uint8_t *buf, int len)
{
int fd;
int bytes_read = 0;
@@ -155,7 +155,7 @@ static int tftp_send_oack(struct tftp_session *spt,
}
static void tftp_send_error(struct tftp_session *spt,
- u_int16_t errorcode, const char *msg,
+ uint16_t errorcode, const char *msg,
struct tftp_t *recv_tp)
{
struct sockaddr_in saddr, daddr;
@@ -194,7 +194,7 @@ out:
}
static int tftp_send_data(struct tftp_session *spt,
- u_int16_t block_nr,
+ uint16_t block_nr,
struct tftp_t *recv_tp)
{
struct sockaddr_in saddr, daddr;
diff --git a/slirp/tftp.h b/slirp/tftp.h
index 1415c8527..b9f0847eb 100644
--- a/slirp/tftp.h
+++ b/slirp/tftp.h
@@ -16,17 +16,17 @@
struct tftp_t {
struct ip ip;
struct udphdr udp;
- u_int16_t tp_op;
+ uint16_t tp_op;
union {
struct {
- u_int16_t tp_block_nr;
- u_int8_t tp_buf[512];
+ uint16_t tp_block_nr;
+ uint8_t tp_buf[512];
} tp_data;
struct {
- u_int16_t tp_error_code;
- u_int8_t tp_msg[512];
+ uint16_t tp_error_code;
+ uint8_t tp_msg[512];
} tp_error;
- u_int8_t tp_buf[512 + 2];
+ uint8_t tp_buf[512 + 2];
} x;
};
@@ -35,7 +35,7 @@ struct tftp_session {
char *filename;
struct in_addr client_ip;
- u_int16_t client_port;
+ uint16_t client_port;
int timestamp;
};
diff --git a/slirp/udp.c b/slirp/udp.c
index d6c39b97b..02b3793e9 100644
--- a/slirp/udp.c
+++ b/slirp/udp.c
@@ -41,7 +41,7 @@
#include <slirp.h>
#include "ip_icmp.h"
-static u_int8_t udp_tos(struct socket *so);
+static uint8_t udp_tos(struct socket *so);
void
udp_init(Slirp *slirp)
@@ -88,7 +88,7 @@ udp_input(register struct mbuf *m, int iphlen)
* Make mbuf data length reflect UDP length.
* If not enough data to reflect UDP length, drop.
*/
- len = ntohs((u_int16_t)uh->uh_ulen);
+ len = ntohs((uint16_t)uh->uh_ulen);
if (ip->ip_len != len) {
if (len > ip->ip_len) {
@@ -321,7 +321,7 @@ static const struct tos_t udptos[] = {
{0, 0, 0, 0}
};
-static u_int8_t
+static uint8_t
udp_tos(struct socket *so)
{
int i = 0;
@@ -339,7 +339,7 @@ udp_tos(struct socket *so)
}
struct socket *
-udp_listen(Slirp *slirp, u_int32_t haddr, u_int hport, u_int32_t laddr,
+udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
u_int lport, int flags)
{
struct sockaddr_in addr;
diff --git a/slirp/udp.h b/slirp/udp.h
index 47d2f3d4c..9b5c3cf56 100644
--- a/slirp/udp.h
+++ b/slirp/udp.h
@@ -41,10 +41,10 @@
* Per RFC 768, September, 1981.
*/
struct udphdr {
- u_int16_t uh_sport; /* source port */
- u_int16_t uh_dport; /* destination port */
- int16_t uh_ulen; /* udp length */
- u_int16_t uh_sum; /* udp checksum */
+ uint16_t uh_sport; /* source port */
+ uint16_t uh_dport; /* destination port */
+ int16_t uh_ulen; /* udp length */
+ uint16_t uh_sum; /* udp checksum */
};
/*
@@ -78,7 +78,7 @@ void udp_input(register struct mbuf *, int);
int udp_output(struct socket *, struct mbuf *, struct sockaddr_in *);
int udp_attach(struct socket *);
void udp_detach(struct socket *);
-struct socket * udp_listen(Slirp *, u_int32_t, u_int, u_int32_t, u_int,
+struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
int);
int udp_output2(struct socket *so, struct mbuf *m,
struct sockaddr_in *saddr, struct sockaddr_in *daddr,
diff --git a/target-i386/translate.c b/target-i386/translate.c
index 2fcc02616..7b6e3c2ea 100644
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -2310,10 +2310,7 @@ static inline void gen_jcc(DisasContext *s, int b,
int l1, l2, cc_op;
cc_op = s->cc_op;
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
if (s->jmp_opt) {
l1 = gen_new_label();
gen_jcc1(s, cc_op, b, l1);
@@ -2322,7 +2319,7 @@ static inline void gen_jcc(DisasContext *s, int b,
gen_set_label(l1);
gen_goto_tb(s, 1, val);
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
} else {
l1 = gen_new_label();
@@ -2400,11 +2397,11 @@ static void gen_movl_seg_T0(DisasContext *s, int seg_reg, target_ulong cur_eip)
stop as a special handling must be done to disable hardware
interrupts for the next instruction */
if (seg_reg == R_SS || (s->code32 && seg_reg < R_FS))
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
} else {
gen_op_movl_seg_T0_vm(seg_reg);
if (seg_reg == R_SS)
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
}
@@ -2672,7 +2669,7 @@ static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip)
gen_op_set_cc_op(s->cc_op);
gen_jmp_im(cur_eip);
gen_helper_raise_exception(tcg_const_i32(trapno));
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
/* an interrupt is different from an exception because of the
@@ -2685,7 +2682,7 @@ static void gen_interrupt(DisasContext *s, int intno,
gen_jmp_im(cur_eip);
gen_helper_raise_interrupt(tcg_const_i32(intno),
tcg_const_i32(next_eip - cur_eip));
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
static void gen_debug(DisasContext *s, target_ulong cur_eip)
@@ -2694,7 +2691,7 @@ static void gen_debug(DisasContext *s, target_ulong cur_eip)
gen_op_set_cc_op(s->cc_op);
gen_jmp_im(cur_eip);
gen_helper_debug();
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
/* generate a generic end of block. Trace exception is also generated
@@ -2716,7 +2713,7 @@ static void gen_eob(DisasContext *s)
} else {
tcg_gen_exit_tb(0);
}
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
/* generate a jump to eip. No segment change must happen before as a
@@ -2724,12 +2721,9 @@ static void gen_eob(DisasContext *s)
static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num)
{
if (s->jmp_opt) {
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_goto_tb(s, tb_num, eip);
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
} else {
gen_jmp_im(eip);
gen_eob(s);
@@ -6901,10 +6895,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
if (!s->pe) {
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
} else {
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
gen_helper_sysenter();
gen_eob(s);
@@ -6917,10 +6908,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
if (!s->pe) {
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
} else {
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
gen_helper_sysexit(tcg_const_i32(dflag));
gen_eob(s);
@@ -6929,10 +6917,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
#ifdef TARGET_X86_64
case 0x105: /* syscall */
/* XXX: is it usable in real mode ? */
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
gen_helper_syscall(tcg_const_i32(s->pc - pc_start));
gen_eob(s);
@@ -6941,10 +6926,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
if (!s->pe) {
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
} else {
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
gen_helper_sysret(tcg_const_i32(s->dflag));
/* condition codes are modified only in long mode */
@@ -6968,7 +6950,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
gen_op_set_cc_op(s->cc_op);
gen_jmp_im(pc_start - s->cs_base);
gen_helper_hlt(tcg_const_i32(s->pc - pc_start));
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
break;
case 0x100:
@@ -7085,10 +7067,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) ||
s->cpl != 0)
goto illegal_op;
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
gen_helper_mwait(tcg_const_i32(s->pc - pc_start));
gen_eob(s);
@@ -7125,7 +7104,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
gen_helper_vmrun(tcg_const_i32(s->aflag),
tcg_const_i32(s->pc - pc_start));
tcg_gen_exit_tb(0);
- s->is_jmp = 3;
+ s->is_jmp = DISAS_TB_JUMP;
}
break;
case 1: /* VMMCALL */
@@ -7613,10 +7592,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
gen_svm_check_intercept(s, pc_start, SVM_EXIT_RSM);
if (!(s->flags & HF_SMM_MASK))
goto illegal_op;
- if (s->cc_op != CC_OP_DYNAMIC) {
- gen_op_set_cc_op(s->cc_op);
- s->cc_op = CC_OP_DYNAMIC;
- }
+ gen_update_cc_op(s);
gen_jmp_im(s->pc - s->cs_base);
gen_helper_rsm();
gen_eob(s);
diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c
index ca54e2c30..f61829081 100644
--- a/target-microblaze/translate.c
+++ b/target-microblaze/translate.c
@@ -63,8 +63,7 @@ static TCGv env_iflags;
/* This is the state at translation time. */
typedef struct DisasContext {
CPUState *env;
- target_ulong pc, ppc;
- target_ulong cache_pc;
+ target_ulong pc;
/* Decoder. */
int type_b;
@@ -153,6 +152,14 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest)
}
}
+/* True if ALU operand b is a small immediate that may deserve
+ faster treatment. */
+static inline int dec_alu_op_b_is_small_imm(DisasContext *dc)
+{
+ /* Immediate insn without the imm prefix ? */
+ return dc->type_b && !(dc->tb_flags & IMM_FLAG);
+}
+
static inline TCGv *dec_alu_op_b(DisasContext *dc)
{
if (dc->type_b) {
@@ -780,6 +787,13 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t)
/* Treat the fast cases first. */
if (!dc->type_b) {
+ /* If any of the regs is r0, return a ptr to the other. */
+ if (dc->ra == 0) {
+ return &cpu_R[dc->rb];
+ } else if (dc->rb == 0) {
+ return &cpu_R[dc->ra];
+ }
+
*t = tcg_temp_new();
tcg_gen_add_tl(*t, cpu_R[dc->ra], cpu_R[dc->rb]);
return t;
@@ -904,50 +918,24 @@ static void dec_store(DisasContext *dc)
static inline void eval_cc(DisasContext *dc, unsigned int cc,
TCGv d, TCGv a, TCGv b)
{
- int l1;
-
switch (cc) {
case CC_EQ:
- l1 = gen_new_label();
- tcg_gen_movi_tl(env_btaken, 1);
- tcg_gen_brcond_tl(TCG_COND_EQ, a, b, l1);
- tcg_gen_movi_tl(env_btaken, 0);
- gen_set_label(l1);
+ tcg_gen_setcond_tl(TCG_COND_EQ, d, a, b);
break;
case CC_NE:
- l1 = gen_new_label();
- tcg_gen_movi_tl(env_btaken, 1);
- tcg_gen_brcond_tl(TCG_COND_NE, a, b, l1);
- tcg_gen_movi_tl(env_btaken, 0);
- gen_set_label(l1);
+ tcg_gen_setcond_tl(TCG_COND_NE, d, a, b);
break;
case CC_LT:
- l1 = gen_new_label();
- tcg_gen_movi_tl(env_btaken, 1);
- tcg_gen_brcond_tl(TCG_COND_LT, a, b, l1);
- tcg_gen_movi_tl(env_btaken, 0);
- gen_set_label(l1);
+ tcg_gen_setcond_tl(TCG_COND_LT, d, a, b);
break;
case CC_LE:
- l1 = gen_new_label();
- tcg_gen_movi_tl(env_btaken, 1);
- tcg_gen_brcond_tl(TCG_COND_LE, a, b, l1);
- tcg_gen_movi_tl(env_btaken, 0);
- gen_set_label(l1);
+ tcg_gen_setcond_tl(TCG_COND_LE, d, a, b);
break;
case CC_GE:
- l1 = gen_new_label();
- tcg_gen_movi_tl(env_btaken, 1);
- tcg_gen_brcond_tl(TCG_COND_GE, a, b, l1);
- tcg_gen_movi_tl(env_btaken, 0);
- gen_set_label(l1);
+ tcg_gen_setcond_tl(TCG_COND_GE, d, a, b);
break;
case CC_GT:
- l1 = gen_new_label();
- tcg_gen_movi_tl(env_btaken, 1);
- tcg_gen_brcond_tl(TCG_COND_GT, a, b, l1);
- tcg_gen_movi_tl(env_btaken, 0);
- gen_set_label(l1);
+ tcg_gen_setcond_tl(TCG_COND_GT, d, a, b);
break;
default:
cpu_abort(dc->env, "Unknown condition code %x.\n", cc);
@@ -984,10 +972,16 @@ static void dec_bcc(DisasContext *dc)
cpu_env, offsetof(CPUState, bimm));
}
- tcg_gen_movi_tl(env_btarget, dc->pc);
- tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc)));
- eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0));
+ if (dec_alu_op_b_is_small_imm(dc)) {
+ int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend. */
+
+ tcg_gen_movi_tl(env_btarget, dc->pc + offset);
+ } else {
+ tcg_gen_movi_tl(env_btarget, dc->pc);
+ tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc)));
+ }
dc->jmp = JMP_INDIRECT;
+ eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0));
}
static void dec_br(DisasContext *dc)
@@ -1031,13 +1025,13 @@ static void dec_br(DisasContext *dc)
}
}
} else {
- if (!dc->type_b || (dc->tb_flags & IMM_FLAG)) {
+ if (dec_alu_op_b_is_small_imm(dc)) {
+ dc->jmp = JMP_DIRECT;
+ dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm);
+ } else {
tcg_gen_movi_tl(env_btaken, 1);
tcg_gen_movi_tl(env_btarget, dc->pc);
tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc)));
- } else {
- dc->jmp = JMP_DIRECT;
- dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm);
}
}
}
@@ -1279,9 +1273,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
dc->is_jmp = DISAS_NEXT;
dc->jmp = 0;
dc->delayed_branch = !!(dc->tb_flags & D_FLAG);
- dc->ppc = pc_start;
dc->pc = pc_start;
- dc->cache_pc = -1;
dc->singlestep_enabled = env->singlestep_enabled;
dc->cpustate_changed = 0;
dc->abort_at_next_insn = 0;
@@ -1337,7 +1329,6 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
decode(dc);
if (dc->clear_imm)
dc->tb_flags &= ~IMM_FLAG;
- dc->ppc = dc->pc;
dc->pc += 4;
num_insns++;
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index 81051aa00..b8e6feefc 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -598,7 +598,7 @@ void cpu_mips_start_count(CPUState *env);
void cpu_mips_stop_count(CPUState *env);
/* mips_int.c */
-void cpu_mips_update_irq (CPUState *env);
+void cpu_mips_soft_irq(CPUState *env, int irq, int level);
/* helper.c */
int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
diff --git a/target-mips/helper.h b/target-mips/helper.h
index a6ba75dfb..cb13fb235 100644
--- a/target-mips/helper.h
+++ b/target-mips/helper.h
@@ -2,7 +2,6 @@
DEF_HELPER_2(raise_exception_err, void, i32, int)
DEF_HELPER_1(raise_exception, void, i32)
-DEF_HELPER_0(interrupt_restart, void)
#ifdef TARGET_MIPS64
DEF_HELPER_3(ldl, tl, tl, tl, int)
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index 8ae510adc..a619b7261 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -46,18 +46,6 @@ void helper_raise_exception (uint32_t exception)
helper_raise_exception_err(exception, 0);
}
-void helper_interrupt_restart (void)
-{
- if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
- !(env->CP0_Status & (1 << CP0St_ERL)) &&
- !(env->hflags & MIPS_HFLAG_DM) &&
- (env->CP0_Status & (1 << CP0St_IE)) &&
- (env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask)) {
- env->CP0_Cause &= ~(0x1f << CP0Ca_EC);
- helper_raise_exception(EXCP_EXT_INTERRUPT);
- }
-}
-
#if !defined(CONFIG_USER_ONLY)
static void do_restore_state (void *pc_ptr)
{
@@ -1313,7 +1301,6 @@ void helper_mtc0_status (target_ulong arg1)
default: cpu_abort(env, "Invalid MMU mode!\n"); break;
}
}
- cpu_mips_update_irq(env);
}
void helper_mttc0_status(target_ulong arg1)
@@ -1347,6 +1334,7 @@ void helper_mtc0_cause (target_ulong arg1)
{
uint32_t mask = 0x00C00300;
uint32_t old = env->CP0_Cause;
+ int i;
if (env->insn_flags & ISA_MIPS32R2)
mask |= 1 << CP0Ca_DC;
@@ -1360,10 +1348,11 @@ void helper_mtc0_cause (target_ulong arg1)
cpu_mips_start_count(env);
}
- /* Handle the software interrupt as an hardware one, as they
- are very similar */
- if (arg1 & CP0Ca_IP_mask) {
- cpu_mips_update_irq(env);
+ /* Set/reset software interrupts */
+ for (i = 0 ; i < 2 ; i++) {
+ if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) {
+ cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i)));
+ }
}
}
@@ -1793,8 +1782,6 @@ target_ulong helper_di (void)
target_ulong t0 = env->CP0_Status;
env->CP0_Status = t0 & ~(1 << CP0St_IE);
- cpu_mips_update_irq(env);
-
return t0;
}
@@ -1803,8 +1790,6 @@ target_ulong helper_ei (void)
target_ulong t0 = env->CP0_Status;
env->CP0_Status = t0 | (1 << CP0St_IE);
- cpu_mips_update_irq(env);
-
return t0;
}
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 716827338..6c72dee1b 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -5190,7 +5190,17 @@ static void gen_dmtc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int
switch (sel) {
case 0:
save_cpu_state(ctx, 1);
+ /* Mark as an IO operation because we may trigger a software
+ interrupt. */
+ if (use_icount) {
+ gen_io_start();
+ }
gen_helper_mtc0_cause(arg);
+ if (use_icount) {
+ gen_io_end();
+ }
+ /* Stop translation as we may have triggered an intetrupt */
+ ctx->bstate = BS_STOP;
rn = "Cause";
break;
default:
@@ -12365,7 +12375,6 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
} else {
switch (ctx.bstate) {
case BS_STOP:
- gen_helper_interrupt_restart();
gen_goto_tb(&ctx, 0, ctx.pc);
break;
case BS_NONE:
@@ -12373,7 +12382,6 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
gen_goto_tb(&ctx, 0, ctx.pc);
break;
case BS_EXCP:
- gen_helper_interrupt_restart();
tcg_gen_exit_tb(0);
break;
case BS_BRANCH:
diff --git a/tests/sha1.c b/tests/sha1.c
index 3a7655582..93b7c8e80 100644
--- a/tests/sha1.c
+++ b/tests/sha1.c
@@ -23,7 +23,7 @@ A million repetitions of "a"
#include <stdio.h>
#include <string.h>
-#include <sys/types.h> /* for u_int*_t */
+#include <stdint.h>
/* ================ sha1.h ================ */
/*
@@ -33,14 +33,14 @@ By Steve Reid <steve@edmweb.com>
*/
typedef struct {
- u_int32_t state[5];
- u_int32_t count[2];
+ uint32_t state[5];
+ uint32_t count[2];
unsigned char buffer[64];
} SHA1_CTX;
-void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]);
+void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
void SHA1Init(SHA1_CTX* context);
-void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len);
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
/* ================ end of sha1.h ================ */
#include <endian.h>
@@ -70,12 +70,12 @@ void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
/* Hash a single 512-bit block. This is the core of the algorithm. */
-void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64])
+void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
{
-u_int32_t a, b, c, d, e;
+uint32_t a, b, c, d, e;
typedef union {
unsigned char c[64];
- u_int32_t l[16];
+ uint32_t l[16];
} CHAR64LONG16;
#ifdef SHA1HANDSOFF
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
@@ -145,10 +145,10 @@ void SHA1Init(SHA1_CTX* context)
/* Run your data through this. */
-void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len)
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
{
-u_int32_t i;
-u_int32_t j;
+uint32_t i;
+uint32_t j;
j = context->count[0];
if ((context->count[0] += len << 3) < j)
@@ -186,7 +186,7 @@ unsigned char c;
for (i = 0; i < 2; i++)
{
- u_int32_t t = context->count[i];
+ uint32_t t = context->count[i];
int j;
for (j = 0; j < 4; t >>= 8, j++)
diff --git a/cocoa.m b/ui/cocoa.m
index 56c789a98..56c789a98 100644
--- a/cocoa.m
+++ b/ui/cocoa.m
diff --git a/curses.c b/ui/curses.c
index ed3165e45..ed3165e45 100644
--- a/curses.c
+++ b/ui/curses.c
diff --git a/curses_keys.h b/ui/curses_keys.h
index 1decd1119..1decd1119 100644
--- a/curses_keys.h
+++ b/ui/curses_keys.h
diff --git a/d3des.c b/ui/d3des.c
index 60c840ed5..60c840ed5 100644
--- a/d3des.c
+++ b/ui/d3des.c
diff --git a/d3des.h b/ui/d3des.h
index ea3da44ce..ea3da44ce 100644
--- a/d3des.h
+++ b/ui/d3des.h
diff --git a/keymaps.c b/ui/keymaps.c
index 78c7ea375..78c7ea375 100644
--- a/keymaps.c
+++ b/ui/keymaps.c
diff --git a/keymaps.h b/ui/keymaps.h
index a7600d575..a7600d575 100644
--- a/keymaps.h
+++ b/ui/keymaps.h
diff --git a/sdl.c b/ui/sdl.c
index 0072680f4..0072680f4 100644
--- a/sdl.c
+++ b/ui/sdl.c
diff --git a/sdl_keysym.h b/ui/sdl_keysym.h
index ee904805d..ee904805d 100644
--- a/sdl_keysym.h
+++ b/ui/sdl_keysym.h
diff --git a/sdl_zoom.c b/ui/sdl_zoom.c
index a986c7c14..a986c7c14 100644
--- a/sdl_zoom.c
+++ b/ui/sdl_zoom.c
diff --git a/sdl_zoom.h b/ui/sdl_zoom.h
index 74955bc94..74955bc94 100644
--- a/sdl_zoom.h
+++ b/ui/sdl_zoom.h
diff --git a/sdl_zoom_template.h b/ui/sdl_zoom_template.h
index 64bbca849..64bbca849 100644
--- a/sdl_zoom_template.h
+++ b/ui/sdl_zoom_template.h
diff --git a/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index a51ddc8a1..a51ddc8a1 100644
--- a/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
diff --git a/vnc-auth-sasl.h b/ui/vnc-auth-sasl.h
index fd9b18a8f..fd9b18a8f 100644
--- a/vnc-auth-sasl.h
+++ b/ui/vnc-auth-sasl.h
diff --git a/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index 07c169186..07c169186 100644
--- a/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
diff --git a/vnc-auth-vencrypt.h b/ui/vnc-auth-vencrypt.h
index 9f674c517..9f674c517 100644
--- a/vnc-auth-vencrypt.h
+++ b/ui/vnc-auth-vencrypt.h
diff --git a/vnchextile.h b/ui/vnc-enc-hextile-template.h
index b9f9f5ef8..b9f9f5ef8 100644
--- a/vnchextile.h
+++ b/ui/vnc-enc-hextile-template.h
diff --git a/vnc-encoding-hextile.c b/ui/vnc-enc-hextile.c
index 728f25e5d..364a49115 100644
--- a/vnc-encoding-hextile.c
+++ b/ui/vnc-enc-hextile.c
@@ -33,32 +33,32 @@ static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h)
}
#define BPP 8
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
#undef BPP
#define BPP 16
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
#undef BPP
#define BPP 32
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
#undef BPP
#define GENERIC
#define BPP 8
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
#undef BPP
#undef GENERIC
#define GENERIC
#define BPP 16
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
#undef BPP
#undef GENERIC
#define GENERIC
#define BPP 32
-#include "vnchextile.h"
+#include "vnc-enc-hextile-template.h"
#undef BPP
#undef GENERIC
@@ -75,7 +75,7 @@ int vnc_hextile_send_framebuffer_update(VncState *vs, int x,
has_fg = has_bg = 0;
for (j = y; j < (y + h); j += 16) {
for (i = x; i < (x + w); i += 16) {
- vs->send_hextile_tile(vs, i, j,
+ vs->hextile.send_tile(vs, i, j,
MIN(16, x + w - i), MIN(16, y + h - j),
last_bg, last_fg, &has_bg, &has_fg);
}
@@ -91,25 +91,25 @@ void vnc_hextile_set_pixel_conversion(VncState *vs, int generic)
if (!generic) {
switch (vs->ds->surface->pf.bits_per_pixel) {
case 8:
- vs->send_hextile_tile = send_hextile_tile_8;
+ vs->hextile.send_tile = send_hextile_tile_8;
break;
case 16:
- vs->send_hextile_tile = send_hextile_tile_16;
+ vs->hextile.send_tile = send_hextile_tile_16;
break;
case 32:
- vs->send_hextile_tile = send_hextile_tile_32;
+ vs->hextile.send_tile = send_hextile_tile_32;
break;
}
} else {
switch (vs->ds->surface->pf.bits_per_pixel) {
case 8:
- vs->send_hextile_tile = send_hextile_tile_generic_8;
+ vs->hextile.send_tile = send_hextile_tile_generic_8;
break;
case 16:
- vs->send_hextile_tile = send_hextile_tile_generic_16;
+ vs->hextile.send_tile = send_hextile_tile_generic_16;
break;
case 32:
- vs->send_hextile_tile = send_hextile_tile_generic_32;
+ vs->hextile.send_tile = send_hextile_tile_generic_32;
break;
}
}
diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c
new file mode 100644
index 000000000..86bb49a16
--- /dev/null
+++ b/ui/vnc-enc-tight.c
@@ -0,0 +1,1715 @@
+/*
+ * QEMU VNC display driver: tight encoding
+ *
+ * From libvncserver/libvncserver/tight.c
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config-host.h"
+
+#ifdef CONFIG_VNC_PNG
+#include <png.h>
+#endif
+#ifdef CONFIG_VNC_JPEG
+#include <stdio.h>
+#include <jpeglib.h>
+#endif
+
+#include "qemu-common.h"
+
+#include "bswap.h"
+#include "qint.h"
+#include "vnc.h"
+#include "vnc-enc-tight.h"
+#include "vnc-palette.h"
+
+/* Compression level stuff. The following array contains various
+ encoder parameters for each of 10 compression levels (0..9).
+ Last three parameters correspond to JPEG quality levels (0..9). */
+
+static const struct {
+ int max_rect_size, max_rect_width;
+ int mono_min_rect_size, gradient_min_rect_size;
+ int idx_zlib_level, mono_zlib_level, raw_zlib_level, gradient_zlib_level;
+ int gradient_threshold, gradient_threshold24;
+ int idx_max_colors_divisor;
+ int jpeg_quality, jpeg_threshold, jpeg_threshold24;
+} tight_conf[] = {
+ { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 },
+ { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 },
+ { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 },
+ { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 },
+ { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 },
+ { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 },
+ { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 },
+ { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 },
+ { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 },
+ { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 }
+};
+
+
+static int tight_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h);
+
+#ifdef CONFIG_VNC_PNG
+static const struct {
+ int png_zlib_level, png_filters;
+} tight_png_conf[] = {
+ { 0, PNG_NO_FILTERS },
+ { 1, PNG_NO_FILTERS },
+ { 2, PNG_NO_FILTERS },
+ { 3, PNG_NO_FILTERS },
+ { 4, PNG_NO_FILTERS },
+ { 5, PNG_ALL_FILTERS },
+ { 6, PNG_ALL_FILTERS },
+ { 7, PNG_ALL_FILTERS },
+ { 8, PNG_ALL_FILTERS },
+ { 9, PNG_ALL_FILTERS },
+};
+
+static int send_png_rect(VncState *vs, int x, int y, int w, int h,
+ VncPalette *palette);
+
+static bool tight_can_send_png_rect(VncState *vs, int w, int h)
+{
+ if (vs->tight.type != VNC_ENCODING_TIGHT_PNG) {
+ return false;
+ }
+
+ if (ds_get_bytes_per_pixel(vs->ds) == 1 ||
+ vs->clientds.pf.bytes_per_pixel == 1) {
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+/*
+ * Code to guess if given rectangle is suitable for smooth image
+ * compression (by applying "gradient" filter or JPEG coder).
+ */
+
+static uint
+tight_detect_smooth_image24(VncState *vs, int w, int h)
+{
+ int off;
+ int x, y, d, dx;
+ uint c;
+ uint stats[256];
+ int pixels = 0;
+ int pix, left[3];
+ uint errors;
+ unsigned char *buf = vs->tight.tight.buffer;
+
+ /*
+ * If client is big-endian, color samples begin from the second
+ * byte (offset 1) of a 32-bit pixel value.
+ */
+ off = !!(vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG);
+
+ memset(stats, 0, sizeof (stats));
+
+ for (y = 0, x = 0; y < h && x < w;) {
+ for (d = 0; d < h - y && d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH;
+ d++) {
+ for (c = 0; c < 3; c++) {
+ left[c] = buf[((y+d)*w+x+d)*4+off+c] & 0xFF;
+ }
+ for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; dx++) {
+ for (c = 0; c < 3; c++) {
+ pix = buf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF;
+ stats[abs(pix - left[c])]++;
+ left[c] = pix;
+ }
+ pixels++;
+ }
+ }
+ if (w > h) {
+ x += h;
+ y = 0;
+ } else {
+ x = 0;
+ y += w;
+ }
+ }
+
+ /* 95% smooth or more ... */
+ if (stats[0] * 33 / pixels >= 95) {
+ return 0;
+ }
+
+ errors = 0;
+ for (c = 1; c < 8; c++) {
+ errors += stats[c] * (c * c);
+ if (stats[c] == 0 || stats[c] > stats[c-1] * 2) {
+ return 0;
+ }
+ }
+ for (; c < 256; c++) {
+ errors += stats[c] * (c * c);
+ }
+ errors /= (pixels * 3 - stats[0]);
+
+ return errors;
+}
+
+#define DEFINE_DETECT_FUNCTION(bpp) \
+ \
+ static uint \
+ tight_detect_smooth_image##bpp(VncState *vs, int w, int h) { \
+ bool endian; \
+ uint##bpp##_t pix; \
+ int max[3], shift[3]; \
+ int x, y, d, dx; \
+ uint c; \
+ uint stats[256]; \
+ int pixels = 0; \
+ int sample, sum, left[3]; \
+ uint errors; \
+ unsigned char *buf = vs->tight.tight.buffer; \
+ \
+ endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \
+ \
+ \
+ max[0] = vs->clientds.pf.rmax; \
+ max[1] = vs->clientds.pf.gmax; \
+ max[2] = vs->clientds.pf.bmax; \
+ shift[0] = vs->clientds.pf.rshift; \
+ shift[1] = vs->clientds.pf.gshift; \
+ shift[2] = vs->clientds.pf.bshift; \
+ \
+ memset(stats, 0, sizeof(stats)); \
+ \
+ y = 0, x = 0; \
+ while (y < h && x < w) { \
+ for (d = 0; d < h - y && \
+ d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH; d++) { \
+ pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d]; \
+ if (endian) { \
+ pix = bswap_##bpp(pix); \
+ } \
+ for (c = 0; c < 3; c++) { \
+ left[c] = (int)(pix >> shift[c] & max[c]); \
+ } \
+ for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; \
+ dx++) { \
+ pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d+dx]; \
+ if (endian) { \
+ pix = bswap_##bpp(pix); \
+ } \
+ sum = 0; \
+ for (c = 0; c < 3; c++) { \
+ sample = (int)(pix >> shift[c] & max[c]); \
+ sum += abs(sample - left[c]); \
+ left[c] = sample; \
+ } \
+ if (sum > 255) { \
+ sum = 255; \
+ } \
+ stats[sum]++; \
+ pixels++; \
+ } \
+ } \
+ if (w > h) { \
+ x += h; \
+ y = 0; \
+ } else { \
+ x = 0; \
+ y += w; \
+ } \
+ } \
+ \
+ if ((stats[0] + stats[1]) * 100 / pixels >= 90) { \
+ return 0; \
+ } \
+ \
+ errors = 0; \
+ for (c = 1; c < 8; c++) { \
+ errors += stats[c] * (c * c); \
+ if (stats[c] == 0 || stats[c] > stats[c-1] * 2) { \
+ return 0; \
+ } \
+ } \
+ for (; c < 256; c++) { \
+ errors += stats[c] * (c * c); \
+ } \
+ errors /= (pixels - stats[0]); \
+ \
+ return errors; \
+ }
+
+DEFINE_DETECT_FUNCTION(16)
+DEFINE_DETECT_FUNCTION(32)
+
+static int
+tight_detect_smooth_image(VncState *vs, int w, int h)
+{
+ uint errors;
+ int compression = vs->tight.compression;
+ int quality = vs->tight.quality;
+
+ if (!vs->vd->lossy) {
+ return 0;
+ }
+
+ if (ds_get_bytes_per_pixel(vs->ds) == 1 ||
+ vs->clientds.pf.bytes_per_pixel == 1 ||
+ w < VNC_TIGHT_DETECT_MIN_WIDTH || h < VNC_TIGHT_DETECT_MIN_HEIGHT) {
+ return 0;
+ }
+
+ if (vs->tight.quality != -1) {
+ if (w * h < VNC_TIGHT_JPEG_MIN_RECT_SIZE) {
+ return 0;
+ }
+ } else {
+ if (w * h < tight_conf[compression].gradient_min_rect_size) {
+ return 0;
+ }
+ }
+
+ if (vs->clientds.pf.bytes_per_pixel == 4) {
+ if (vs->tight.pixel24) {
+ errors = tight_detect_smooth_image24(vs, w, h);
+ if (vs->tight.quality != -1) {
+ return (errors < tight_conf[quality].jpeg_threshold24);
+ }
+ return (errors < tight_conf[compression].gradient_threshold24);
+ } else {
+ errors = tight_detect_smooth_image32(vs, w, h);
+ }
+ } else {
+ errors = tight_detect_smooth_image16(vs, w, h);
+ }
+ if (quality != -1) {
+ return (errors < tight_conf[quality].jpeg_threshold);
+ }
+ return (errors < tight_conf[compression].gradient_threshold);
+}
+
+/*
+ * Code to determine how many different colors used in rectangle.
+ */
+#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \
+ \
+ static int \
+ tight_fill_palette##bpp(VncState *vs, int x, int y, \
+ int max, size_t count, \
+ uint32_t *bg, uint32_t *fg, \
+ VncPalette **palette) { \
+ uint##bpp##_t *data; \
+ uint##bpp##_t c0, c1, ci; \
+ int i, n0, n1; \
+ \
+ data = (uint##bpp##_t *)vs->tight.tight.buffer; \
+ \
+ c0 = data[0]; \
+ i = 1; \
+ while (i < count && data[i] == c0) \
+ i++; \
+ if (i >= count) { \
+ *bg = *fg = c0; \
+ return 1; \
+ } \
+ \
+ if (max < 2) { \
+ return 0; \
+ } \
+ \
+ n0 = i; \
+ c1 = data[i]; \
+ n1 = 0; \
+ for (i++; i < count; i++) { \
+ ci = data[i]; \
+ if (ci == c0) { \
+ n0++; \
+ } else if (ci == c1) { \
+ n1++; \
+ } else \
+ break; \
+ } \
+ if (i >= count) { \
+ if (n0 > n1) { \
+ *bg = (uint32_t)c0; \
+ *fg = (uint32_t)c1; \
+ } else { \
+ *bg = (uint32_t)c1; \
+ *fg = (uint32_t)c0; \
+ } \
+ return 2; \
+ } \
+ \
+ if (max == 2) { \
+ return 0; \
+ } \
+ \
+ *palette = palette_new(max, bpp); \
+ palette_put(*palette, c0); \
+ palette_put(*palette, c1); \
+ palette_put(*palette, ci); \
+ \
+ for (i++; i < count; i++) { \
+ if (data[i] == ci) { \
+ continue; \
+ } else { \
+ ci = data[i]; \
+ if (!palette_put(*palette, (uint32_t)ci)) { \
+ return 0; \
+ } \
+ } \
+ } \
+ \
+ return palette_size(*palette); \
+ }
+
+DEFINE_FILL_PALETTE_FUNCTION(8)
+DEFINE_FILL_PALETTE_FUNCTION(16)
+DEFINE_FILL_PALETTE_FUNCTION(32)
+
+static int tight_fill_palette(VncState *vs, int x, int y,
+ size_t count, uint32_t *bg, uint32_t *fg,
+ VncPalette **palette)
+{
+ int max;
+
+ max = count / tight_conf[vs->tight.compression].idx_max_colors_divisor;
+ if (max < 2 &&
+ count >= tight_conf[vs->tight.compression].mono_min_rect_size) {
+ max = 2;
+ }
+ if (max >= 256) {
+ max = 256;
+ }
+
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 4:
+ return tight_fill_palette32(vs, x, y, max, count, bg, fg, palette);
+ case 2:
+ return tight_fill_palette16(vs, x, y, max, count, bg, fg, palette);
+ default:
+ max = 2;
+ return tight_fill_palette8(vs, x, y, max, count, bg, fg, palette);
+ }
+ return 0;
+}
+
+/*
+ * Converting truecolor samples into palette indices.
+ */
+#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \
+ \
+ static void \
+ tight_encode_indexed_rect##bpp(uint8_t *buf, int count, \
+ VncPalette *palette) { \
+ uint##bpp##_t *src; \
+ uint##bpp##_t rgb; \
+ int i, rep; \
+ uint8_t idx; \
+ \
+ src = (uint##bpp##_t *) buf; \
+ \
+ for (i = 0; i < count; i++) { \
+ \
+ rgb = *src++; \
+ rep = 0; \
+ while (i < count && *src == rgb) { \
+ rep++, src++, i++; \
+ } \
+ idx = palette_idx(palette, rgb); \
+ /* \
+ * Should never happen, but don't break everything \
+ * if it does, use the first color instead \
+ */ \
+ if (idx == -1) { \
+ idx = 0; \
+ } \
+ while (rep >= 0) { \
+ *buf++ = idx; \
+ rep--; \
+ } \
+ } \
+ }
+
+DEFINE_IDX_ENCODE_FUNCTION(16)
+DEFINE_IDX_ENCODE_FUNCTION(32)
+
+#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \
+ \
+ static void \
+ tight_encode_mono_rect##bpp(uint8_t *buf, int w, int h, \
+ uint##bpp##_t bg, uint##bpp##_t fg) { \
+ uint##bpp##_t *ptr; \
+ unsigned int value, mask; \
+ int aligned_width; \
+ int x, y, bg_bits; \
+ \
+ ptr = (uint##bpp##_t *) buf; \
+ aligned_width = w - w % 8; \
+ \
+ for (y = 0; y < h; y++) { \
+ for (x = 0; x < aligned_width; x += 8) { \
+ for (bg_bits = 0; bg_bits < 8; bg_bits++) { \
+ if (*ptr++ != bg) { \
+ break; \
+ } \
+ } \
+ if (bg_bits == 8) { \
+ *buf++ = 0; \
+ continue; \
+ } \
+ mask = 0x80 >> bg_bits; \
+ value = mask; \
+ for (bg_bits++; bg_bits < 8; bg_bits++) { \
+ mask >>= 1; \
+ if (*ptr++ != bg) { \
+ value |= mask; \
+ } \
+ } \
+ *buf++ = (uint8_t)value; \
+ } \
+ \
+ mask = 0x80; \
+ value = 0; \
+ if (x >= w) { \
+ continue; \
+ } \
+ \
+ for (; x < w; x++) { \
+ if (*ptr++ != bg) { \
+ value |= mask; \
+ } \
+ mask >>= 1; \
+ } \
+ *buf++ = (uint8_t)value; \
+ } \
+ }
+
+DEFINE_MONO_ENCODE_FUNCTION(8)
+DEFINE_MONO_ENCODE_FUNCTION(16)
+DEFINE_MONO_ENCODE_FUNCTION(32)
+
+/*
+ * ``Gradient'' filter for 24-bit color samples.
+ * Should be called only when redMax, greenMax and blueMax are 255.
+ * Color components assumed to be byte-aligned.
+ */
+
+static void
+tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h)
+{
+ uint32_t *buf32;
+ uint32_t pix32;
+ int shift[3];
+ int *prev;
+ int here[3], upper[3], left[3], upperleft[3];
+ int prediction;
+ int x, y, c;
+
+ buf32 = (uint32_t *)buf;
+ memset(vs->tight.gradient.buffer, 0, w * 3 * sizeof(int));
+
+ if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
+ shift[0] = vs->clientds.pf.rshift;
+ shift[1] = vs->clientds.pf.gshift;
+ shift[2] = vs->clientds.pf.bshift;
+ } else {
+ shift[0] = 24 - vs->clientds.pf.rshift;
+ shift[1] = 24 - vs->clientds.pf.gshift;
+ shift[2] = 24 - vs->clientds.pf.bshift;
+ }
+
+ for (y = 0; y < h; y++) {
+ for (c = 0; c < 3; c++) {
+ upper[c] = 0;
+ here[c] = 0;
+ }
+ prev = (int *)vs->tight.gradient.buffer;
+ for (x = 0; x < w; x++) {
+ pix32 = *buf32++;
+ for (c = 0; c < 3; c++) {
+ upperleft[c] = upper[c];
+ left[c] = here[c];
+ upper[c] = *prev;
+ here[c] = (int)(pix32 >> shift[c] & 0xFF);
+ *prev++ = here[c];
+
+ prediction = left[c] + upper[c] - upperleft[c];
+ if (prediction < 0) {
+ prediction = 0;
+ } else if (prediction > 0xFF) {
+ prediction = 0xFF;
+ }
+ *buf++ = (char)(here[c] - prediction);
+ }
+ }
+ }
+}
+
+
+/*
+ * ``Gradient'' filter for other color depths.
+ */
+
+#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \
+ \
+ static void \
+ tight_filter_gradient##bpp(VncState *vs, uint##bpp##_t *buf, \
+ int w, int h) { \
+ uint##bpp##_t pix, diff; \
+ bool endian; \
+ int *prev; \
+ int max[3], shift[3]; \
+ int here[3], upper[3], left[3], upperleft[3]; \
+ int prediction; \
+ int x, y, c; \
+ \
+ memset (vs->tight.gradient.buffer, 0, w * 3 * sizeof(int)); \
+ \
+ endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \
+ \
+ max[0] = vs->clientds.pf.rmax; \
+ max[1] = vs->clientds.pf.gmax; \
+ max[2] = vs->clientds.pf.bmax; \
+ shift[0] = vs->clientds.pf.rshift; \
+ shift[1] = vs->clientds.pf.gshift; \
+ shift[2] = vs->clientds.pf.bshift; \
+ \
+ for (y = 0; y < h; y++) { \
+ for (c = 0; c < 3; c++) { \
+ upper[c] = 0; \
+ here[c] = 0; \
+ } \
+ prev = (int *)vs->tight.gradient.buffer; \
+ for (x = 0; x < w; x++) { \
+ pix = *buf; \
+ if (endian) { \
+ pix = bswap_##bpp(pix); \
+ } \
+ diff = 0; \
+ for (c = 0; c < 3; c++) { \
+ upperleft[c] = upper[c]; \
+ left[c] = here[c]; \
+ upper[c] = *prev; \
+ here[c] = (int)(pix >> shift[c] & max[c]); \
+ *prev++ = here[c]; \
+ \
+ prediction = left[c] + upper[c] - upperleft[c]; \
+ if (prediction < 0) { \
+ prediction = 0; \
+ } else if (prediction > max[c]) { \
+ prediction = max[c]; \
+ } \
+ diff |= ((here[c] - prediction) & max[c]) \
+ << shift[c]; \
+ } \
+ if (endian) { \
+ diff = bswap_##bpp(diff); \
+ } \
+ *buf++ = diff; \
+ } \
+ } \
+ }
+
+DEFINE_GRADIENT_FILTER_FUNCTION(16)
+DEFINE_GRADIENT_FILTER_FUNCTION(32)
+
+/*
+ * Check if a rectangle is all of the same color. If needSameColor is
+ * set to non-zero, then also check that its color equals to the
+ * *colorPtr value. The result is 1 if the test is successfull, and in
+ * that case new color will be stored in *colorPtr.
+ */
+
+#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
+ \
+ static bool \
+ check_solid_tile##bpp(VncState *vs, int x, int y, int w, int h, \
+ uint32_t* color, bool samecolor) \
+ { \
+ VncDisplay *vd = vs->vd; \
+ uint##bpp##_t *fbptr; \
+ uint##bpp##_t c; \
+ int dx, dy; \
+ \
+ fbptr = (uint##bpp##_t *) \
+ (vd->server->data + y * ds_get_linesize(vs->ds) + \
+ x * ds_get_bytes_per_pixel(vs->ds)); \
+ \
+ c = *fbptr; \
+ if (samecolor && (uint32_t)c != *color) { \
+ return false; \
+ } \
+ \
+ for (dy = 0; dy < h; dy++) { \
+ for (dx = 0; dx < w; dx++) { \
+ if (c != fbptr[dx]) { \
+ return false; \
+ } \
+ } \
+ fbptr = (uint##bpp##_t *) \
+ ((uint8_t *)fbptr + ds_get_linesize(vs->ds)); \
+ } \
+ \
+ *color = (uint32_t)c; \
+ return true; \
+ }
+
+DEFINE_CHECK_SOLID_FUNCTION(32)
+DEFINE_CHECK_SOLID_FUNCTION(16)
+DEFINE_CHECK_SOLID_FUNCTION(8)
+
+static bool check_solid_tile(VncState *vs, int x, int y, int w, int h,
+ uint32_t* color, bool samecolor)
+{
+ VncDisplay *vd = vs->vd;
+
+ switch(vd->server->pf.bytes_per_pixel) {
+ case 4:
+ return check_solid_tile32(vs, x, y, w, h, color, samecolor);
+ case 2:
+ return check_solid_tile16(vs, x, y, w, h, color, samecolor);
+ default:
+ return check_solid_tile8(vs, x, y, w, h, color, samecolor);
+ }
+}
+
+static void find_best_solid_area(VncState *vs, int x, int y, int w, int h,
+ uint32_t color, int *w_ptr, int *h_ptr)
+{
+ int dx, dy, dw, dh;
+ int w_prev;
+ int w_best = 0, h_best = 0;
+
+ w_prev = w;
+
+ for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+
+ dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, y + h - dy);
+ dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, w_prev);
+
+ if (!check_solid_tile(vs, x, dy, dw, dh, &color, true)) {
+ break;
+ }
+
+ for (dx = x + dw; dx < x + w_prev;) {
+ dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, x + w_prev - dx);
+
+ if (!check_solid_tile(vs, dx, dy, dw, dh, &color, true)) {
+ break;
+ }
+ dx += dw;
+ }
+
+ w_prev = dx - x;
+ if (w_prev * (dy + dh - y) > w_best * h_best) {
+ w_best = w_prev;
+ h_best = dy + dh - y;
+ }
+ }
+
+ *w_ptr = w_best;
+ *h_ptr = h_best;
+}
+
+static void extend_solid_area(VncState *vs, int x, int y, int w, int h,
+ uint32_t color, int *x_ptr, int *y_ptr,
+ int *w_ptr, int *h_ptr)
+{
+ int cx, cy;
+
+ /* Try to extend the area upwards. */
+ for ( cy = *y_ptr - 1;
+ cy >= y && check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
+ cy-- );
+ *h_ptr += *y_ptr - (cy + 1);
+ *y_ptr = cy + 1;
+
+ /* ... downwards. */
+ for ( cy = *y_ptr + *h_ptr;
+ cy < y + h &&
+ check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
+ cy++ );
+ *h_ptr += cy - (*y_ptr + *h_ptr);
+
+ /* ... to the left. */
+ for ( cx = *x_ptr - 1;
+ cx >= x && check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
+ cx-- );
+ *w_ptr += *x_ptr - (cx + 1);
+ *x_ptr = cx + 1;
+
+ /* ... to the right. */
+ for ( cx = *x_ptr + *w_ptr;
+ cx < x + w &&
+ check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
+ cx++ );
+ *w_ptr += cx - (*x_ptr + *w_ptr);
+}
+
+static int tight_init_stream(VncState *vs, int stream_id,
+ int level, int strategy)
+{
+ z_streamp zstream = &vs->tight.stream[stream_id];
+
+ if (zstream->opaque == NULL) {
+ int err;
+
+ VNC_DEBUG("VNC: TIGHT: initializing zlib stream %d\n", stream_id);
+ VNC_DEBUG("VNC: TIGHT: opaque = %p | vs = %p\n", zstream->opaque, vs);
+ zstream->zalloc = vnc_zlib_zalloc;
+ zstream->zfree = vnc_zlib_zfree;
+
+ err = deflateInit2(zstream, level, Z_DEFLATED, MAX_WBITS,
+ MAX_MEM_LEVEL, strategy);
+
+ if (err != Z_OK) {
+ fprintf(stderr, "VNC: error initializing zlib\n");
+ return -1;
+ }
+
+ vs->tight.levels[stream_id] = level;
+ zstream->opaque = vs;
+ }
+
+ if (vs->tight.levels[stream_id] != level) {
+ if (deflateParams(zstream, level, strategy) != Z_OK) {
+ return -1;
+ }
+ vs->tight.levels[stream_id] = level;
+ }
+ return 0;
+}
+
+static void tight_send_compact_size(VncState *vs, size_t len)
+{
+ int lpc = 0;
+ int bytes = 0;
+ char buf[3] = {0, 0, 0};
+
+ buf[bytes++] = len & 0x7F;
+ if (len > 0x7F) {
+ buf[bytes-1] |= 0x80;
+ buf[bytes++] = (len >> 7) & 0x7F;
+ if (len > 0x3FFF) {
+ buf[bytes-1] |= 0x80;
+ buf[bytes++] = (len >> 14) & 0xFF;
+ }
+ }
+ for (lpc = 0; lpc < bytes; lpc++) {
+ vnc_write_u8(vs, buf[lpc]);
+ }
+}
+
+static int tight_compress_data(VncState *vs, int stream_id, size_t bytes,
+ int level, int strategy)
+{
+ z_streamp zstream = &vs->tight.stream[stream_id];
+ int previous_out;
+
+ if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) {
+ vnc_write(vs, vs->tight.tight.buffer, vs->tight.tight.offset);
+ return bytes;
+ }
+
+ if (tight_init_stream(vs, stream_id, level, strategy)) {
+ return -1;
+ }
+
+ /* reserve memory in output buffer */
+ buffer_reserve(&vs->tight.zlib, bytes + 64);
+
+ /* set pointers */
+ zstream->next_in = vs->tight.tight.buffer;
+ zstream->avail_in = vs->tight.tight.offset;
+ zstream->next_out = vs->tight.zlib.buffer + vs->tight.zlib.offset;
+ zstream->avail_out = vs->tight.zlib.capacity - vs->tight.zlib.offset;
+ zstream->data_type = Z_BINARY;
+ previous_out = zstream->total_out;
+
+ /* start encoding */
+ if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) {
+ fprintf(stderr, "VNC: error during tight compression\n");
+ return -1;
+ }
+
+ vs->tight.zlib.offset = vs->tight.zlib.capacity - zstream->avail_out;
+ bytes = zstream->total_out - previous_out;
+
+ tight_send_compact_size(vs, bytes);
+ vnc_write(vs, vs->tight.zlib.buffer, bytes);
+
+ buffer_reset(&vs->tight.zlib);
+
+ return bytes;
+}
+
+/*
+ * Subencoding implementations.
+ */
+static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret)
+{
+ uint32_t *buf32;
+ uint32_t pix;
+ int rshift, gshift, bshift;
+
+ buf32 = (uint32_t *)buf;
+
+ if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
+ rshift = vs->clientds.pf.rshift;
+ gshift = vs->clientds.pf.gshift;
+ bshift = vs->clientds.pf.bshift;
+ } else {
+ rshift = 24 - vs->clientds.pf.rshift;
+ gshift = 24 - vs->clientds.pf.gshift;
+ bshift = 24 - vs->clientds.pf.bshift;
+ }
+
+ if (ret) {
+ *ret = count * 3;
+ }
+
+ while (count--) {
+ pix = *buf32++;
+ *buf++ = (char)(pix >> rshift);
+ *buf++ = (char)(pix >> gshift);
+ *buf++ = (char)(pix >> bshift);
+ }
+}
+
+static int send_full_color_rect(VncState *vs, int x, int y, int w, int h)
+{
+ int stream = 0;
+ size_t bytes;
+
+#ifdef CONFIG_VNC_PNG
+ if (tight_can_send_png_rect(vs, w, h)) {
+ return send_png_rect(vs, x, y, w, h, NULL);
+ }
+#endif
+
+ vnc_write_u8(vs, stream << 4); /* no flushing, no filter */
+
+ if (vs->tight.pixel24) {
+ tight_pack24(vs, vs->tight.tight.buffer, w * h, &vs->tight.tight.offset);
+ bytes = 3;
+ } else {
+ bytes = vs->clientds.pf.bytes_per_pixel;
+ }
+
+ bytes = tight_compress_data(vs, stream, w * h * bytes,
+ tight_conf[vs->tight.compression].raw_zlib_level,
+ Z_DEFAULT_STRATEGY);
+
+ return (bytes >= 0);
+}
+
+static int send_solid_rect(VncState *vs)
+{
+ size_t bytes;
+
+ vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */
+
+ if (vs->tight.pixel24) {
+ tight_pack24(vs, vs->tight.tight.buffer, 1, &vs->tight.tight.offset);
+ bytes = 3;
+ } else {
+ bytes = vs->clientds.pf.bytes_per_pixel;
+ }
+
+ vnc_write(vs, vs->tight.tight.buffer, bytes);
+ return 1;
+}
+
+static int send_mono_rect(VncState *vs, int x, int y,
+ int w, int h, uint32_t bg, uint32_t fg)
+{
+ size_t bytes;
+ int stream = 1;
+ int level = tight_conf[vs->tight.compression].mono_zlib_level;
+
+#ifdef CONFIG_VNC_PNG
+ if (tight_can_send_png_rect(vs, w, h)) {
+ int ret;
+ int bpp = vs->clientds.pf.bytes_per_pixel * 8;
+ VncPalette *palette = palette_new(2, bpp);
+
+ palette_put(palette, bg);
+ palette_put(palette, fg);
+ ret = send_png_rect(vs, x, y, w, h, palette);
+ palette_destroy(palette);
+ return ret;
+ }
+#endif
+
+ bytes = ((w + 7) / 8) * h;
+
+ vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+ vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
+ vnc_write_u8(vs, 1);
+
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 4:
+ {
+ uint32_t buf[2] = {bg, fg};
+ size_t ret = sizeof (buf);
+
+ if (vs->tight.pixel24) {
+ tight_pack24(vs, (unsigned char*)buf, 2, &ret);
+ }
+ vnc_write(vs, buf, ret);
+
+ tight_encode_mono_rect32(vs->tight.tight.buffer, w, h, bg, fg);
+ break;
+ }
+ case 2:
+ vnc_write(vs, &bg, 2);
+ vnc_write(vs, &fg, 2);
+ tight_encode_mono_rect16(vs->tight.tight.buffer, w, h, bg, fg);
+ break;
+ default:
+ vnc_write_u8(vs, bg);
+ vnc_write_u8(vs, fg);
+ tight_encode_mono_rect8(vs->tight.tight.buffer, w, h, bg, fg);
+ break;
+ }
+ vs->tight.tight.offset = bytes;
+
+ bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY);
+ return (bytes >= 0);
+}
+
+struct palette_cb_priv {
+ VncState *vs;
+ uint8_t *header;
+#ifdef CONFIG_VNC_PNG
+ png_colorp png_palette;
+#endif
+};
+
+static void write_palette(int idx, uint32_t color, void *opaque)
+{
+ struct palette_cb_priv *priv = opaque;
+ VncState *vs = priv->vs;
+ uint32_t bytes = vs->clientds.pf.bytes_per_pixel;
+
+ if (bytes == 4) {
+ ((uint32_t*)priv->header)[idx] = color;
+ } else {
+ ((uint16_t*)priv->header)[idx] = color;
+ }
+}
+
+static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h)
+{
+ int stream = 3;
+ int level = tight_conf[vs->tight.compression].gradient_zlib_level;
+ size_t bytes;
+
+ if (vs->clientds.pf.bytes_per_pixel == 1)
+ return send_full_color_rect(vs, x, y, w, h);
+
+ vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+ vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT);
+
+ buffer_reserve(&vs->tight.gradient, w * 3 * sizeof (int));
+
+ if (vs->tight.pixel24) {
+ tight_filter_gradient24(vs, vs->tight.tight.buffer, w, h);
+ bytes = 3;
+ } else if (vs->clientds.pf.bytes_per_pixel == 4) {
+ tight_filter_gradient32(vs, (uint32_t *)vs->tight.tight.buffer, w, h);
+ bytes = 4;
+ } else {
+ tight_filter_gradient16(vs, (uint16_t *)vs->tight.tight.buffer, w, h);
+ bytes = 2;
+ }
+
+ buffer_reset(&vs->tight.gradient);
+
+ bytes = w * h * bytes;
+ vs->tight.tight.offset = bytes;
+
+ bytes = tight_compress_data(vs, stream, bytes,
+ level, Z_FILTERED);
+ return (bytes >= 0);
+}
+
+static int send_palette_rect(VncState *vs, int x, int y,
+ int w, int h, VncPalette *palette)
+{
+ int stream = 2;
+ int level = tight_conf[vs->tight.compression].idx_zlib_level;
+ int colors;
+ size_t bytes;
+
+#ifdef CONFIG_VNC_PNG
+ if (tight_can_send_png_rect(vs, w, h)) {
+ return send_png_rect(vs, x, y, w, h, palette);
+ }
+#endif
+
+ colors = palette_size(palette);
+
+ vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+ vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
+ vnc_write_u8(vs, colors - 1);
+
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 4:
+ {
+ size_t old_offset, offset;
+ uint32_t header[palette_size(palette)];
+ struct palette_cb_priv priv = { vs, (uint8_t *)header };
+
+ old_offset = vs->output.offset;
+ palette_iter(palette, write_palette, &priv);
+ vnc_write(vs, header, sizeof(header));
+
+ if (vs->tight.pixel24) {
+ tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset);
+ vs->output.offset = old_offset + offset;
+ }
+
+ tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette);
+ break;
+ }
+ case 2:
+ {
+ uint16_t header[palette_size(palette)];
+ struct palette_cb_priv priv = { vs, (uint8_t *)header };
+
+ palette_iter(palette, write_palette, &priv);
+ vnc_write(vs, header, sizeof(header));
+ tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette);
+ break;
+ }
+ default:
+ return -1; /* No palette for 8bits colors */
+ break;
+ }
+ bytes = w * h;
+ vs->tight.tight.offset = bytes;
+
+ bytes = tight_compress_data(vs, stream, bytes,
+ level, Z_DEFAULT_STRATEGY);
+ return (bytes >= 0);
+}
+
+#if defined(CONFIG_VNC_JPEG) || defined(CONFIG_VNC_PNG)
+static void rgb_prepare_row24(VncState *vs, uint8_t *dst, int x, int y,
+ int count)
+{
+ VncDisplay *vd = vs->vd;
+ uint32_t *fbptr;
+ uint32_t pix;
+
+ fbptr = (uint32_t *)(vd->server->data + y * ds_get_linesize(vs->ds) +
+ x * ds_get_bytes_per_pixel(vs->ds));
+
+ while (count--) {
+ pix = *fbptr++;
+ *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.rshift);
+ *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.gshift);
+ *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.bshift);
+ }
+}
+
+#define DEFINE_RGB_GET_ROW_FUNCTION(bpp) \
+ \
+ static void \
+ rgb_prepare_row##bpp(VncState *vs, uint8_t *dst, \
+ int x, int y, int count) \
+ { \
+ VncDisplay *vd = vs->vd; \
+ uint##bpp##_t *fbptr; \
+ uint##bpp##_t pix; \
+ int r, g, b; \
+ \
+ fbptr = (uint##bpp##_t *) \
+ (vd->server->data + y * ds_get_linesize(vs->ds) + \
+ x * ds_get_bytes_per_pixel(vs->ds)); \
+ \
+ while (count--) { \
+ pix = *fbptr++; \
+ \
+ r = (int)((pix >> vs->ds->surface->pf.rshift) \
+ & vs->ds->surface->pf.rmax); \
+ g = (int)((pix >> vs->ds->surface->pf.gshift) \
+ & vs->ds->surface->pf.gmax); \
+ b = (int)((pix >> vs->ds->surface->pf.bshift) \
+ & vs->ds->surface->pf.bmax); \
+ \
+ *dst++ = (uint8_t)((r * 255 + vs->ds->surface->pf.rmax / 2) \
+ / vs->ds->surface->pf.rmax); \
+ *dst++ = (uint8_t)((g * 255 + vs->ds->surface->pf.gmax / 2) \
+ / vs->ds->surface->pf.gmax); \
+ *dst++ = (uint8_t)((b * 255 + vs->ds->surface->pf.bmax / 2) \
+ / vs->ds->surface->pf.bmax); \
+ } \
+ }
+
+DEFINE_RGB_GET_ROW_FUNCTION(16)
+DEFINE_RGB_GET_ROW_FUNCTION(32)
+
+static void rgb_prepare_row(VncState *vs, uint8_t *dst, int x, int y,
+ int count)
+{
+ if (ds_get_bytes_per_pixel(vs->ds) == 4) {
+ if (vs->ds->surface->pf.rmax == 0xFF &&
+ vs->ds->surface->pf.gmax == 0xFF &&
+ vs->ds->surface->pf.bmax == 0xFF) {
+ rgb_prepare_row24(vs, dst, x, y, count);
+ } else {
+ rgb_prepare_row32(vs, dst, x, y, count);
+ }
+ } else {
+ rgb_prepare_row16(vs, dst, x, y, count);
+ }
+}
+#endif /* CONFIG_VNC_JPEG or CONFIG_VNC_PNG */
+
+/*
+ * JPEG compression stuff.
+ */
+#ifdef CONFIG_VNC_JPEG
+/*
+ * Destination manager implementation for JPEG library.
+ */
+
+/* This is called once per encoding */
+static void jpeg_init_destination(j_compress_ptr cinfo)
+{
+ VncState *vs = cinfo->client_data;
+ Buffer *buffer = &vs->tight.jpeg;
+
+ cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset;
+ cinfo->dest->free_in_buffer = (size_t)(buffer->capacity - buffer->offset);
+}
+
+/* This is called when we ran out of buffer (shouldn't happen!) */
+static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)
+{
+ VncState *vs = cinfo->client_data;
+ Buffer *buffer = &vs->tight.jpeg;
+
+ buffer->offset = buffer->capacity;
+ buffer_reserve(buffer, 2048);
+ jpeg_init_destination(cinfo);
+ return TRUE;
+}
+
+/* This is called when we are done processing data */
+static void jpeg_term_destination(j_compress_ptr cinfo)
+{
+ VncState *vs = cinfo->client_data;
+ Buffer *buffer = &vs->tight.jpeg;
+
+ buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer;
+}
+
+static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ struct jpeg_destination_mgr manager;
+ JSAMPROW row[1];
+ uint8_t *buf;
+ int dy;
+
+ if (ds_get_bytes_per_pixel(vs->ds) == 1)
+ return send_full_color_rect(vs, x, y, w, h);
+
+ buffer_reserve(&vs->tight.jpeg, 2048);
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+
+ cinfo.client_data = vs;
+ cinfo.image_width = w;
+ cinfo.image_height = h;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, quality, true);
+
+ manager.init_destination = jpeg_init_destination;
+ manager.empty_output_buffer = jpeg_empty_output_buffer;
+ manager.term_destination = jpeg_term_destination;
+ cinfo.dest = &manager;
+
+ jpeg_start_compress(&cinfo, true);
+
+ buf = qemu_malloc(w * 3);
+ row[0] = buf;
+ for (dy = 0; dy < h; dy++) {
+ rgb_prepare_row(vs, buf, x, y + dy, w);
+ jpeg_write_scanlines(&cinfo, row, 1);
+ }
+ qemu_free(buf);
+
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+
+ vnc_write_u8(vs, VNC_TIGHT_JPEG << 4);
+
+ tight_send_compact_size(vs, vs->tight.jpeg.offset);
+ vnc_write(vs, vs->tight.jpeg.buffer, vs->tight.jpeg.offset);
+ buffer_reset(&vs->tight.jpeg);
+
+ return 1;
+}
+#endif /* CONFIG_VNC_JPEG */
+
+/*
+ * PNG compression stuff.
+ */
+#ifdef CONFIG_VNC_PNG
+static void write_png_palette(int idx, uint32_t pix, void *opaque)
+{
+ struct palette_cb_priv *priv = opaque;
+ VncState *vs = priv->vs;
+ png_colorp color = &priv->png_palette[idx];
+
+ if (vs->tight.pixel24)
+ {
+ color->red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax;
+ color->green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax;
+ color->blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax;
+ }
+ else
+ {
+ int red, green, blue;
+
+ red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax;
+ green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax;
+ blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax;
+ color->red = ((red * 255 + vs->clientds.pf.rmax / 2) /
+ vs->clientds.pf.rmax);
+ color->green = ((green * 255 + vs->clientds.pf.gmax / 2) /
+ vs->clientds.pf.gmax);
+ color->blue = ((blue * 255 + vs->clientds.pf.bmax / 2) /
+ vs->clientds.pf.bmax);
+ }
+}
+
+static void png_write_data(png_structp png_ptr, png_bytep data,
+ png_size_t length)
+{
+ VncState *vs = png_get_io_ptr(png_ptr);
+
+ buffer_reserve(&vs->tight.png, vs->tight.png.offset + length);
+ memcpy(vs->tight.png.buffer + vs->tight.png.offset, data, length);
+
+ vs->tight.png.offset += length;
+}
+
+static void png_flush_data(png_structp png_ptr)
+{
+}
+
+static void *vnc_png_malloc(png_structp png_ptr, png_size_t size)
+{
+ return qemu_malloc(size);
+}
+
+static void vnc_png_free(png_structp png_ptr, png_voidp ptr)
+{
+ qemu_free(ptr);
+}
+
+static int send_png_rect(VncState *vs, int x, int y, int w, int h,
+ VncPalette *palette)
+{
+ png_byte color_type;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_colorp png_palette = NULL;
+ size_t offset;
+ int level = tight_png_conf[vs->tight.compression].png_zlib_level;
+ int filters = tight_png_conf[vs->tight.compression].png_filters;
+ uint8_t *buf;
+ int dy;
+
+ png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL,
+ NULL, vnc_png_malloc, vnc_png_free);
+
+ if (png_ptr == NULL)
+ return -1;
+
+ info_ptr = png_create_info_struct(png_ptr);
+
+ if (info_ptr == NULL) {
+ png_destroy_write_struct(&png_ptr, NULL);
+ return -1;
+ }
+
+ png_set_write_fn(png_ptr, (void *) vs, png_write_data, png_flush_data);
+ png_set_compression_level(png_ptr, level);
+ png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, filters);
+
+ if (palette) {
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ } else {
+ color_type = PNG_COLOR_TYPE_RGB;
+ }
+
+ png_set_IHDR(png_ptr, info_ptr, w, h,
+ 8, color_type, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ struct palette_cb_priv priv;
+
+ png_palette = png_malloc(png_ptr, sizeof(*png_palette) *
+ palette_size(palette));
+
+ priv.vs = vs;
+ priv.png_palette = png_palette;
+ palette_iter(palette, write_png_palette, &priv);
+
+ png_set_PLTE(png_ptr, info_ptr, png_palette, palette_size(palette));
+
+ offset = vs->tight.tight.offset;
+ if (vs->clientds.pf.bytes_per_pixel == 4) {
+ tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette);
+ } else {
+ tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette);
+ }
+ }
+
+ png_write_info(png_ptr, info_ptr);
+
+ buffer_reserve(&vs->tight.png, 2048);
+ buf = qemu_malloc(w * 3);
+ for (dy = 0; dy < h; dy++)
+ {
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ memcpy(buf, vs->tight.tight.buffer + (dy * w), w);
+ } else {
+ rgb_prepare_row(vs, buf, x, y + dy, w);
+ }
+ png_write_row(png_ptr, buf);
+ }
+ qemu_free(buf);
+
+ png_write_end(png_ptr, NULL);
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ png_free(png_ptr, png_palette);
+ }
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ vnc_write_u8(vs, VNC_TIGHT_PNG << 4);
+
+ tight_send_compact_size(vs, vs->tight.png.offset);
+ vnc_write(vs, vs->tight.png.buffer, vs->tight.png.offset);
+ buffer_reset(&vs->tight.png);
+ return 1;
+}
+#endif /* CONFIG_VNC_PNG */
+
+static void vnc_tight_start(VncState *vs)
+{
+ buffer_reset(&vs->tight.tight);
+
+ // make the output buffer be the zlib buffer, so we can compress it later
+ vs->tight.tmp = vs->output;
+ vs->output = vs->tight.tight;
+}
+
+static void vnc_tight_stop(VncState *vs)
+{
+ // switch back to normal output/zlib buffers
+ vs->tight.tight = vs->output;
+ vs->output = vs->tight.tmp;
+}
+
+static int send_sub_rect_nojpeg(VncState *vs, int x, int y, int w, int h,
+ int bg, int fg, int colors, VncPalette *palette)
+{
+ int ret;
+
+ if (colors == 0) {
+ if (tight_detect_smooth_image(vs, w, h)) {
+ ret = send_gradient_rect(vs, x, y, w, h);
+ } else {
+ ret = send_full_color_rect(vs, x, y, w, h);
+ }
+ } else if (colors == 1) {
+ ret = send_solid_rect(vs);
+ } else if (colors == 2) {
+ ret = send_mono_rect(vs, x, y, w, h, bg, fg);
+ } else if (colors <= 256) {
+ ret = send_palette_rect(vs, x, y, w, h, palette);
+ }
+ return ret;
+}
+
+#ifdef CONFIG_VNC_JPEG
+static int send_sub_rect_jpeg(VncState *vs, int x, int y, int w, int h,
+ int bg, int fg, int colors,
+ VncPalette *palette)
+{
+ int ret;
+
+ if (colors == 0) {
+ if (tight_detect_smooth_image(vs, w, h)) {
+ int quality = tight_conf[vs->tight.quality].jpeg_quality;
+
+ ret = send_jpeg_rect(vs, x, y, w, h, quality);
+ } else {
+ ret = send_full_color_rect(vs, x, y, w, h);
+ }
+ } else if (colors == 1) {
+ ret = send_solid_rect(vs);
+ } else if (colors == 2) {
+ ret = send_mono_rect(vs, x, y, w, h, bg, fg);
+ } else if (colors <= 256) {
+ if (colors > 96 &&
+ tight_detect_smooth_image(vs, w, h)) {
+ int quality = tight_conf[vs->tight.quality].jpeg_quality;
+
+ ret = send_jpeg_rect(vs, x, y, w, h, quality);
+ } else {
+ ret = send_palette_rect(vs, x, y, w, h, palette);
+ }
+ }
+ return ret;
+}
+#endif
+
+static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
+{
+ VncPalette *palette = NULL;
+ uint32_t bg = 0, fg = 0;
+ int colors;
+ int ret = 0;
+
+ vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type);
+
+ vnc_tight_start(vs);
+ vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+ vnc_tight_stop(vs);
+
+ colors = tight_fill_palette(vs, x, y, w * h, &fg, &bg, &palette);
+
+#ifdef CONFIG_VNC_JPEG
+ if (vs->tight.quality != -1) {
+ ret = send_sub_rect_jpeg(vs, x, y, w, h, bg, fg, colors, palette);
+ } else {
+ ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette);
+ }
+#else
+ ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette);
+#endif
+
+ palette_destroy(palette);
+ return ret;
+}
+
+static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h)
+{
+ vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type);
+
+ vnc_tight_start(vs);
+ vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+ vnc_tight_stop(vs);
+
+ return send_solid_rect(vs);
+}
+
+static int send_rect_simple(VncState *vs, int x, int y, int w, int h)
+{
+ int max_size, max_width;
+ int max_sub_width, max_sub_height;
+ int dx, dy;
+ int rw, rh;
+ int n = 0;
+
+ max_size = tight_conf[vs->tight.compression].max_rect_size;
+ max_width = tight_conf[vs->tight.compression].max_rect_width;
+
+ if (w > max_width || w * h > max_size) {
+ max_sub_width = (w > max_width) ? max_width : w;
+ max_sub_height = max_size / max_sub_width;
+
+ for (dy = 0; dy < h; dy += max_sub_height) {
+ for (dx = 0; dx < w; dx += max_width) {
+ rw = MIN(max_sub_width, w - dx);
+ rh = MIN(max_sub_height, h - dy);
+ n += send_sub_rect(vs, x+dx, y+dy, rw, rh);
+ }
+ }
+ } else {
+ n += send_sub_rect(vs, x, y, w, h);
+ }
+
+ return n;
+}
+
+static int find_large_solid_color_rect(VncState *vs, int x, int y,
+ int w, int h, int max_rows)
+{
+ int dx, dy, dw, dh;
+ int n = 0;
+
+ /* Try to find large solid-color areas and send them separately. */
+
+ for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+
+ /* If a rectangle becomes too large, send its upper part now. */
+
+ if (dy - y >= max_rows) {
+ n += send_rect_simple(vs, x, y, w, max_rows);
+ y += max_rows;
+ h -= max_rows;
+ }
+
+ dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (y + h - dy));
+
+ for (dx = x; dx < x + w; dx += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+ uint32_t color_value;
+ int x_best, y_best, w_best, h_best;
+
+ dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (x + w - dx));
+
+ if (!check_solid_tile(vs, dx, dy, dw, dh, &color_value, false)) {
+ continue ;
+ }
+
+ /* Get dimensions of solid-color area. */
+
+ find_best_solid_area(vs, dx, dy, w - (dx - x), h - (dy - y),
+ color_value, &w_best, &h_best);
+
+ /* Make sure a solid rectangle is large enough
+ (or the whole rectangle is of the same color). */
+
+ if (w_best * h_best != w * h &&
+ w_best * h_best < VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE) {
+ continue;
+ }
+
+ /* Try to extend solid rectangle to maximum size. */
+
+ x_best = dx; y_best = dy;
+ extend_solid_area(vs, x, y, w, h, color_value,
+ &x_best, &y_best, &w_best, &h_best);
+
+ /* Send rectangles at top and left to solid-color area. */
+
+ if (y_best != y) {
+ n += send_rect_simple(vs, x, y, w, y_best-y);
+ }
+ if (x_best != x) {
+ n += tight_send_framebuffer_update(vs, x, y_best,
+ x_best-x, h_best);
+ }
+
+ /* Send solid-color rectangle. */
+ n += send_sub_rect_solid(vs, x_best, y_best, w_best, h_best);
+
+ /* Send remaining rectangles (at right and bottom). */
+
+ if (x_best + w_best != x + w) {
+ n += tight_send_framebuffer_update(vs, x_best+w_best,
+ y_best,
+ w-(x_best-x)-w_best,
+ h_best);
+ }
+ if (y_best + h_best != y + h) {
+ n += tight_send_framebuffer_update(vs, x, y_best+h_best,
+ w, h-(y_best-y)-h_best);
+ }
+
+ /* Return after all recursive calls are done. */
+ return n;
+ }
+ }
+ return n + send_rect_simple(vs, x, y, w, h);
+}
+
+static int tight_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h)
+{
+ int max_rows;
+
+ if (vs->clientds.pf.bytes_per_pixel == 4 && vs->clientds.pf.rmax == 0xFF &&
+ vs->clientds.pf.bmax == 0xFF && vs->clientds.pf.gmax == 0xFF) {
+ vs->tight.pixel24 = true;
+ } else {
+ vs->tight.pixel24 = false;
+ }
+
+ if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE)
+ return send_rect_simple(vs, x, y, w, h);
+
+ /* Calculate maximum number of rows in one non-solid rectangle. */
+
+ max_rows = tight_conf[vs->tight.compression].max_rect_size;
+ max_rows /= MIN(tight_conf[vs->tight.compression].max_rect_width, w);
+
+ return find_large_solid_color_rect(vs, x, y, w, h, max_rows);
+}
+
+int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h)
+{
+ vs->tight.type = VNC_ENCODING_TIGHT;
+ return tight_send_framebuffer_update(vs, x, y, w, h);
+}
+
+int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h)
+{
+ vs->tight.type = VNC_ENCODING_TIGHT_PNG;
+ return tight_send_framebuffer_update(vs, x, y, w, h);
+}
+
+void vnc_tight_clear(VncState *vs)
+{
+ int i;
+ for (i=0; i<ARRAY_SIZE(vs->tight.stream); i++) {
+ if (vs->tight.stream[i].opaque) {
+ deflateEnd(&vs->tight.stream[i]);
+ }
+ }
+
+ buffer_free(&vs->tight.tight);
+ buffer_free(&vs->tight.zlib);
+ buffer_free(&vs->tight.gradient);
+#ifdef CONFIG_VNC_JPEG
+ buffer_free(&vs->tight.jpeg);
+#endif
+#ifdef CONFIG_VNC_PNG
+ buffer_free(&vs->tight.png);
+#endif
+}
diff --git a/vnc-encoding-tight.h b/ui/vnc-enc-tight.h
index 64d10625f..a3add788e 100644
--- a/vnc-encoding-tight.h
+++ b/ui/vnc-enc-tight.h
@@ -42,8 +42,9 @@
* bit 3: if 1, then compression stream 3 should be reset;
* bits 7-4: if 1000 (0x08), then the compression type is "fill",
* if 1001 (0x09), then the compression type is "jpeg",
+ * if 1010 (0x0A), then the compression type is "png",
* if 0xxx, then the compression type is "basic",
- * values greater than 1001 are not valid.
+ * values greater than 1010 are not valid.
*
* If the compression type is "basic", then bits 6..4 of the
* compression control byte (those xxx in 0xxx) specify the following:
@@ -53,17 +54,17 @@
* bit 6: if 1, then a "filter id" byte is following this byte.
*
*-- The data that follows after the compression control byte described
- * above depends on the compression type ("fill", "jpeg" or "basic").
+ * above depends on the compression type ("fill", "jpeg", "png" or "basic").
*
*-- If the compression type is "fill", then the only pixel value follows, in
* client pixel format (see NOTE 1). This value applies to all pixels of the
* rectangle.
*
- *-- If the compression type is "jpeg", the following data stream looks like
- * this:
+ *-- If the compression type is "jpeg" or "png", the following data stream
+ * looks like this:
*
* 1..3 bytes: data size (N) in compact representation;
- * N bytes: JPEG image.
+ * N bytes: JPEG or PNG image.
*
* Data size is compactly represented in one, two or three bytes, according
* to the following scheme:
@@ -144,7 +145,7 @@
*-- NOTE 2. The decoder must reset compression streams' states before
* decoding the rectangle, if some of bits 0,1,2,3 in the compression control
* byte are set to 1. Note that the decoder must reset zlib streams even if
- * the compression type is "fill" or "jpeg".
+ * the compression type is "fill", "jpeg" or "png".
*
*-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only
* when bits-per-pixel value is either 16 or 32, not 8.
@@ -158,7 +159,8 @@
#define VNC_TIGHT_EXPLICIT_FILTER 0x04
#define VNC_TIGHT_FILL 0x08
#define VNC_TIGHT_JPEG 0x09
-#define VNC_TIGHT_MAX_SUBENCODING 0x09
+#define VNC_TIGHT_PNG 0x0A
+#define VNC_TIGHT_MAX_SUBENCODING 0x0A
/* Filters to improve compression efficiency */
#define VNC_TIGHT_FILTER_COPY 0x00
@@ -173,4 +175,9 @@
#define VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE 2048
#define VNC_TIGHT_MAX_SPLIT_TILE_SIZE 16
+#define VNC_TIGHT_JPEG_MIN_RECT_SIZE 4096
+#define VNC_TIGHT_DETECT_SUBROW_WIDTH 7
+#define VNC_TIGHT_DETECT_MIN_WIDTH 8
+#define VNC_TIGHT_DETECT_MIN_HEIGHT 8
+
#endif /* VNC_ENCODING_TIGHT_H */
diff --git a/vnc-encoding-zlib.c b/ui/vnc-enc-zlib.c
index a99bc387d..3c6e6abf9 100644
--- a/vnc-encoding-zlib.c
+++ b/ui/vnc-enc-zlib.c
@@ -47,21 +47,21 @@ void vnc_zlib_zfree(void *x, void *addr)
static void vnc_zlib_start(VncState *vs)
{
- buffer_reset(&vs->zlib);
+ buffer_reset(&vs->zlib.zlib);
// make the output buffer be the zlib buffer, so we can compress it later
- vs->zlib_tmp = vs->output;
- vs->output = vs->zlib;
+ vs->zlib.tmp = vs->output;
+ vs->output = vs->zlib.zlib;
}
static int vnc_zlib_stop(VncState *vs)
{
- z_streamp zstream = &vs->zlib_stream;
+ z_streamp zstream = &vs->zlib.stream;
int previous_out;
// switch back to normal output/zlib buffers
- vs->zlib = vs->output;
- vs->output = vs->zlib_tmp;
+ vs->zlib.zlib = vs->output;
+ vs->output = vs->zlib.tmp;
// compress the zlib buffer
@@ -75,7 +75,7 @@ static int vnc_zlib_stop(VncState *vs)
zstream->zalloc = vnc_zlib_zalloc;
zstream->zfree = vnc_zlib_zfree;
- err = deflateInit2(zstream, vs->tight_compression, Z_DEFLATED, MAX_WBITS,
+ err = deflateInit2(zstream, vs->tight.compression, Z_DEFLATED, MAX_WBITS,
MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
if (err != Z_OK) {
@@ -83,24 +83,24 @@ static int vnc_zlib_stop(VncState *vs)
return -1;
}
- vs->zlib_level = vs->tight_compression;
+ vs->zlib.level = vs->tight.compression;
zstream->opaque = vs;
}
- if (vs->tight_compression != vs->zlib_level) {
- if (deflateParams(zstream, vs->tight_compression,
+ if (vs->tight.compression != vs->zlib.level) {
+ if (deflateParams(zstream, vs->tight.compression,
Z_DEFAULT_STRATEGY) != Z_OK) {
return -1;
}
- vs->zlib_level = vs->tight_compression;
+ vs->zlib.level = vs->tight.compression;
}
// reserve memory in output buffer
- buffer_reserve(&vs->output, vs->zlib.offset + 64);
+ buffer_reserve(&vs->output, vs->zlib.zlib.offset + 64);
// set pointers
- zstream->next_in = vs->zlib.buffer;
- zstream->avail_in = vs->zlib.offset;
+ zstream->next_in = vs->zlib.zlib.buffer;
+ zstream->avail_in = vs->zlib.zlib.offset;
zstream->next_out = vs->output.buffer + vs->output.offset;
zstream->avail_out = vs->output.capacity - vs->output.offset;
zstream->data_type = Z_BINARY;
@@ -145,8 +145,8 @@ int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
void vnc_zlib_clear(VncState *vs)
{
- if (vs->zlib_stream.opaque) {
- deflateEnd(&vs->zlib_stream);
+ if (vs->zlib.stream.opaque) {
+ deflateEnd(&vs->zlib.stream);
}
- buffer_free(&vs->zlib);
+ buffer_free(&vs->zlib.zlib);
}
diff --git a/ui/vnc-jobs-async.c b/ui/vnc-jobs-async.c
new file mode 100644
index 000000000..6e9cf08b6
--- /dev/null
+++ b/ui/vnc-jobs-async.c
@@ -0,0 +1,331 @@
+/*
+ * QEMU VNC display driver
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#include "vnc.h"
+#include "vnc-jobs.h"
+
+/*
+ * Locking:
+ *
+ * There is three levels of locking:
+ * - jobs queue lock: for each operation on the queue (push, pop, isEmpty?)
+ * - VncDisplay global lock: mainly used for framebuffer updates to avoid
+ * screen corruption if the framebuffer is updated
+ * while the worker is doing something.
+ * - VncState::output lock: used to make sure the output buffer is not corrupted
+ * if two threads try to write on it at the same time
+ *
+ * While the VNC worker thread is working, the VncDisplay global lock is hold
+ * to avoid screen corruptions (this does not block vnc_refresh() because it
+ * uses trylock()) but the output lock is not hold because the thread work on
+ * its own output buffer.
+ * When the encoding job is done, the worker thread will hold the output lock
+ * and copy its output buffer in vs->output.
+*/
+
+struct VncJobQueue {
+ QemuCond cond;
+ QemuMutex mutex;
+ QemuThread thread;
+ Buffer buffer;
+ bool exit;
+ QTAILQ_HEAD(, VncJob) jobs;
+};
+
+typedef struct VncJobQueue VncJobQueue;
+
+/*
+ * We use a single global queue, but most of the functions are
+ * already reetrant, so we can easilly add more than one encoding thread
+ */
+static VncJobQueue *queue;
+
+static void vnc_lock_queue(VncJobQueue *queue)
+{
+ qemu_mutex_lock(&queue->mutex);
+}
+
+static void vnc_unlock_queue(VncJobQueue *queue)
+{
+ qemu_mutex_unlock(&queue->mutex);
+}
+
+VncJob *vnc_job_new(VncState *vs)
+{
+ VncJob *job = qemu_mallocz(sizeof(VncJob));
+
+ job->vs = vs;
+ vnc_lock_queue(queue);
+ QLIST_INIT(&job->rectangles);
+ vnc_unlock_queue(queue);
+ return job;
+}
+
+int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
+{
+ VncRectEntry *entry = qemu_mallocz(sizeof(VncRectEntry));
+
+ entry->rect.x = x;
+ entry->rect.y = y;
+ entry->rect.w = w;
+ entry->rect.h = h;
+
+ vnc_lock_queue(queue);
+ QLIST_INSERT_HEAD(&job->rectangles, entry, next);
+ vnc_unlock_queue(queue);
+ return 1;
+}
+
+void vnc_job_push(VncJob *job)
+{
+ vnc_lock_queue(queue);
+ if (queue->exit || QLIST_EMPTY(&job->rectangles)) {
+ qemu_free(job);
+ } else {
+ QTAILQ_INSERT_TAIL(&queue->jobs, job, next);
+ qemu_cond_broadcast(&queue->cond);
+ }
+ vnc_unlock_queue(queue);
+}
+
+static bool vnc_has_job_locked(VncState *vs)
+{
+ VncJob *job;
+
+ QTAILQ_FOREACH(job, &queue->jobs, next) {
+ if (job->vs == vs || !vs) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool vnc_has_job(VncState *vs)
+{
+ bool ret;
+
+ vnc_lock_queue(queue);
+ ret = vnc_has_job_locked(vs);
+ vnc_unlock_queue(queue);
+ return ret;
+}
+
+void vnc_jobs_clear(VncState *vs)
+{
+ VncJob *job, *tmp;
+
+ vnc_lock_queue(queue);
+ QTAILQ_FOREACH_SAFE(job, &queue->jobs, next, tmp) {
+ if (job->vs == vs || !vs) {
+ QTAILQ_REMOVE(&queue->jobs, job, next);
+ }
+ }
+ vnc_unlock_queue(queue);
+}
+
+void vnc_jobs_join(VncState *vs)
+{
+ vnc_lock_queue(queue);
+ while (vnc_has_job_locked(vs)) {
+ qemu_cond_wait(&queue->cond, &queue->mutex);
+ }
+ vnc_unlock_queue(queue);
+}
+
+/*
+ * Copy data for local use
+ */
+static void vnc_async_encoding_start(VncState *orig, VncState *local)
+{
+ local->vnc_encoding = orig->vnc_encoding;
+ local->features = orig->features;
+ local->ds = orig->ds;
+ local->vd = orig->vd;
+ local->write_pixels = orig->write_pixels;
+ local->clientds = orig->clientds;
+ local->tight = orig->tight;
+ local->zlib = orig->zlib;
+ local->hextile = orig->hextile;
+ local->output = queue->buffer;
+ local->csock = -1; /* Don't do any network work on this thread */
+
+ buffer_reset(&local->output);
+}
+
+static void vnc_async_encoding_end(VncState *orig, VncState *local)
+{
+ orig->tight = local->tight;
+ orig->zlib = local->zlib;
+ orig->hextile = local->hextile;
+}
+
+static int vnc_worker_thread_loop(VncJobQueue *queue)
+{
+ VncJob *job;
+ VncRectEntry *entry, *tmp;
+ VncState vs;
+ int n_rectangles;
+ int saved_offset;
+ bool flush;
+
+ vnc_lock_queue(queue);
+ while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) {
+ qemu_cond_wait(&queue->cond, &queue->mutex);
+ }
+ /* Here job can only be NULL if queue->exit is true */
+ job = QTAILQ_FIRST(&queue->jobs);
+ vnc_unlock_queue(queue);
+
+ if (queue->exit) {
+ return -1;
+ }
+
+ vnc_lock_output(job->vs);
+ if (job->vs->csock == -1 || job->vs->abort == true) {
+ goto disconnected;
+ }
+ vnc_unlock_output(job->vs);
+
+ /* Make a local copy of vs and switch output buffers */
+ vnc_async_encoding_start(job->vs, &vs);
+
+ /* Start sending rectangles */
+ n_rectangles = 0;
+ vnc_write_u8(&vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(&vs, 0);
+ saved_offset = vs.output.offset;
+ vnc_write_u16(&vs, 0);
+
+ vnc_lock_display(job->vs->vd);
+ QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) {
+ int n;
+
+ if (job->vs->csock == -1) {
+ vnc_unlock_display(job->vs->vd);
+ goto disconnected;
+ }
+
+ n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y,
+ entry->rect.w, entry->rect.h);
+
+ if (n >= 0) {
+ n_rectangles += n;
+ }
+ qemu_free(entry);
+ }
+ vnc_unlock_display(job->vs->vd);
+
+ /* Put n_rectangles at the beginning of the message */
+ vs.output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
+ vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
+
+ /* Switch back buffers */
+ vnc_lock_output(job->vs);
+ if (job->vs->csock == -1) {
+ goto disconnected;
+ }
+
+ vnc_write(job->vs, vs.output.buffer, vs.output.offset);
+
+disconnected:
+ /* Copy persistent encoding data */
+ vnc_async_encoding_end(job->vs, &vs);
+ flush = (job->vs->csock != -1 && job->vs->abort != true);
+ vnc_unlock_output(job->vs);
+
+ if (flush) {
+ vnc_flush(job->vs);
+ }
+
+ vnc_lock_queue(queue);
+ QTAILQ_REMOVE(&queue->jobs, job, next);
+ vnc_unlock_queue(queue);
+ qemu_cond_broadcast(&queue->cond);
+ qemu_free(job);
+ return 0;
+}
+
+static VncJobQueue *vnc_queue_init(void)
+{
+ VncJobQueue *queue = qemu_mallocz(sizeof(VncJobQueue));
+
+ qemu_cond_init(&queue->cond);
+ qemu_mutex_init(&queue->mutex);
+ QTAILQ_INIT(&queue->jobs);
+ return queue;
+}
+
+static void vnc_queue_clear(VncJobQueue *q)
+{
+ qemu_cond_destroy(&queue->cond);
+ qemu_mutex_destroy(&queue->mutex);
+ buffer_free(&queue->buffer);
+ qemu_free(q);
+ queue = NULL; /* Unset global queue */
+}
+
+static void *vnc_worker_thread(void *arg)
+{
+ VncJobQueue *queue = arg;
+
+ qemu_thread_self(&queue->thread);
+
+ while (!vnc_worker_thread_loop(queue)) ;
+ vnc_queue_clear(queue);
+ return NULL;
+}
+
+void vnc_start_worker_thread(void)
+{
+ VncJobQueue *q;
+
+ if (vnc_worker_thread_running())
+ return ;
+
+ q = vnc_queue_init();
+ qemu_thread_create(&q->thread, vnc_worker_thread, q);
+ queue = q; /* Set global queue */
+}
+
+bool vnc_worker_thread_running(void)
+{
+ return queue; /* Check global queue */
+}
+
+void vnc_stop_worker_thread(void)
+{
+ if (!vnc_worker_thread_running())
+ return ;
+
+ /* Remove all jobs and wake up the thread */
+ vnc_lock_queue(queue);
+ queue->exit = true;
+ vnc_unlock_queue(queue);
+ vnc_jobs_clear(NULL);
+ qemu_cond_broadcast(&queue->cond);
+}
diff --git a/ui/vnc-jobs-sync.c b/ui/vnc-jobs-sync.c
new file mode 100644
index 000000000..49b77afcc
--- /dev/null
+++ b/ui/vnc-jobs-sync.c
@@ -0,0 +1,73 @@
+/*
+ * QEMU VNC display driver
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc.h"
+#include "vnc-jobs.h"
+
+void vnc_jobs_clear(VncState *vs)
+{
+}
+
+void vnc_jobs_join(VncState *vs)
+{
+}
+
+VncJob *vnc_job_new(VncState *vs)
+{
+ vs->job.vs = vs;
+ vs->job.rectangles = 0;
+
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vs->job.saved_offset = vs->output.offset;
+ vnc_write_u16(vs, 0);
+ return &vs->job;
+}
+
+void vnc_job_push(VncJob *job)
+{
+ VncState *vs = job->vs;
+
+ vs->output.buffer[job->saved_offset] = (job->rectangles >> 8) & 0xFF;
+ vs->output.buffer[job->saved_offset + 1] = job->rectangles & 0xFF;
+ vnc_flush(job->vs);
+}
+
+int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
+{
+ int n;
+
+ n = vnc_send_framebuffer_update(job->vs, x, y, w, h);
+ if (n >= 0)
+ job->rectangles += n;
+ return n;
+}
+
+bool vnc_has_job(VncState *vs)
+{
+ return false;
+}
diff --git a/ui/vnc-jobs.h b/ui/vnc-jobs.h
new file mode 100644
index 000000000..b8dab8169
--- /dev/null
+++ b/ui/vnc-jobs.h
@@ -0,0 +1,87 @@
+/*
+ * QEMU VNC display driver
+ *
+ * From libvncserver/rfb/rfbproto.h
+ * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
+ * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved.
+ * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef VNC_JOBS_H
+#define VNC_JOBS_H
+
+/* Jobs */
+VncJob *vnc_job_new(VncState *vs);
+int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h);
+void vnc_job_push(VncJob *job);
+bool vnc_has_job(VncState *vs);
+void vnc_jobs_clear(VncState *vs);
+void vnc_jobs_join(VncState *vs);
+
+#ifdef CONFIG_VNC_THREAD
+
+void vnc_start_worker_thread(void);
+bool vnc_worker_thread_running(void);
+void vnc_stop_worker_thread(void);
+
+#endif /* CONFIG_VNC_THREAD */
+
+/* Locks */
+static inline int vnc_trylock_display(VncDisplay *vd)
+{
+#ifdef CONFIG_VNC_THREAD
+ return qemu_mutex_trylock(&vd->mutex);
+#else
+ return 0;
+#endif
+}
+
+static inline void vnc_lock_display(VncDisplay *vd)
+{
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_lock(&vd->mutex);
+#endif
+}
+
+static inline void vnc_unlock_display(VncDisplay *vd)
+{
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_unlock(&vd->mutex);
+#endif
+}
+
+static inline void vnc_lock_output(VncState *vs)
+{
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_lock(&vs->output_mutex);
+#endif
+}
+
+static inline void vnc_unlock_output(VncState *vs)
+{
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_unlock(&vs->output_mutex);
+#endif
+}
+
+#endif /* VNC_JOBS_H */
diff --git a/ui/vnc-palette.c b/ui/vnc-palette.c
new file mode 100644
index 000000000..bff6445cc
--- /dev/null
+++ b/ui/vnc-palette.c
@@ -0,0 +1,136 @@
+/*
+ * QEMU VNC display driver: palette hash table
+ *
+ * From libvncserver/libvncserver/tight.c
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc-palette.h"
+
+static VncPaletteEntry *palette_find(const VncPalette *palette,
+ uint32_t color, unsigned int hash)
+{
+ VncPaletteEntry *entry;
+
+ QLIST_FOREACH(entry, &palette->table[hash], next) {
+ if (entry->color == color) {
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+static unsigned int palette_hash(uint32_t rgb, int bpp)
+{
+ if (bpp == 16) {
+ return ((unsigned int)(((rgb >> 8) + rgb) & 0xFF));
+ } else {
+ return ((unsigned int)(((rgb >> 16) + (rgb >> 8)) & 0xFF));
+ }
+}
+
+VncPalette *palette_new(size_t max, int bpp)
+{
+ VncPalette *palette;
+
+ palette = qemu_mallocz(sizeof(*palette));
+ palette->max = max;
+ palette->bpp = bpp;
+ return palette;
+}
+
+void palette_destroy(VncPalette *palette)
+{
+ int i;
+
+ if (palette == NULL) {
+ return ;
+ }
+
+ for (i = 0; i < VNC_PALETTE_HASH_SIZE; i++) {
+ VncPaletteEntry *entry = QLIST_FIRST(&palette->table[i]);
+ while (entry) {
+ VncPaletteEntry *tmp = QLIST_NEXT(entry, next);
+ QLIST_REMOVE(entry, next);
+ qemu_free(entry);
+ entry = tmp;
+ }
+ }
+
+ qemu_free(palette);
+}
+
+int palette_put(VncPalette *palette, uint32_t color)
+{
+ unsigned int hash;
+ unsigned int idx = palette->size;
+ VncPaletteEntry *entry;
+
+ hash = palette_hash(color, palette->bpp) % VNC_PALETTE_HASH_SIZE;
+ entry = palette_find(palette, color, hash);
+
+ if (!entry && palette->size >= palette->max) {
+ return 0;
+ }
+ if (!entry) {
+ VncPaletteEntry *entry;
+
+ entry = qemu_mallocz(sizeof(*entry));
+ entry->color = color;
+ entry->idx = idx;
+ QLIST_INSERT_HEAD(&palette->table[hash], entry, next);
+ palette->size++;
+ }
+ return palette->size;
+}
+
+int palette_idx(const VncPalette *palette, uint32_t color)
+{
+ VncPaletteEntry *entry;
+ unsigned int hash;
+
+ hash = palette_hash(color, palette->bpp) % VNC_PALETTE_HASH_SIZE;
+ entry = palette_find(palette, color, hash);
+ return (entry == NULL ? -1 : entry->idx);
+}
+
+size_t palette_size(const VncPalette *palette)
+{
+ return palette->size;
+}
+
+void palette_iter(const VncPalette *palette,
+ void (*iter)(int idx, uint32_t color, void *opaque),
+ void *opaque)
+{
+ int i;
+ VncPaletteEntry *entry;
+
+ for (i = 0; i < VNC_PALETTE_HASH_SIZE; i++) {
+ QLIST_FOREACH(entry, &palette->table[i], next) {
+ iter(entry->idx, entry->color, opaque);
+ }
+ }
+}
diff --git a/ui/vnc-palette.h b/ui/vnc-palette.h
new file mode 100644
index 000000000..d0645ebde
--- /dev/null
+++ b/ui/vnc-palette.h
@@ -0,0 +1,63 @@
+/*
+ * QEMU VNC display driver: palette hash table
+ *
+ * From libvncserver/libvncserver/tight.c
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef VNC_PALETTE_H
+#define VNC_PALETTE_H
+
+#include "qlist.h"
+#include "qemu-queue.h"
+#include <stdint.h>
+
+#define VNC_PALETTE_HASH_SIZE 256
+
+typedef struct VncPaletteEntry {
+ int idx;
+ uint32_t color;
+ QLIST_ENTRY(VncPaletteEntry) next;
+} VncPaletteEntry;
+
+typedef struct VncPalette {
+ QObject_HEAD;
+ size_t size;
+ size_t max;
+ int bpp;
+ QLIST_HEAD(,VncPaletteEntry) table[VNC_PALETTE_HASH_SIZE];
+} VncPalette;
+
+VncPalette *palette_new(size_t max, int bpp);
+void palette_destroy(VncPalette *palette);
+
+int palette_put(VncPalette *palette, uint32_t color);
+int palette_idx(const VncPalette *palette, uint32_t color);
+size_t palette_size(const VncPalette *palette);
+
+void palette_iter(const VncPalette *palette,
+ void (*iter)(int idx, uint32_t color, void *opaque),
+ void *opaque);
+
+#endif /* VNC_PALETTE_H */
diff --git a/vnc-tls.c b/ui/vnc-tls.c
index dec626c53..dec626c53 100644
--- a/vnc-tls.c
+++ b/ui/vnc-tls.c
diff --git a/vnc-tls.h b/ui/vnc-tls.h
index 2b9363389..2b9363389 100644
--- a/vnc-tls.h
+++ b/ui/vnc-tls.h
diff --git a/vnc.c b/ui/vnc.c
index ed0e09600..7fc40acae 100644
--- a/vnc.c
+++ b/ui/vnc.c
@@ -25,6 +25,7 @@
*/
#include "vnc.h"
+#include "vnc-jobs.h"
#include "sysemu.h"
#include "qemu_socket.h"
#include "qemu-timer.h"
@@ -45,7 +46,6 @@
} \
}
-
static VncDisplay *vnc_display; /* needed for info vnc */
static DisplayChangeListener *dcl;
@@ -351,10 +351,6 @@ void do_info_vnc(Monitor *mon, QObject **ret_data)
}
}
-static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
- return (vs->features & (1 << feature));
-}
-
/* TODO
1) Get the queue working for IO.
2) there is some weirdness when using the -S option (the screen is grey
@@ -363,6 +359,7 @@ static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
*/
static int vnc_update_client(VncState *vs, int has_dirty);
+static int vnc_update_client_sync(VncState *vs, int has_dirty);
static void vnc_disconnect_start(VncState *vs);
static void vnc_disconnect_finish(VncState *vs);
static void vnc_init_timer(VncDisplay *vd);
@@ -506,19 +503,48 @@ static void vnc_desktop_resize(VncState *vs)
}
vs->client_width = ds_get_width(ds);
vs->client_height = ds_get_height(ds);
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, 0, 0, vs->client_width, vs->client_height,
VNC_ENCODING_DESKTOPRESIZE);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
+#ifdef CONFIG_VNC_THREAD
+static void vnc_abort_display_jobs(VncDisplay *vd)
+{
+ VncState *vs;
+
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_lock_output(vs);
+ vs->abort = true;
+ vnc_unlock_output(vs);
+ }
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_jobs_join(vs);
+ }
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_lock_output(vs);
+ vs->abort = false;
+ vnc_unlock_output(vs);
+ }
+}
+#else
+static void vnc_abort_display_jobs(VncDisplay *vd)
+{
+}
+#endif
+
static void vnc_dpy_resize(DisplayState *ds)
{
VncDisplay *vd = ds->opaque;
VncState *vs;
+ vnc_abort_display_jobs(vd);
+
/* server surface */
if (!vd->server)
vd->server = qemu_mallocz(sizeof(*vd->server));
@@ -646,7 +672,7 @@ int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
return 1;
}
-static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
{
int n = 0;
@@ -661,6 +687,9 @@ static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
case VNC_ENCODING_TIGHT:
n = vnc_tight_send_framebuffer_update(vs, x, y, w, h);
break;
+ case VNC_ENCODING_TIGHT_PNG:
+ n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h);
+ break;
default:
vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW);
n = vnc_raw_send_framebuffer_update(vs, x, y, w, h);
@@ -672,12 +701,14 @@ static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
{
/* send bitblit op to the vnc client */
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT);
vnc_write_u16(vs, src_x);
vnc_write_u16(vs, src_y);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
@@ -694,7 +725,7 @@ static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
vs->force_update = 1;
- vnc_update_client(vs, 1);
+ vnc_update_client_sync(vs, 1);
/* vs might be free()ed here */
}
}
@@ -765,6 +796,7 @@ static int vnc_cursor_define(VncState *vs)
int isize;
if (vnc_has_feature(vs, VNC_FEATURE_RICH_CURSOR)) {
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0); /* padding */
vnc_write_u16(vs, 1); /* # of rects */
@@ -773,6 +805,7 @@ static int vnc_cursor_define(VncState *vs)
isize = c->width * c->height * vs->clientds.pf.bytes_per_pixel;
vnc_write_pixels_generic(vs, &pf, c->data, isize);
vnc_write(vs, vs->vd->cursor_mask, vs->vd->cursor_msize);
+ vnc_unlock_output(vs);
return 0;
}
return -1;
@@ -814,15 +847,29 @@ static int find_and_clear_dirty_height(struct VncState *vs,
return h;
}
+#ifdef CONFIG_VNC_THREAD
+static int vnc_update_client_sync(VncState *vs, int has_dirty)
+{
+ int ret = vnc_update_client(vs, has_dirty);
+ vnc_jobs_join(vs);
+ return ret;
+}
+#else
+static int vnc_update_client_sync(VncState *vs, int has_dirty)
+{
+ return vnc_update_client(vs, has_dirty);
+}
+#endif
+
static int vnc_update_client(VncState *vs, int has_dirty)
{
if (vs->need_update && vs->csock != -1) {
VncDisplay *vd = vs->vd;
+ VncJob *job;
int y;
- int n_rectangles;
- int saved_offset;
int width, height;
- int n;
+ int n = 0;
+
if (vs->output.offset && !vs->audio_cap && !vs->force_update)
/* kernel send buffers are full -> drop frames to throttle */
@@ -837,11 +884,7 @@ static int vnc_update_client(VncState *vs, int has_dirty)
* happening in parallel don't disturb us, the next pass will
* send them to the client.
*/
- n_rectangles = 0;
- vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
- vnc_write_u8(vs, 0);
- saved_offset = vs->output.offset;
- vnc_write_u16(vs, 0);
+ job = vnc_job_new(vs);
width = MIN(vd->server->width, vs->client_width);
height = MIN(vd->server->height, vs->client_height);
@@ -858,25 +901,23 @@ static int vnc_update_client(VncState *vs, int has_dirty)
} else {
if (last_x != -1) {
int h = find_and_clear_dirty_height(vs, y, last_x, x);
- n = send_framebuffer_update(vs, last_x * 16, y,
- (x - last_x) * 16, h);
- n_rectangles += n;
+
+ n += vnc_job_add_rect(job, last_x * 16, y,
+ (x - last_x) * 16, h);
}
last_x = -1;
}
}
if (last_x != -1) {
int h = find_and_clear_dirty_height(vs, y, last_x, x);
- n = send_framebuffer_update(vs, last_x * 16, y,
- (x - last_x) * 16, h);
- n_rectangles += n;
+ n += vnc_job_add_rect(job, last_x * 16, y,
+ (x - last_x) * 16, h);
}
}
- vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
- vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
- vnc_flush(vs);
+
+ vnc_job_push(job);
vs->force_update = 0;
- return n_rectangles;
+ return n;
}
if (vs->csock == -1)
@@ -892,16 +933,20 @@ static void audio_capture_notify(void *opaque, audcnotification_e cmd)
switch (cmd) {
case AUD_CNOTIFY_DISABLE:
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_END);
+ vnc_unlock_output(vs);
vnc_flush(vs);
break;
case AUD_CNOTIFY_ENABLE:
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_BEGIN);
+ vnc_unlock_output(vs);
vnc_flush(vs);
break;
}
@@ -915,11 +960,13 @@ static void audio_capture(void *opaque, void *buf, int size)
{
VncState *vs = opaque;
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
vnc_write_u32(vs, size);
vnc_write(vs, buf, size);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
@@ -961,6 +1008,9 @@ static void vnc_disconnect_start(VncState *vs)
static void vnc_disconnect_finish(VncState *vs)
{
+ vnc_jobs_join(vs); /* Wait encoding jobs */
+
+ vnc_lock_output(vs);
vnc_qmp_event(vs, QEVENT_VNC_DISCONNECTED);
buffer_free(&vs->input);
@@ -989,6 +1039,11 @@ static void vnc_disconnect_finish(VncState *vs)
vnc_remove_timer(vs->vd);
if (vs->vd->lock_key_sync)
qemu_remove_led_event_handler(vs->led);
+ vnc_unlock_output(vs);
+
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_destroy(&vs->output_mutex);
+#endif
qemu_free(vs);
}
@@ -1108,7 +1163,7 @@ static long vnc_client_write_plain(VncState *vs)
* the client socket. Will delegate actual work according to whether
* SASL SSF layers are enabled (thus requiring encryption calls)
*/
-void vnc_client_write(void *opaque)
+static void vnc_client_write_locked(void *opaque)
{
VncState *vs = opaque;
@@ -1122,6 +1177,19 @@ void vnc_client_write(void *opaque)
vnc_client_write_plain(vs);
}
+void vnc_client_write(void *opaque)
+{
+ VncState *vs = opaque;
+
+ vnc_lock_output(vs);
+ if (vs->output.offset) {
+ vnc_client_write_locked(opaque);
+ } else {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ }
+ vnc_unlock_output(vs);
+}
+
void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
{
vs->read_handler = func;
@@ -1273,8 +1341,11 @@ void vnc_write_u8(VncState *vs, uint8_t value)
void vnc_flush(VncState *vs)
{
- if (vs->csock != -1 && vs->output.offset)
- vnc_client_write(vs);
+ vnc_lock_output(vs);
+ if (vs->csock != -1 && vs->output.offset) {
+ vnc_client_write_locked(vs);
+ }
+ vnc_unlock_output(vs);
}
uint8_t read_u8(uint8_t *data, size_t offset)
@@ -1309,12 +1380,14 @@ static void check_pointer_type_change(Notifier *notifier)
int absolute = kbd_mouse_is_absolute();
if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, absolute, 0,
ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_POINTER_TYPE_CHANGE);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
vs->absolute = absolute;
@@ -1618,21 +1691,25 @@ static void framebuffer_update_request(VncState *vs, int incremental,
static void send_ext_key_event_ack(VncState *vs)
{
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_EXT_KEY_EVENT);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
static void send_ext_audio_ack(VncState *vs)
{
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_AUDIO);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
@@ -1643,8 +1720,8 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
vs->features = 0;
vs->vnc_encoding = 0;
- vs->tight_compression = 9;
- vs->tight_quality = 9;
+ vs->tight.compression = 9;
+ vs->tight.quality = -1; /* Lossless by default */
vs->absolute = -1;
/*
@@ -1669,6 +1746,10 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
vs->features |= VNC_FEATURE_TIGHT_MASK;
vs->vnc_encoding = enc;
break;
+ case VNC_ENCODING_TIGHT_PNG:
+ vs->features |= VNC_FEATURE_TIGHT_PNG_MASK;
+ vs->vnc_encoding = enc;
+ break;
case VNC_ENCODING_ZLIB:
vs->features |= VNC_FEATURE_ZLIB_MASK;
vs->vnc_encoding = enc;
@@ -1692,10 +1773,10 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
vs->features |= VNC_FEATURE_WMVI_MASK;
break;
case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9:
- vs->tight_compression = (enc & 0x0F);
+ vs->tight.compression = (enc & 0x0F);
break;
case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9:
- vs->tight_quality = (enc & 0x0F);
+ vs->tight.quality = (enc & 0x0F);
break;
default:
VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc);
@@ -1791,12 +1872,14 @@ static void vnc_colordepth(VncState *vs)
{
if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
/* Sending a WMVi message to notify the client*/
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds),
ds_get_height(vs->ds), VNC_ENCODING_WMVi);
pixel_format_message(vs);
+ vnc_unlock_output(vs);
vnc_flush(vs);
} else {
set_pixel_conversion(vs);
@@ -2224,12 +2307,21 @@ static void vnc_refresh(void *opaque)
vga_hw_update();
+ if (vnc_trylock_display(vd)) {
+ vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) +
+ vd->timer_interval);
+ return;
+ }
+
has_dirty = vnc_refresh_server_surface(vd);
+ vnc_unlock_display(vd);
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
rects += vnc_update_client(vs, has_dirty);
/* vs might be free()ed here */
}
+
/* vd->timer could be NULL now if the last client disconnected,
* in this case don't update the timer */
if (vd->timer == NULL)
@@ -2288,6 +2380,10 @@ static void vnc_connect(VncDisplay *vd, int csock)
vs->as.fmt = AUD_FMT_S16;
vs->as.endianness = 0;
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_init(&vs->output_mutex);
+#endif
+
QTAILQ_INSERT_HEAD(&vd->clients, vs, next);
vga_hw_update();
@@ -2345,6 +2441,11 @@ void vnc_display_init(DisplayState *ds)
if (!vs->kbd_layout)
exit(1);
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_init(&vs->mutex);
+ vnc_start_worker_thread();
+#endif
+
dcl->dpy_copy = vnc_dpy_copy;
dcl->dpy_update = vnc_dpy_update;
dcl->dpy_resize = vnc_dpy_resize;
@@ -2482,6 +2583,8 @@ int vnc_display_open(DisplayState *ds, const char *display)
#endif
} else if (strncmp(options, "acl", 3) == 0) {
acl = 1;
+ } else if (strncmp(options, "lossy", 5) == 0) {
+ vs->lossy = true;
}
}
diff --git a/vnc.h b/ui/vnc.h
index 7b64cf730..9619b247f 100644
--- a/vnc.h
+++ b/ui/vnc.h
@@ -29,10 +29,14 @@
#include "qemu-common.h"
#include "qemu-queue.h"
+#ifdef CONFIG_VNC_THREAD
+#include "qemu-thread.h"
+#endif
#include "console.h"
#include "monitor.h"
#include "audio/audio.h"
#include <zlib.h>
+#include <stdbool.h>
#include "keymaps.h"
@@ -58,6 +62,9 @@ typedef struct Buffer
} Buffer;
typedef struct VncState VncState;
+typedef struct VncJob VncJob;
+typedef struct VncRect VncRect;
+typedef struct VncRectEntry VncRectEntry;
typedef int VncReadEvent(VncState *vs, uint8_t *data, size_t len);
@@ -100,6 +107,9 @@ struct VncDisplay
DisplayState *ds;
kbd_layout_t *kbd_layout;
int lock_key_sync;
+#ifdef CONFIG_VNC_THREAD
+ QemuMutex mutex;
+#endif
QEMUCursor *cursor;
int cursor_msize;
@@ -111,6 +121,7 @@ struct VncDisplay
char *display;
char *password;
int auth;
+ bool lossy;
#ifdef CONFIG_VNC_TLS
int subauth; /* Used by VeNCrypt */
VncDisplayTLS tls;
@@ -120,6 +131,67 @@ struct VncDisplay
#endif
};
+typedef struct VncTight {
+ int type;
+ uint8_t quality;
+ uint8_t compression;
+ uint8_t pixel24;
+ Buffer tight;
+ Buffer tmp;
+ Buffer zlib;
+ Buffer gradient;
+#ifdef CONFIG_VNC_JPEG
+ Buffer jpeg;
+#endif
+#ifdef CONFIG_VNC_PNG
+ Buffer png;
+#endif
+ int levels[4];
+ z_stream stream[4];
+} VncTight;
+
+typedef struct VncHextile {
+ VncSendHextileTile *send_tile;
+} VncHextile;
+
+typedef struct VncZlib {
+ Buffer zlib;
+ Buffer tmp;
+ z_stream stream;
+ int level;
+} VncZlib;
+
+#ifdef CONFIG_VNC_THREAD
+struct VncRect
+{
+ int x;
+ int y;
+ int w;
+ int h;
+};
+
+struct VncRectEntry
+{
+ struct VncRect rect;
+ QLIST_ENTRY(VncRectEntry) next;
+};
+
+struct VncJob
+{
+ VncState *vs;
+
+ QLIST_HEAD(, VncRectEntry) rectangles;
+ QTAILQ_ENTRY(VncJob) next;
+};
+#else
+struct VncJob
+{
+ VncState *vs;
+ int rectangles;
+ size_t saved_offset;
+};
+#endif
+
struct VncState
{
int csock;
@@ -167,26 +239,20 @@ struct VncState
uint8_t modifiers_state[256];
QEMUPutLEDEntry *led;
- /* Encoding specific */
-
- /* Tight */
- uint8_t tight_quality;
- uint8_t tight_compression;
- uint8_t tight_pixel24;
- Buffer tight;
- Buffer tight_tmp;
- Buffer tight_zlib;
- int tight_levels[4];
- z_stream tight_stream[4];
+ bool abort;
+#ifndef CONFIG_VNC_THREAD
+ VncJob job;
+#else
+ QemuMutex output_mutex;
+#endif
- /* Hextile */
- VncSendHextileTile *send_hextile_tile;
+ /* Encoding specific, if you add something here, don't forget to
+ * update vnc_async_encoding_start()
+ */
+ VncTight tight;
+ VncZlib zlib;
+ VncHextile hextile;
- /* Zlib */
- Buffer zlib;
- Buffer zlib_tmp;
- z_stream zlib_stream;
- int zlib_level;
Notifier mouse_mode_notifier;
@@ -253,6 +319,7 @@ enum {
#define VNC_ENCODING_POINTER_TYPE_CHANGE 0XFFFFFEFF /* -257 */
#define VNC_ENCODING_EXT_KEY_EVENT 0XFFFFFEFE /* -258 */
#define VNC_ENCODING_AUDIO 0XFFFFFEFD /* -259 */
+#define VNC_ENCODING_TIGHT_PNG 0xFFFFFEFC /* -260 */
#define VNC_ENCODING_WMVi 0x574D5669
/*****************************************************************************
@@ -269,6 +336,7 @@ enum {
#define VNC_TIGHT_CCB_TYPE_MASK (0x0f << 4)
#define VNC_TIGHT_CCB_TYPE_FILL (0x08 << 4)
#define VNC_TIGHT_CCB_TYPE_JPEG (0x09 << 4)
+#define VNC_TIGHT_CCB_TYPE_PNG (0x0A << 4)
#define VNC_TIGHT_CCB_BASIC_MAX (0x07 << 4)
#define VNC_TIGHT_CCB_BASIC_ZLIB (0x03 << 4)
#define VNC_TIGHT_CCB_BASIC_FILTER (0x04 << 4)
@@ -287,6 +355,7 @@ enum {
#define VNC_FEATURE_ZLIB 5
#define VNC_FEATURE_COPYRECT 6
#define VNC_FEATURE_RICH_CURSOR 7
+#define VNC_FEATURE_TIGHT_PNG 8
#define VNC_FEATURE_RESIZE_MASK (1 << VNC_FEATURE_RESIZE)
#define VNC_FEATURE_HEXTILE_MASK (1 << VNC_FEATURE_HEXTILE)
@@ -296,6 +365,7 @@ enum {
#define VNC_FEATURE_ZLIB_MASK (1 << VNC_FEATURE_ZLIB)
#define VNC_FEATURE_COPYRECT_MASK (1 << VNC_FEATURE_COPYRECT)
#define VNC_FEATURE_RICH_CURSOR_MASK (1 << VNC_FEATURE_RICH_CURSOR)
+#define VNC_FEATURE_TIGHT_PNG_MASK (1 << VNC_FEATURE_TIGHT_PNG)
/* Client -> Server message IDs */
@@ -399,6 +469,10 @@ void buffer_append(Buffer *buffer, const void *data, size_t len);
char *vnc_socket_local_addr(const char *format, int fd);
char *vnc_socket_remote_addr(const char *format, int fd);
+static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
+ return (vs->features & (1 << feature));
+}
+
/* Framebuffer */
void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
int32_t encoding);
@@ -406,6 +480,8 @@ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v);
/* Encodings */
+int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+
int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
int vnc_hextile_send_framebuffer_update(VncState *vs, int x,
@@ -417,8 +493,9 @@ void vnc_zlib_zfree(void *x, void *addr);
int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
void vnc_zlib_clear(VncState *vs);
-
int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h);
void vnc_tight_clear(VncState *vs);
#endif /* __QEMU_VNC_H */
diff --git a/vnc_keysym.h b/ui/vnc_keysym.h
index 55cb87ede..55cb87ede 100644
--- a/vnc_keysym.h
+++ b/ui/vnc_keysym.h
diff --git a/x_keymap.c b/ui/x_keymap.c
index b9b094418..b9b094418 100644
--- a/x_keymap.c
+++ b/ui/x_keymap.c
diff --git a/x_keymap.h b/ui/x_keymap.h
index 2042ce0ed..2042ce0ed 100644
--- a/x_keymap.h
+++ b/ui/x_keymap.h
diff --git a/vl.c b/vl.c
index 3d8298ec9..cbe1097c8 100644
--- a/vl.c
+++ b/vl.c
@@ -1351,7 +1351,7 @@ static void main_loop(void)
int64_t ti;
#endif
#ifndef CONFIG_IOTHREAD
- nonblocking = tcg_cpu_exec();
+ nonblocking = cpu_exec_all();
#endif
#ifdef CONFIG_PROFILER
ti = profile_getclock();
diff --git a/vnc-encoding-tight.c b/vnc-encoding-tight.c
deleted file mode 100644
index faba4834c..000000000
--- a/vnc-encoding-tight.c
+++ /dev/null
@@ -1,959 +0,0 @@
-/*
- * QEMU VNC display driver: tight encoding
- *
- * From libvncserver/libvncserver/tight.c
- * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
- * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
- *
- * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "qdict.h"
-#include "qint.h"
-#include "vnc.h"
-#include "vnc-encoding-tight.h"
-
-/* Compression level stuff. The following array contains various
- encoder parameters for each of 10 compression levels (0..9).
- Last three parameters correspond to JPEG quality levels (0..9). */
-
-static const struct {
- int max_rect_size, max_rect_width;
- int mono_min_rect_size, gradient_min_rect_size;
- int idx_zlib_level, mono_zlib_level, raw_zlib_level, gradient_zlib_level;
- int gradient_threshold, gradient_threshold24;
- int idx_max_colors_divisor;
- int jpeg_quality, jpeg_threshold, jpeg_threshold24;
-} tight_conf[] = {
- { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 },
- { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 },
- { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 },
- { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 },
- { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 },
- { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 },
- { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 },
- { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 },
- { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 },
- { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 }
-};
-
-/*
- * Code to determine how many different colors used in rectangle.
- */
-
-static void tight_palette_rgb2buf(uint32_t rgb, int bpp, uint8_t buf[6])
-{
- memset(buf, 0, 6);
-
- if (bpp == 32) {
- buf[0] = ((rgb >> 24) & 0xFF);
- buf[1] = ((rgb >> 16) & 0xFF);
- buf[2] = ((rgb >> 8) & 0xFF);
- buf[3] = ((rgb >> 0) & 0xFF);
- buf[4] = ((buf[0] & 1) == 0) << 3 | ((buf[1] & 1) == 0) << 2;
- buf[4]|= ((buf[2] & 1) == 0) << 1 | ((buf[3] & 1) == 0) << 0;
- buf[0] |= 1;
- buf[1] |= 1;
- buf[2] |= 1;
- buf[3] |= 1;
- }
- if (bpp == 16) {
- buf[0] = ((rgb >> 8) & 0xFF);
- buf[1] = ((rgb >> 0) & 0xFF);
- buf[2] = ((buf[0] & 1) == 0) << 1 | ((buf[1] & 1) == 0) << 0;
- buf[0] |= 1;
- buf[1] |= 1;
- }
-}
-
-static uint32_t tight_palette_buf2rgb(int bpp, const uint8_t *buf)
-{
- uint32_t rgb = 0;
-
- if (bpp == 32) {
- rgb |= ((buf[0] & ~1) | !((buf[4] >> 3) & 1)) << 24;
- rgb |= ((buf[1] & ~1) | !((buf[4] >> 2) & 1)) << 16;
- rgb |= ((buf[2] & ~1) | !((buf[4] >> 1) & 1)) << 8;
- rgb |= ((buf[3] & ~1) | !((buf[4] >> 0) & 1)) << 0;
- }
- if (bpp == 16) {
- rgb |= ((buf[0] & ~1) | !((buf[2] >> 1) & 1)) << 8;
- rgb |= ((buf[1] & ~1) | !((buf[2] >> 0) & 1)) << 0;
- }
- return rgb;
-}
-
-
-static int tight_palette_insert(QDict *palette, uint32_t rgb, int bpp, int max)
-{
- uint8_t key[6];
- int idx = qdict_size(palette);
- bool present;
-
- tight_palette_rgb2buf(rgb, bpp, key);
- present = qdict_haskey(palette, (char *)key);
- if (idx >= max && !present) {
- return 0;
- }
- if (!present) {
- qdict_put(palette, (char *)key, qint_from_int(idx));
- }
- return qdict_size(palette);
-}
-
-#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \
- \
- static int \
- tight_fill_palette##bpp(VncState *vs, int x, int y, \
- int max, size_t count, \
- uint32_t *bg, uint32_t *fg, \
- struct QDict **palette) { \
- uint##bpp##_t *data; \
- uint##bpp##_t c0, c1, ci; \
- int i, n0, n1; \
- \
- data = (uint##bpp##_t *)vs->tight.buffer; \
- \
- c0 = data[0]; \
- i = 1; \
- while (i < count && data[i] == c0) \
- i++; \
- if (i >= count) { \
- *bg = *fg = c0; \
- return 1; \
- } \
- \
- if (max < 2) { \
- return 0; \
- } \
- \
- n0 = i; \
- c1 = data[i]; \
- n1 = 0; \
- for (i++; i < count; i++) { \
- ci = data[i]; \
- if (ci == c0) { \
- n0++; \
- } else if (ci == c1) { \
- n1++; \
- } else \
- break; \
- } \
- if (i >= count) { \
- if (n0 > n1) { \
- *bg = (uint32_t)c0; \
- *fg = (uint32_t)c1; \
- } else { \
- *bg = (uint32_t)c1; \
- *fg = (uint32_t)c0; \
- } \
- return 2; \
- } \
- \
- if (max == 2) { \
- return 0; \
- } \
- \
- *palette = qdict_new(); \
- tight_palette_insert(*palette, c0, bpp, max); \
- tight_palette_insert(*palette, c1, bpp, max); \
- tight_palette_insert(*palette, ci, bpp, max); \
- \
- for (i++; i < count; i++) { \
- if (data[i] == ci) { \
- continue; \
- } else { \
- if (!tight_palette_insert(*palette, (uint32_t)ci, \
- bpp, max)) { \
- return 0; \
- } \
- ci = data[i]; \
- } \
- } \
- \
- return qdict_size(*palette); \
- }
-
-DEFINE_FILL_PALETTE_FUNCTION(8)
-DEFINE_FILL_PALETTE_FUNCTION(16)
-DEFINE_FILL_PALETTE_FUNCTION(32)
-
-static int tight_fill_palette(VncState *vs, int x, int y,
- size_t count, uint32_t *bg, uint32_t *fg,
- struct QDict **palette)
-{
- int max;
-
- max = count / tight_conf[vs->tight_compression].idx_max_colors_divisor;
- if (max < 2 &&
- count >= tight_conf[vs->tight_compression].mono_min_rect_size) {
- max = 2;
- }
- if (max >= 256) {
- max = 256;
- }
-
- switch(vs->clientds.pf.bytes_per_pixel) {
- case 4:
- return tight_fill_palette32(vs, x, y, max, count, bg, fg, palette);
- case 2:
- return tight_fill_palette16(vs, x, y, max, count, bg, fg, palette);
- default:
- max = 2;
- return tight_fill_palette8(vs, x, y, max, count, bg, fg, palette);
- }
- return 0;
-}
-
-/* Callback to dump a palette with qdict_iter
-static void print_palette(const char *key, QObject *obj, void *opaque)
-{
- uint8_t idx = qint_get_int(qobject_to_qint(obj));
- uint32_t rgb = tight_palette_buf2rgb(32, (uint8_t *)key);
-
- fprintf(stderr, "%.2x ", (unsigned char)*key);
- while (*key++)
- fprintf(stderr, "%.2x ", (unsigned char)*key);
-
- fprintf(stderr, ": idx: %x rgb: %x\n", idx, rgb);
-}
-*/
-
-/*
- * Converting truecolor samples into palette indices.
- */
-#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \
- \
- static void \
- tight_encode_indexed_rect##bpp(uint8_t *buf, int count, \
- struct QDict *palette) { \
- uint##bpp##_t *src; \
- uint##bpp##_t rgb; \
- uint8_t key[6]; \
- int i, rep; \
- uint8_t idx; \
- \
- src = (uint##bpp##_t *) buf; \
- \
- for (i = 0; i < count; i++) { \
- rgb = *src++; \
- rep = 0; \
- while (i < count && *src == rgb) { \
- rep++, src++, i++; \
- } \
- tight_palette_rgb2buf(rgb, bpp, key); \
- if (!qdict_haskey(palette, (char *)key)) { \
- /* \
- * Should never happen, but don't break everything \
- * if it does, use the first color instead \
- */ \
- idx = 0; \
- } else { \
- idx = qdict_get_int(palette, (char *)key); \
- } \
- while (rep >= 0) { \
- *buf++ = idx; \
- rep--; \
- } \
- } \
- }
-
-DEFINE_IDX_ENCODE_FUNCTION(16)
-DEFINE_IDX_ENCODE_FUNCTION(32)
-
-#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \
- \
- static void \
- tight_encode_mono_rect##bpp(uint8_t *buf, int w, int h, \
- uint##bpp##_t bg, uint##bpp##_t fg) { \
- uint##bpp##_t *ptr; \
- unsigned int value, mask; \
- int aligned_width; \
- int x, y, bg_bits; \
- \
- ptr = (uint##bpp##_t *) buf; \
- aligned_width = w - w % 8; \
- \
- for (y = 0; y < h; y++) { \
- for (x = 0; x < aligned_width; x += 8) { \
- for (bg_bits = 0; bg_bits < 8; bg_bits++) { \
- if (*ptr++ != bg) { \
- break; \
- } \
- } \
- if (bg_bits == 8) { \
- *buf++ = 0; \
- continue; \
- } \
- mask = 0x80 >> bg_bits; \
- value = mask; \
- for (bg_bits++; bg_bits < 8; bg_bits++) { \
- mask >>= 1; \
- if (*ptr++ != bg) { \
- value |= mask; \
- } \
- } \
- *buf++ = (uint8_t)value; \
- } \
- \
- mask = 0x80; \
- value = 0; \
- if (x >= w) { \
- continue; \
- } \
- \
- for (; x < w; x++) { \
- if (*ptr++ != bg) { \
- value |= mask; \
- } \
- mask >>= 1; \
- } \
- *buf++ = (uint8_t)value; \
- } \
- }
-
-DEFINE_MONO_ENCODE_FUNCTION(8)
-DEFINE_MONO_ENCODE_FUNCTION(16)
-DEFINE_MONO_ENCODE_FUNCTION(32)
-
-/*
- * Check if a rectangle is all of the same color. If needSameColor is
- * set to non-zero, then also check that its color equals to the
- * *colorPtr value. The result is 1 if the test is successfull, and in
- * that case new color will be stored in *colorPtr.
- */
-
-#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
- \
- static bool \
- check_solid_tile##bpp(VncState *vs, int x, int y, int w, int h, \
- uint32_t* color, bool samecolor) \
- { \
- VncDisplay *vd = vs->vd; \
- uint##bpp##_t *fbptr; \
- uint##bpp##_t c; \
- int dx, dy; \
- \
- fbptr = (uint##bpp##_t *) \
- (vd->server->data + y * ds_get_linesize(vs->ds) + \
- x * ds_get_bytes_per_pixel(vs->ds)); \
- \
- c = *fbptr; \
- if (samecolor && (uint32_t)c != *color) { \
- return false; \
- } \
- \
- for (dy = 0; dy < h; dy++) { \
- for (dx = 0; dx < w; dx++) { \
- if (c != fbptr[dx]) { \
- return false; \
- } \
- } \
- fbptr = (uint##bpp##_t *) \
- ((uint8_t *)fbptr + ds_get_linesize(vs->ds)); \
- } \
- \
- *color = (uint32_t)c; \
- return true; \
- }
-
-DEFINE_CHECK_SOLID_FUNCTION(32)
-DEFINE_CHECK_SOLID_FUNCTION(16)
-DEFINE_CHECK_SOLID_FUNCTION(8)
-
-static bool check_solid_tile(VncState *vs, int x, int y, int w, int h,
- uint32_t* color, bool samecolor)
-{
- VncDisplay *vd = vs->vd;
-
- switch(vd->server->pf.bytes_per_pixel) {
- case 4:
- return check_solid_tile32(vs, x, y, w, h, color, samecolor);
- case 2:
- return check_solid_tile16(vs, x, y, w, h, color, samecolor);
- default:
- return check_solid_tile8(vs, x, y, w, h, color, samecolor);
- }
-}
-
-static void find_best_solid_area(VncState *vs, int x, int y, int w, int h,
- uint32_t color, int *w_ptr, int *h_ptr)
-{
- int dx, dy, dw, dh;
- int w_prev;
- int w_best = 0, h_best = 0;
-
- w_prev = w;
-
- for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
-
- dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, y + h - dy);
- dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, w_prev);
-
- if (!check_solid_tile(vs, x, dy, dw, dh, &color, true)) {
- break;
- }
-
- for (dx = x + dw; dx < x + w_prev;) {
- dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, x + w_prev - dx);
-
- if (!check_solid_tile(vs, dx, dy, dw, dh, &color, true)) {
- break;
- }
- dx += dw;
- }
-
- w_prev = dx - x;
- if (w_prev * (dy + dh - y) > w_best * h_best) {
- w_best = w_prev;
- h_best = dy + dh - y;
- }
- }
-
- *w_ptr = w_best;
- *h_ptr = h_best;
-}
-
-static void extend_solid_area(VncState *vs, int x, int y, int w, int h,
- uint32_t color, int *x_ptr, int *y_ptr,
- int *w_ptr, int *h_ptr)
-{
- int cx, cy;
-
- /* Try to extend the area upwards. */
- for ( cy = *y_ptr - 1;
- cy >= y && check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
- cy-- );
- *h_ptr += *y_ptr - (cy + 1);
- *y_ptr = cy + 1;
-
- /* ... downwards. */
- for ( cy = *y_ptr + *h_ptr;
- cy < y + h &&
- check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
- cy++ );
- *h_ptr += cy - (*y_ptr + *h_ptr);
-
- /* ... to the left. */
- for ( cx = *x_ptr - 1;
- cx >= x && check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
- cx-- );
- *w_ptr += *x_ptr - (cx + 1);
- *x_ptr = cx + 1;
-
- /* ... to the right. */
- for ( cx = *x_ptr + *w_ptr;
- cx < x + w &&
- check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
- cx++ );
- *w_ptr += cx - (*x_ptr + *w_ptr);
-}
-
-static int tight_init_stream(VncState *vs, int stream_id,
- int level, int strategy)
-{
- z_streamp zstream = &vs->tight_stream[stream_id];
-
- if (zstream->opaque == NULL) {
- int err;
-
- VNC_DEBUG("VNC: TIGHT: initializing zlib stream %d\n", stream_id);
- VNC_DEBUG("VNC: TIGHT: opaque = %p | vs = %p\n", zstream->opaque, vs);
- zstream->zalloc = vnc_zlib_zalloc;
- zstream->zfree = vnc_zlib_zfree;
-
- err = deflateInit2(zstream, level, Z_DEFLATED, MAX_WBITS,
- MAX_MEM_LEVEL, strategy);
-
- if (err != Z_OK) {
- fprintf(stderr, "VNC: error initializing zlib\n");
- return -1;
- }
-
- vs->tight_levels[stream_id] = level;
- zstream->opaque = vs;
- }
-
- if (vs->tight_levels[stream_id] != level) {
- if (deflateParams(zstream, level, strategy) != Z_OK) {
- return -1;
- }
- vs->tight_levels[stream_id] = level;
- }
- return 0;
-}
-
-static void tight_send_compact_size(VncState *vs, size_t len)
-{
- int lpc = 0;
- int bytes = 0;
- char buf[3] = {0, 0, 0};
-
- buf[bytes++] = len & 0x7F;
- if (len > 0x7F) {
- buf[bytes-1] |= 0x80;
- buf[bytes++] = (len >> 7) & 0x7F;
- if (len > 0x3FFF) {
- buf[bytes-1] |= 0x80;
- buf[bytes++] = (len >> 14) & 0xFF;
- }
- }
- for (lpc = 0; lpc < bytes; lpc++) {
- vnc_write_u8(vs, buf[lpc]);
- }
-}
-
-static int tight_compress_data(VncState *vs, int stream_id, size_t bytes,
- int level, int strategy)
-{
- z_streamp zstream = &vs->tight_stream[stream_id];
- int previous_out;
-
- if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) {
- vnc_write(vs, vs->tight.buffer, vs->tight.offset);
- return bytes;
- }
-
- if (tight_init_stream(vs, stream_id, level, strategy)) {
- return -1;
- }
-
- /* reserve memory in output buffer */
- buffer_reserve(&vs->tight_zlib, bytes + 64);
-
- /* set pointers */
- zstream->next_in = vs->tight.buffer;
- zstream->avail_in = vs->tight.offset;
- zstream->next_out = vs->tight_zlib.buffer + vs->tight_zlib.offset;
- zstream->avail_out = vs->tight_zlib.capacity - vs->tight_zlib.offset;
- zstream->data_type = Z_BINARY;
- previous_out = zstream->total_out;
-
- /* start encoding */
- if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) {
- fprintf(stderr, "VNC: error during tight compression\n");
- return -1;
- }
-
- vs->tight_zlib.offset = vs->tight_zlib.capacity - zstream->avail_out;
- bytes = zstream->total_out - previous_out;
-
- tight_send_compact_size(vs, bytes);
- vnc_write(vs, vs->tight_zlib.buffer, bytes);
-
- buffer_reset(&vs->tight_zlib);
-
- return bytes;
-}
-
-/*
- * Subencoding implementations.
- */
-static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret)
-{
- uint32_t *buf32;
- uint32_t pix;
- int rshift, gshift, bshift;
-
- buf32 = (uint32_t *)buf;
-
- if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
- (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
- rshift = vs->clientds.pf.rshift;
- gshift = vs->clientds.pf.gshift;
- bshift = vs->clientds.pf.bshift;
- } else {
- rshift = 24 - vs->clientds.pf.rshift;
- gshift = 24 - vs->clientds.pf.gshift;
- bshift = 24 - vs->clientds.pf.bshift;
- }
-
- if (ret) {
- *ret = count * 3;
- }
-
- while (count--) {
- pix = *buf32++;
- *buf++ = (char)(pix >> rshift);
- *buf++ = (char)(pix >> gshift);
- *buf++ = (char)(pix >> bshift);
- }
-}
-
-static int send_full_color_rect(VncState *vs, int w, int h)
-{
- int stream = 0;
- size_t bytes;
-
- vnc_write_u8(vs, stream << 4); /* no flushing, no filter */
-
- if (vs->tight_pixel24) {
- tight_pack24(vs, vs->tight.buffer, w * h, &vs->tight.offset);
- bytes = 3;
- } else {
- bytes = vs->clientds.pf.bytes_per_pixel;
- }
-
- bytes = tight_compress_data(vs, stream, w * h * bytes,
- tight_conf[vs->tight_compression].raw_zlib_level,
- Z_DEFAULT_STRATEGY);
-
- return (bytes >= 0);
-}
-
-static int send_solid_rect(VncState *vs)
-{
- size_t bytes;
-
- vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */
-
- if (vs->tight_pixel24) {
- tight_pack24(vs, vs->tight.buffer, 1, &vs->tight.offset);
- bytes = 3;
- } else {
- bytes = vs->clientds.pf.bytes_per_pixel;
- }
-
- vnc_write(vs, vs->tight.buffer, bytes);
- return 1;
-}
-
-static int send_mono_rect(VncState *vs, int w, int h, uint32_t bg, uint32_t fg)
-{
- size_t bytes;
- int stream = 1;
- int level = tight_conf[vs->tight_compression].mono_zlib_level;
-
- bytes = ((w + 7) / 8) * h;
-
- vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
- vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
- vnc_write_u8(vs, 1);
-
- switch(vs->clientds.pf.bytes_per_pixel) {
- case 4:
- {
- uint32_t buf[2] = {bg, fg};
- size_t ret = sizeof (buf);
-
- if (vs->tight_pixel24) {
- tight_pack24(vs, (unsigned char*)buf, 2, &ret);
- }
- vnc_write(vs, buf, ret);
-
- tight_encode_mono_rect32(vs->tight.buffer, w, h, bg, fg);
- break;
- }
- case 2:
- vnc_write(vs, &bg, 2);
- vnc_write(vs, &fg, 2);
- tight_encode_mono_rect16(vs->tight.buffer, w, h, bg, fg);
- break;
- default:
- vnc_write_u8(vs, bg);
- vnc_write_u8(vs, fg);
- tight_encode_mono_rect8(vs->tight.buffer, w, h, bg, fg);
- break;
- }
- vs->tight.offset = bytes;
-
- bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY);
- return (bytes >= 0);
-}
-
-struct palette_cb_priv {
- VncState *vs;
- uint8_t *header;
-};
-
-static void write_palette(const char *key, QObject *obj, void *opaque)
-{
- struct palette_cb_priv *priv = opaque;
- VncState *vs = priv->vs;
- uint32_t bytes = vs->clientds.pf.bytes_per_pixel;
- uint8_t idx = qint_get_int(qobject_to_qint(obj));
-
- if (bytes == 4) {
- uint32_t color = tight_palette_buf2rgb(32, (uint8_t *)key);
-
- ((uint32_t*)priv->header)[idx] = color;
- } else {
- uint16_t color = tight_palette_buf2rgb(16, (uint8_t *)key);
-
- ((uint16_t*)priv->header)[idx] = color;
- }
-}
-
-static int send_palette_rect(VncState *vs, int w, int h, struct QDict *palette)
-{
- int stream = 2;
- int level = tight_conf[vs->tight_compression].idx_zlib_level;
- int colors;
- size_t bytes;
-
- colors = qdict_size(palette);
-
- vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
- vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
- vnc_write_u8(vs, colors - 1);
-
- switch(vs->clientds.pf.bytes_per_pixel) {
- case 4:
- {
- size_t old_offset, offset;
- uint32_t header[qdict_size(palette)];
- struct palette_cb_priv priv = { vs, (uint8_t *)header };
-
- old_offset = vs->output.offset;
- qdict_iter(palette, write_palette, &priv);
- vnc_write(vs, header, sizeof(header));
-
- if (vs->tight_pixel24) {
- tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset);
- vs->output.offset = old_offset + offset;
- }
-
- tight_encode_indexed_rect32(vs->tight.buffer, w * h, palette);
- break;
- }
- case 2:
- {
- uint16_t header[qdict_size(palette)];
- struct palette_cb_priv priv = { vs, (uint8_t *)header };
-
- qdict_iter(palette, write_palette, &priv);
- vnc_write(vs, header, sizeof(header));
- tight_encode_indexed_rect16(vs->tight.buffer, w * h, palette);
- break;
- }
- default:
- return -1; /* No palette for 8bits colors */
- break;
- }
- bytes = w * h;
- vs->tight.offset = bytes;
-
- bytes = tight_compress_data(vs, stream, bytes,
- level, Z_DEFAULT_STRATEGY);
- return (bytes >= 0);
-}
-
-static void vnc_tight_start(VncState *vs)
-{
- buffer_reset(&vs->tight);
-
- // make the output buffer be the zlib buffer, so we can compress it later
- vs->tight_tmp = vs->output;
- vs->output = vs->tight;
-}
-
-static void vnc_tight_stop(VncState *vs)
-{
- // switch back to normal output/zlib buffers
- vs->tight = vs->output;
- vs->output = vs->tight_tmp;
-}
-
-static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
-{
- struct QDict *palette = NULL;
- uint32_t bg = 0, fg = 0;
- int colors;
- int ret = 0;
-
- vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_TIGHT);
-
- vnc_tight_start(vs);
- vnc_raw_send_framebuffer_update(vs, x, y, w, h);
- vnc_tight_stop(vs);
-
- colors = tight_fill_palette(vs, x, y, w * h, &fg, &bg, &palette);
-
- if (colors == 0) {
- ret = send_full_color_rect(vs, w, h);
- } else if (colors == 1) {
- ret = send_solid_rect(vs);
- } else if (colors == 2) {
- ret = send_mono_rect(vs, w, h, bg, fg);
- } else if (colors <= 256) {
- ret = send_palette_rect(vs, w, h, palette);
- }
- QDECREF(palette);
- return ret;
-}
-
-static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h)
-{
- vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_TIGHT);
-
- vnc_tight_start(vs);
- vnc_raw_send_framebuffer_update(vs, x, y, w, h);
- vnc_tight_stop(vs);
-
- return send_solid_rect(vs);
-}
-
-static int send_rect_simple(VncState *vs, int x, int y, int w, int h)
-{
- int max_size, max_width;
- int max_sub_width, max_sub_height;
- int dx, dy;
- int rw, rh;
- int n = 0;
-
- max_size = tight_conf[vs->tight_compression].max_rect_size;
- max_width = tight_conf[vs->tight_compression].max_rect_width;
-
- if (w > max_width || w * h > max_size) {
- max_sub_width = (w > max_width) ? max_width : w;
- max_sub_height = max_size / max_sub_width;
-
- for (dy = 0; dy < h; dy += max_sub_height) {
- for (dx = 0; dx < w; dx += max_width) {
- rw = MIN(max_sub_width, w - dx);
- rh = MIN(max_sub_height, h - dy);
- n += send_sub_rect(vs, x+dx, y+dy, rw, rh);
- }
- }
- } else {
- n += send_sub_rect(vs, x, y, w, h);
- }
-
- return n;
-}
-
-static int find_large_solid_color_rect(VncState *vs, int x, int y,
- int w, int h, int max_rows)
-{
- int dx, dy, dw, dh;
- int n = 0;
-
- /* Try to find large solid-color areas and send them separately. */
-
- for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
-
- /* If a rectangle becomes too large, send its upper part now. */
-
- if (dy - y >= max_rows) {
- n += send_rect_simple(vs, x, y, w, max_rows);
- y += max_rows;
- h -= max_rows;
- }
-
- dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (y + h - dy));
-
- for (dx = x; dx < x + w; dx += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
- uint32_t color_value;
- int x_best, y_best, w_best, h_best;
-
- dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (x + w - dx));
-
- if (!check_solid_tile(vs, dx, dy, dw, dh, &color_value, false)) {
- continue ;
- }
-
- /* Get dimensions of solid-color area. */
-
- find_best_solid_area(vs, dx, dy, w - (dx - x), h - (dy - y),
- color_value, &w_best, &h_best);
-
- /* Make sure a solid rectangle is large enough
- (or the whole rectangle is of the same color). */
-
- if (w_best * h_best != w * h &&
- w_best * h_best < VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE) {
- continue;
- }
-
- /* Try to extend solid rectangle to maximum size. */
-
- x_best = dx; y_best = dy;
- extend_solid_area(vs, x, y, w, h, color_value,
- &x_best, &y_best, &w_best, &h_best);
-
- /* Send rectangles at top and left to solid-color area. */
-
- if (y_best != y) {
- n += send_rect_simple(vs, x, y, w, y_best-y);
- }
- if (x_best != x) {
- n += vnc_tight_send_framebuffer_update(vs, x, y_best,
- x_best-x, h_best);
- }
-
- /* Send solid-color rectangle. */
- n += send_sub_rect_solid(vs, x_best, y_best, w_best, h_best);
-
- /* Send remaining rectangles (at right and bottom). */
-
- if (x_best + w_best != x + w) {
- n += vnc_tight_send_framebuffer_update(vs, x_best+w_best,
- y_best,
- w-(x_best-x)-w_best,
- h_best);
- }
- if (y_best + h_best != y + h) {
- n += vnc_tight_send_framebuffer_update(vs, x, y_best+h_best,
- w, h-(y_best-y)-h_best);
- }
-
- /* Return after all recursive calls are done. */
- return n;
- }
- }
- return n + send_rect_simple(vs, x, y, w, h);
-}
-
-int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y,
- int w, int h)
-{
- int max_rows;
-
- if (vs->clientds.pf.bytes_per_pixel == 4 && vs->clientds.pf.rmax == 0xFF &&
- vs->clientds.pf.bmax == 0xFF && vs->clientds.pf.gmax == 0xFF) {
- vs->tight_pixel24 = true;
- } else {
- vs->tight_pixel24 = false;
- }
-
- if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE)
- return send_rect_simple(vs, x, y, w, h);
-
- /* Calculate maximum number of rows in one non-solid rectangle. */
-
- max_rows = tight_conf[vs->tight_compression].max_rect_size;
- max_rows /= MIN(tight_conf[vs->tight_compression].max_rect_width, w);
-
- return find_large_solid_color_rect(vs, x, y, w, h, max_rows);
-}
-
-void vnc_tight_clear(VncState *vs)
-{
- int i;
- for (i=0; i<ARRAY_SIZE(vs->tight_stream); i++) {
- if (vs->tight_stream[i].opaque) {
- deflateEnd(&vs->tight_stream[i]);
- }
- }
-
- buffer_free(&vs->tight);
- buffer_free(&vs->tight_zlib);
-}