diff options
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | Makefile.objs | 1 | ||||
| -rw-r--r-- | arch_init.c | 21 | ||||
| -rw-r--r-- | bswap.h | 2 | ||||
| -rw-r--r-- | console.c | 18 | ||||
| -rw-r--r-- | hw/arm_boot.c | 1 | ||||
| -rw-r--r-- | hw/gumstix.c | 4 | ||||
| -rw-r--r-- | hw/ioapic.c | 16 | ||||
| -rw-r--r-- | hw/mainstone.c | 3 | ||||
| -rw-r--r-- | hw/nseries.c | 7 | ||||
| -rw-r--r-- | hw/omap_sx1.c | 5 | ||||
| -rw-r--r-- | hw/palm.c | 4 | ||||
| -rw-r--r-- | hw/spitz.c | 3 | ||||
| -rw-r--r-- | hw/tosa.c | 3 | ||||
| -rw-r--r-- | hw/virtio-serial-bus.c | 3 | ||||
| -rw-r--r-- | target-arm/helper.c | 1 | ||||
| -rw-r--r-- | target-i386/cpuid.c | 14 | ||||
| -rw-r--r-- | target-ppc/helper.c | 2 | ||||
| -rw-r--r-- | target-ppc/op_helper.c | 6 | ||||
| -rw-r--r-- | vnc-encoding-hextile.c | 5 | ||||
| -rw-r--r-- | vnc-encoding-tight.c | 961 | ||||
| -rw-r--r-- | vnc-encoding-tight.h | 176 | ||||
| -rw-r--r-- | vnc-encoding-zlib.c | 40 | ||||
| -rw-r--r-- | vnc.c | 123 | ||||
| -rw-r--r-- | vnc.h | 26 |
25 files changed, 1329 insertions, 120 deletions
diff --git a/Makefile b/Makefile index 7986bf6922..8fa593be4d 100644 --- a/Makefile +++ b/Makefile @@ -124,6 +124,8 @@ vnc-encoding-zlib.o: vnc.h vnc-encoding-hextile.o: vnc.h +vnc-encoding-tight.o: vnc.h vnc-encoding-tight.h + curses.o: curses.c keymaps.h curses_keys.h bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS) @@ -144,6 +146,8 @@ qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(block-obj-y) $(qobj qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $@") +check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o: $(GENERATED_HEADERS) + check-qint: check-qint.o qint.o qemu-malloc.o check-qstring: check-qstring.o qstring.o qemu-malloc.o check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qemu-malloc.o qlist.o diff --git a/Makefile.objs b/Makefile.objs index 1a942e5e74..9796dcbd16 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -105,6 +105,7 @@ 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 diff --git a/arch_init.c b/arch_init.c index cfc03ead53..8e849a885e 100644 --- a/arch_init.c +++ b/arch_init.c @@ -108,7 +108,7 @@ static int ram_save_block(QEMUFile *f) static ram_addr_t current_addr = 0; ram_addr_t saved_addr = current_addr; ram_addr_t addr = 0; - int found = 0; + int bytes_sent = 0; while (addr < last_ram_offset) { if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) { @@ -123,19 +123,20 @@ static int ram_save_block(QEMUFile *f) if (is_dup_page(p, *p)) { qemu_put_be64(f, current_addr | RAM_SAVE_FLAG_COMPRESS); qemu_put_byte(f, *p); + bytes_sent = 1; } else { qemu_put_be64(f, current_addr | RAM_SAVE_FLAG_PAGE); qemu_put_buffer(f, p, TARGET_PAGE_SIZE); + bytes_sent = TARGET_PAGE_SIZE; } - found = 1; break; } addr += TARGET_PAGE_SIZE; current_addr = (saved_addr + addr) % last_ram_offset; } - return found; + return bytes_sent; } static uint64_t bytes_transferred; @@ -206,11 +207,11 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) bwidth = qemu_get_clock_ns(rt_clock); while (!qemu_file_rate_limit(f)) { - int ret; + int bytes_sent; - ret = ram_save_block(f); - bytes_transferred += ret * TARGET_PAGE_SIZE; - if (ret == 0) { /* no more blocks */ + bytes_sent = ram_save_block(f); + bytes_transferred += bytes_sent; + if (bytes_sent == 0) { /* no more blocks */ break; } } @@ -226,9 +227,11 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) /* try transferring iterative blocks of memory */ if (stage == 3) { + int bytes_sent; + /* flush all remaining blocks regardless of rate limiting */ - while (ram_save_block(f) != 0) { - bytes_transferred += TARGET_PAGE_SIZE; + while ((bytes_sent = ram_save_block(f)) != 0) { + bytes_transferred += bytes_sent; } cpu_physical_memory_set_dirty_tracking(0); } diff --git a/bswap.h b/bswap.h index 956f3fa0b7..20caae63ea 100644 --- a/bswap.h +++ b/bswap.h @@ -205,7 +205,7 @@ static inline void cpu_to_be32wu(uint32_t *p, uint32_t v) #ifdef HOST_WORDS_BIGENDIAN #define cpu_to_32wu cpu_to_be32wu -#define leul_to_cpu(v) le ## HOST_LONG_BITS ## _to_cpu(v) +#define leul_to_cpu(v) glue(glue(le,HOST_LONG_BITS),_to_cpu)(v) #else #define cpu_to_32wu cpu_to_le32wu #define leul_to_cpu(v) (v) diff --git a/console.c b/console.c index 7070b1b1ad..698bc10a60 100644 --- a/console.c +++ b/console.c @@ -167,7 +167,7 @@ void vga_hw_update(void) void vga_hw_invalidate(void) { - if (active_console->hw_invalidate) + if (active_console && active_console->hw_invalidate) active_console->hw_invalidate(active_console->hw); } @@ -1621,6 +1621,22 @@ PixelFormat qemu_default_pixelformat(int bpp) pf.depth = bpp == 32 ? 24 : bpp; switch (bpp) { + case 15: + pf.bits_per_pixel = 16; + pf.bytes_per_pixel = 2; + pf.rmask = 0x00007c00; + pf.gmask = 0x000003E0; + pf.bmask = 0x0000001F; + pf.rmax = 31; + pf.gmax = 31; + pf.bmax = 31; + pf.rshift = 10; + pf.gshift = 5; + pf.bshift = 0; + pf.rbits = 5; + pf.gbits = 5; + pf.bbits = 5; + break; case 16: pf.rmask = 0x0000F800; pf.gmask = 0x000007E0; diff --git a/hw/arm_boot.c b/hw/arm_boot.c index df031a5952..620550b5ab 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -187,6 +187,7 @@ static void main_cpu_reset(void *opaque) env->regs[15] = info->entry & 0xfffffffe; env->thumb = info->entry & 1; } else { + env->regs[15] = info->loader_start; if (old_param) { set_kernel_args_old(info, info->initrd_size, info->loader_start); diff --git a/hw/gumstix.c b/hw/gumstix.c index 3fd31f4bfc..b64e04e205 100644 --- a/hw/gumstix.c +++ b/hw/gumstix.c @@ -74,8 +74,6 @@ static void connex_init(ram_addr_t ram_size, exit(1); } - cpu->env->regs[15] = 0x00000000; - /* Interrupt line of NIC is connected to GPIO line 36 */ smc91c111_init(&nd_table[0], 0x04000300, pxa2xx_gpio_in_get(cpu->gpio)[36]); @@ -114,8 +112,6 @@ static void verdex_init(ram_addr_t ram_size, exit(1); } - cpu->env->regs[15] = 0x00000000; - /* Interrupt line of NIC is connected to GPIO line 99 */ smc91c111_init(&nd_table[0], 0x04000300, pxa2xx_gpio_in_get(cpu->gpio)[99]); diff --git a/hw/ioapic.c b/hw/ioapic.c index 7ad8018518..335da6ef3f 100644 --- a/hw/ioapic.c +++ b/hw/ioapic.c @@ -28,6 +28,13 @@ //#define DEBUG_IOAPIC +#ifdef DEBUG_IOAPIC +#define DPRINTF(fmt, ...) \ + do { printf("ioapic: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) +#endif + #define IOAPIC_NUM_PINS 0x18 #define IOAPIC_LVT_MASKED (1<<16) @@ -95,6 +102,7 @@ void ioapic_set_irq(void *opaque, int vector, int level) * to GSI 2. GSI maps to ioapic 1-1. This is not * the cleanest way of doing it but it should work. */ + DPRINTF("%s: %s vec %x\n", __func__, level? "raise" : "lower", vector); if (vector == 0) vector = 2; @@ -149,9 +157,7 @@ static uint32_t ioapic_mem_readl(void *opaque, target_phys_addr_t addr) val = s->ioredtbl[index] & 0xffffffff; } } -#ifdef DEBUG_IOAPIC - printf("I/O APIC read: %08x = %08x\n", s->ioregsel, val); -#endif + DPRINTF("read: %08x = %08x\n", s->ioregsel, val); } return val; } @@ -166,9 +172,7 @@ static void ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t va s->ioregsel = val; return; } else if (addr == 0x10) { -#ifdef DEBUG_IOAPIC - printf("I/O APIC write: %08x = %08x\n", s->ioregsel, val); -#endif + DPRINTF("write: %08x = %08x\n", s->ioregsel, val); switch (s->ioregsel) { case 0x00: s->id = (val >> 24) & 0xff; diff --git a/hw/mainstone.c b/hw/mainstone.c index a4379e3ec6..54bacfb9dc 100644 --- a/hw/mainstone.c +++ b/hw/mainstone.c @@ -89,9 +89,6 @@ static void mainstone_common_init(ram_addr_t ram_size, cpu_register_physical_memory(0, MAINSTONE_ROM, qemu_ram_alloc(MAINSTONE_ROM) | IO_MEM_ROM); - /* Setup initial (reset) machine state */ - cpu->env->regs[15] = mainstone_binfo.loader_start; - #ifdef TARGET_WORDS_BIGENDIAN be = 1; #else diff --git a/hw/nseries.c b/hw/nseries.c index 0273eeea3f..04a028dc27 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -1016,7 +1016,6 @@ static void n8x0_boot_init(void *opaque) n800_dss_init(&s->blizzard); /* CPU setup */ - s->cpu->env->regs[15] = s->cpu->env->boot_info->loader_start; s->cpu->env->GE = 0x5; /* If the machine has a slided keyboard, open it */ @@ -1317,11 +1316,6 @@ static void n8x0_init(ram_addr_t ram_size, const char *boot_device, if (usb_enabled) n8x0_usb_setup(s); - /* Setup initial (reset) machine state */ - - /* Start at the OneNAND bootloader. */ - s->cpu->env->regs[15] = 0; - if (kernel_filename) { /* Or at the linux loader. */ binfo->kernel_filename = kernel_filename; @@ -1330,7 +1324,6 @@ static void n8x0_init(ram_addr_t ram_size, const char *boot_device, arm_load_kernel(s->cpu->env, binfo); qemu_register_reset(n8x0_boot_init, s); - n8x0_boot_init(s); } if (option_rom[0] && (boot_device[0] == 'n' || !kernel_filename)) { diff --git a/hw/omap_sx1.c b/hw/omap_sx1.c index ca0a7d1005..2e9879f0ef 100644 --- a/hw/omap_sx1.c +++ b/hw/omap_sx1.c @@ -195,15 +195,10 @@ static void sx1_init(ram_addr_t ram_size, /* Load the kernel. */ if (kernel_filename) { - /* Start at bootloader. */ - cpu->env->regs[15] = sx1_binfo.loader_start; - sx1_binfo.kernel_filename = kernel_filename; sx1_binfo.kernel_cmdline = kernel_cmdline; sx1_binfo.initrd_filename = initrd_filename; arm_load_kernel(cpu->env, &sx1_binfo); - } else { - cpu->env->regs[15] = 0x00000000; } /* TODO: fix next line */ diff --git a/hw/palm.c b/hw/palm.c index ba7c398a14..8db133d09e 100644 --- a/hw/palm.c +++ b/hw/palm.c @@ -243,7 +243,6 @@ static void palmte_init(ram_addr_t ram_size, rom_size = load_image_targphys(option_rom[0], OMAP_CS0_BASE, flash_size); rom_loaded = 1; - cpu->env->regs[15] = 0x00000000; } if (rom_size < 0) { fprintf(stderr, "%s: error loading '%s'\n", @@ -258,9 +257,6 @@ static void palmte_init(ram_addr_t ram_size, /* Load the kernel. */ if (kernel_filename) { - /* Start at bootloader. */ - cpu->env->regs[15] = palmte_binfo.loader_start; - palmte_binfo.kernel_filename = kernel_filename; palmte_binfo.kernel_cmdline = kernel_cmdline; palmte_binfo.initrd_filename = initrd_filename; diff --git a/hw/spitz.c b/hw/spitz.c index c3b5cd8e4f..4f82e24495 100644 --- a/hw/spitz.c +++ b/hw/spitz.c @@ -993,9 +993,6 @@ static void spitz_common_init(ram_addr_t ram_size, /* A 4.0 GB microdrive is permanently sitting in CF slot 0. */ spitz_microdrive_attach(cpu, 0); - /* Setup initial (reset) machine state */ - cpu->env->regs[15] = spitz_binfo.loader_start; - spitz_binfo.kernel_filename = kernel_filename; spitz_binfo.kernel_cmdline = kernel_cmdline; spitz_binfo.initrd_filename = initrd_filename; diff --git a/hw/tosa.c b/hw/tosa.c index bc6591f066..fbe8d8c693 100644 --- a/hw/tosa.c +++ b/hw/tosa.c @@ -229,9 +229,6 @@ static void tosa_init(ram_addr_t ram_size, tosa_tg_init(cpu); - /* Setup initial (reset) machine state */ - cpu->env->regs[15] = tosa_binfo.loader_start; - tosa_binfo.kernel_filename = kernel_filename; tosa_binfo.kernel_cmdline = kernel_cmdline; tosa_binfo.initrd_filename = initrd_filename; diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index 3ce95e8f1e..7f9d28f896 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -774,7 +774,8 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports) } vser->config.max_nr_ports = max_nr_ports; - vser->ports_map = qemu_mallocz((max_nr_ports + 31) / 32); + vser->ports_map = qemu_mallocz(((max_nr_ports + 31) / 32) + * sizeof(vser->ports_map[0])); /* * Reserve location 0 for a console port for backward compat * (old kernel, new qemu) diff --git a/target-arm/helper.c b/target-arm/helper.c index 99e0394e93..63e5dc7ef6 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -207,7 +207,6 @@ void cpu_reset(CPUARMState *env) #else /* SVC mode with interrupts disabled. */ env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; - env->regs[15] = 0; /* On ARMv7-M the CPSR_I is the value of the PRIMASK register, and is clear at reset. Initial SP and PC are loaded from ROM. */ if (IS_M(env)) { diff --git a/target-i386/cpuid.c b/target-i386/cpuid.c index 56938e2a7c..7a112159ce 100644 --- a/target-i386/cpuid.c +++ b/target-i386/cpuid.c @@ -364,6 +364,20 @@ static x86_def_t builtin_x86_defs[] = { .model_id = "QEMU Virtual CPU version " QEMU_VERSION, }, { + .name = "kvm32", + .level = 5, + .family = 15, + .model = 6, + .stepping = 1, + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_PSE36, + .ext_features = CPUID_EXT_SSE3, + .ext2_features = PPRO_FEATURES & EXT2_FEATURE_MASK, + .ext3_features = 0, + .xlevel = 0x80000008, + .model_id = "Common 32-bit KVM processor" + }, + { .name = "coreduo", .level = 10, .family = 6, diff --git a/target-ppc/helper.c b/target-ppc/helper.c index a5479c4505..d342b09c8e 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -2080,7 +2080,6 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp) srr1 = SPR_SRR1; asrr0 = -1; asrr1 = -1; - msr &= ~((target_ulong)0x783F0000); switch (excp) { case POWERPC_EXCP_NONE: /* Should never happen */ @@ -2594,7 +2593,6 @@ static inline void powerpc_excp(CPUState *env, int excp_model, int excp) #if 0 /* Fix this: not on all targets */ new_msr &= ~((target_ulong)1 << MSR_PMM); #endif - new_msr &= ~((target_ulong)1 << MSR_LE); if (msr_ile) new_msr |= (target_ulong)1 << MSR_LE; else diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c index 8f2ee986bb..3c3aa60bc3 100644 --- a/target-ppc/op_helper.c +++ b/target-ppc/op_helper.c @@ -1646,20 +1646,20 @@ static inline void do_rfi(target_ulong nip, target_ulong msr, void helper_rfi (void) { do_rfi(env->spr[SPR_SRR0], env->spr[SPR_SRR1], - ~((target_ulong)0x0), 1); + ~((target_ulong)0x783F0000), 1); } #if defined(TARGET_PPC64) void helper_rfid (void) { do_rfi(env->spr[SPR_SRR0], env->spr[SPR_SRR1], - ~((target_ulong)0x0), 0); + ~((target_ulong)0x783F0000), 0); } void helper_hrfid (void) { do_rfi(env->spr[SPR_HSRR0], env->spr[SPR_HSRR1], - ~((target_ulong)0x0), 0); + ~((target_ulong)0x783F0000), 0); } #endif #endif diff --git a/vnc-encoding-hextile.c b/vnc-encoding-hextile.c index a01c5e260f..728f25e5d8 100644 --- a/vnc-encoding-hextile.c +++ b/vnc-encoding-hextile.c @@ -62,8 +62,8 @@ static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h) #undef BPP #undef GENERIC -void vnc_hextile_send_framebuffer_update(VncState *vs, int x, - int y, int w, int h) +int vnc_hextile_send_framebuffer_update(VncState *vs, int x, + int y, int w, int h) { int i, j; int has_fg, has_bg; @@ -83,6 +83,7 @@ void vnc_hextile_send_framebuffer_update(VncState *vs, int x, free(last_fg); free(last_bg); + return 1; } void vnc_hextile_set_pixel_conversion(VncState *vs, int generic) diff --git a/vnc-encoding-tight.c b/vnc-encoding-tight.c new file mode 100644 index 0000000000..50be44e4a7 --- /dev/null +++ b/vnc-encoding-tight.c @@ -0,0 +1,961 @@ +/* + * 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 <stdbool.h> + +#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); \ + \ + 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 rep = 0; \ + uint8_t idx; \ + \ + src = (uint##bpp##_t *) buf; \ + \ + count -= 1; \ + while (count--) { \ + rgb = *src++; \ + rep = 0; \ + while (count && *src == rgb) { \ + rep++, src++, count--; \ + } \ + 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); +} diff --git a/vnc-encoding-tight.h b/vnc-encoding-tight.h new file mode 100644 index 0000000000..64d10625fe --- /dev/null +++ b/vnc-encoding-tight.h @@ -0,0 +1,176 @@ +/* + * QEMU VNC display driver: tight encoding + * + * 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_ENCODING_TIGHT_H +#define VNC_ENCODING_TIGHT_H + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Tight Encoding. + * + *-- The first byte of each Tight-encoded rectangle is a "compression control + * byte". Its format is as follows (bit 0 is the least significant one): + * + * bit 0: if 1, then compression stream 0 should be reset; + * bit 1: if 1, then compression stream 1 should be reset; + * bit 2: if 1, then compression stream 2 should be reset; + * 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 0xxx, then the compression type is "basic", + * values greater than 1001 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: + * + * bits 5-4: decimal representation is the index of a particular zlib + * stream which should be used for decompressing the data; + * 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"). + * + *-- 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: + * + * 1..3 bytes: data size (N) in compact representation; + * N bytes: JPEG image. + * + * Data size is compactly represented in one, two or three bytes, according + * to the following scheme: + * + * 0xxxxxxx (for values 0..127) + * 1xxxxxxx 0yyyyyyy (for values 128..16383) + * 1xxxxxxx 1yyyyyyy zzzzzzzz (for values 16384..4194303) + * + * Here each character denotes one bit, xxxxxxx are the least significant 7 + * bits of the value (bits 0-6), yyyyyyy are bits 7-13, and zzzzzzzz are the + * most significant 8 bits (bits 14-21). For example, decimal value 10000 + * should be represented as two bytes: binary 10010000 01001110, or + * hexadecimal 90 4E. + * + *-- If the compression type is "basic" and bit 6 of the compression control + * byte was set to 1, then the next (second) byte specifies "filter id" which + * tells the decoder what filter type was used by the encoder to pre-process + * pixel data before the compression. The "filter id" byte can be one of the + * following: + * + * 0: no filter ("copy" filter); + * 1: "palette" filter; + * 2: "gradient" filter. + * + *-- If bit 6 of the compression control byte is set to 0 (no "filter id" + * byte), or if the filter id is 0, then raw pixel values in the client + * format (see NOTE 1) will be compressed. See below details on the + * compression. + * + *-- The "gradient" filter pre-processes pixel data with a simple algorithm + * which converts each color component to a difference between a "predicted" + * intensity and the actual intensity. Such a technique does not affect + * uncompressed data size, but helps to compress photo-like images better. + * Pseudo-code for converting intensities to differences is the following: + * + * P[i,j] := V[i-1,j] + V[i,j-1] - V[i-1,j-1]; + * if (P[i,j] < 0) then P[i,j] := 0; + * if (P[i,j] > MAX) then P[i,j] := MAX; + * D[i,j] := V[i,j] - P[i,j]; + * + * Here V[i,j] is the intensity of a color component for a pixel at + * coordinates (i,j). MAX is the maximum value of intensity for a color + * component. + * + *-- The "palette" filter converts true-color pixel data to indexed colors + * and a palette which can consist of 2..256 colors. If the number of colors + * is 2, then each pixel is encoded in 1 bit, otherwise 8 bits is used to + * encode one pixel. 1-bit encoding is performed such way that the most + * significant bits correspond to the leftmost pixels, and each raw of pixels + * is aligned to the byte boundary. When "palette" filter is used, the + * palette is sent before the pixel data. The palette begins with an unsigned + * byte which value is the number of colors in the palette minus 1 (i.e. 1 + * means 2 colors, 255 means 256 colors in the palette). Then follows the + * palette itself which consist of pixel values in client pixel format (see + * NOTE 1). + * + *-- The pixel data is compressed using the zlib library. But if the data + * size after applying the filter but before the compression is less then 12, + * then the data is sent as is, uncompressed. Four separate zlib streams + * (0..3) can be used and the decoder should read the actual stream id from + * the compression control byte (see NOTE 2). + * + * If the compression is not used, then the pixel data is sent as is, + * otherwise the data stream looks like this: + * + * 1..3 bytes: data size (N) in compact representation; + * N bytes: zlib-compressed data. + * + * Data size is compactly represented in one, two or three bytes, just like + * in the "jpeg" compression method (see above). + * + *-- NOTE 1. If the color depth is 24, and all three color components are + * 8-bit wide, then one pixel in Tight encoding is always represented by + * three bytes, where the first byte is red component, the second byte is + * green component, and the third byte is blue component of the pixel color + * value. This applies to colors in palettes as well. + * + *-- 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". + * + *-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only + * when bits-per-pixel value is either 16 or 32, not 8. + * + *-- NOTE 4. The width of any Tight-encoded rectangle cannot exceed 2048 + * pixels. If a rectangle is wider, it must be split into several rectangles + * and each one should be encoded separately. + * + */ + +#define VNC_TIGHT_EXPLICIT_FILTER 0x04 +#define VNC_TIGHT_FILL 0x08 +#define VNC_TIGHT_JPEG 0x09 +#define VNC_TIGHT_MAX_SUBENCODING 0x09 + +/* Filters to improve compression efficiency */ +#define VNC_TIGHT_FILTER_COPY 0x00 +#define VNC_TIGHT_FILTER_PALETTE 0x01 +#define VNC_TIGHT_FILTER_GRADIENT 0x02 + +/* Note: The following constant should not be changed. */ +#define VNC_TIGHT_MIN_TO_COMPRESS 12 + +/* The parameters below may be adjusted. */ +#define VNC_TIGHT_MIN_SPLIT_RECT_SIZE 4096 +#define VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE 2048 +#define VNC_TIGHT_MAX_SPLIT_TILE_SIZE 16 + +#endif /* VNC_ENCODING_TIGHT_H */ diff --git a/vnc-encoding-zlib.c b/vnc-encoding-zlib.c index 4a495adf94..a99bc387dc 100644 --- a/vnc-encoding-zlib.c +++ b/vnc-encoding-zlib.c @@ -28,7 +28,7 @@ #define ZALLOC_ALIGNMENT 16 -static void *zalloc(void *x, unsigned items, unsigned size) +void *vnc_zlib_zalloc(void *x, unsigned items, unsigned size) { void *p; @@ -40,7 +40,7 @@ static void *zalloc(void *x, unsigned items, unsigned size) return (p); } -static void zfree(void *x, void *addr) +void vnc_zlib_zfree(void *x, void *addr) { qemu_free(addr); } @@ -54,9 +54,9 @@ static void vnc_zlib_start(VncState *vs) vs->output = vs->zlib; } -static int vnc_zlib_stop(VncState *vs, int stream_id) +static int vnc_zlib_stop(VncState *vs) { - z_streamp zstream = &vs->zlib_stream[stream_id]; + z_streamp zstream = &vs->zlib_stream; int previous_out; // switch back to normal output/zlib buffers @@ -70,10 +70,10 @@ static int vnc_zlib_stop(VncState *vs, int stream_id) if (zstream->opaque != vs) { int err; - VNC_DEBUG("VNC: initializing zlib stream %d\n", stream_id); + VNC_DEBUG("VNC: initializing zlib stream\n"); VNC_DEBUG("VNC: opaque = %p | vs = %p\n", zstream->opaque, vs); - zstream->zalloc = zalloc; - zstream->zfree = zfree; + zstream->zalloc = vnc_zlib_zalloc; + zstream->zfree = vnc_zlib_zfree; err = deflateInit2(zstream, vs->tight_compression, Z_DEFLATED, MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); @@ -83,10 +83,17 @@ static int vnc_zlib_stop(VncState *vs, int stream_id) return -1; } + vs->zlib_level = vs->tight_compression; zstream->opaque = vs; } - // XXX what to do if tight_compression changed in between? + 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; + } // reserve memory in output buffer buffer_reserve(&vs->output, vs->zlib.offset + 64); @@ -109,7 +116,7 @@ static int vnc_zlib_stop(VncState *vs, int stream_id) return zstream->total_out - previous_out; } -void vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) { int old_offset, new_offset, bytes_written; @@ -122,21 +129,24 @@ void vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) // compress the stream vnc_zlib_start(vs); vnc_raw_send_framebuffer_update(vs, x, y, w, h); - bytes_written = vnc_zlib_stop(vs, 0); + bytes_written = vnc_zlib_stop(vs); if (bytes_written == -1) - return; + return 0; // hack in the size new_offset = vs->output.offset; vs->output.offset = old_offset; vnc_write_u32(vs, bytes_written); vs->output.offset = new_offset; + + return 1; } -void vnc_zlib_init(VncState *vs) +void vnc_zlib_clear(VncState *vs) { - int i; - for (i=0; i<(sizeof(vs->zlib_stream) / sizeof(z_stream)); i++) - vs->zlib_stream[i].opaque = NULL; + if (vs->zlib_stream.opaque) { + deflateEnd(&vs->zlib_stream); + } + buffer_free(&vs->zlib); } diff --git a/vnc.c b/vnc.c index 11ae3e5172..30db8b1c35 100644 --- a/vnc.c +++ b/vnc.c @@ -508,15 +508,43 @@ void buffer_reset(Buffer *buffer) buffer->offset = 0; } +void buffer_free(Buffer *buffer) +{ + qemu_free(buffer->buffer); + buffer->offset = 0; + buffer->capacity = 0; + buffer->buffer = NULL; +} + void buffer_append(Buffer *buffer, const void *data, size_t len) { memcpy(buffer->buffer + buffer->offset, data, len); buffer->offset += len; } +static void vnc_desktop_resize(VncState *vs) +{ + DisplayState *ds = vs->ds; + + if (vs->csock == -1 || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) { + return; + } + if (vs->client_width == ds_get_width(ds) && + vs->client_height == ds_get_height(ds)) { + return; + } + vs->client_width = ds_get_width(ds); + vs->client_height = ds_get_height(ds); + 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_flush(vs); +} + static void vnc_dpy_resize(DisplayState *ds) { - int size_changed; VncDisplay *vd = ds->opaque; VncState *vs; @@ -534,23 +562,12 @@ static void vnc_dpy_resize(DisplayState *ds) vd->guest.ds = qemu_mallocz(sizeof(*vd->guest.ds)); if (ds_get_bytes_per_pixel(ds) != vd->guest.ds->pf.bytes_per_pixel) console_color_init(ds); - size_changed = ds_get_width(ds) != vd->guest.ds->width || - ds_get_height(ds) != vd->guest.ds->height; *(vd->guest.ds) = *(ds->surface); memset(vd->guest.dirty, 0xFF, sizeof(vd->guest.dirty)); QTAILQ_FOREACH(vs, &vd->clients, next) { vnc_colordepth(vs); - if (size_changed) { - if (vs->csock != -1 && vnc_has_feature(vs, VNC_FEATURE_RESIZE)) { - 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(ds), ds_get_height(ds), - VNC_ENCODING_DESKTOPRESIZE); - vnc_flush(vs); - } - } + vnc_desktop_resize(vs); if (vs->vd->cursor) { vnc_cursor_define(vs); } @@ -644,7 +661,7 @@ static void vnc_write_pixels_generic(VncState *vs, struct PixelFormat *pf, } } -void vnc_raw_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 i; uint8_t *row; @@ -655,23 +672,30 @@ void vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) vs->write_pixels(vs, &vd->server->pf, row, w * ds_get_bytes_per_pixel(vs->ds)); row += ds_get_linesize(vs->ds); } + return 1; } -static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h) { + int n = 0; + switch(vs->vnc_encoding) { case VNC_ENCODING_ZLIB: - vnc_hextile_send_framebuffer_update(vs, x, y, w, h); + n = vnc_zlib_send_framebuffer_update(vs, x, y, w, h); break; case VNC_ENCODING_HEXTILE: vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_HEXTILE); - vnc_hextile_send_framebuffer_update(vs, x, y, w, h); + n = vnc_hextile_send_framebuffer_update(vs, x, y, w, h); + break; + case VNC_ENCODING_TIGHT: + n = vnc_tight_send_framebuffer_update(vs, x, y, w, h); break; default: vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW); - vnc_raw_send_framebuffer_update(vs, x, y, w, h); + n = vnc_raw_send_framebuffer_update(vs, x, y, w, h); break; } + return n; } static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h) @@ -826,6 +850,8 @@ static int vnc_update_client(VncState *vs, int has_dirty) int y; int n_rectangles; int saved_offset; + int width, height; + int n; if (vs->output.offset && !vs->audio_cap && !vs->force_update) /* kernel send buffers are full -> drop frames to throttle */ @@ -846,10 +872,13 @@ static int vnc_update_client(VncState *vs, int has_dirty) saved_offset = vs->output.offset; vnc_write_u16(vs, 0); - for (y = 0; y < vd->server->height; y++) { + width = MIN(vd->server->width, vs->client_width); + height = MIN(vd->server->height, vs->client_height); + + for (y = 0; y < height; y++) { int x; int last_x = -1; - for (x = 0; x < vd->server->width / 16; x++) { + for (x = 0; x < width / 16; x++) { if (vnc_get_bit(vs->dirty[y], x)) { if (last_x == -1) { last_x = x; @@ -858,16 +887,18 @@ 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); - send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h); - n_rectangles++; + n = send_framebuffer_update(vs, last_x * 16, y, + (x - last_x) * 16, h); + n_rectangles += n; } last_x = -1; } } if (last_x != -1) { int h = find_and_clear_dirty_height(vs, y, last_x, x); - send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h); - n_rectangles++; + n = send_framebuffer_update(vs, last_x * 16, y, + (x - last_x) * 16, h); + n_rectangles += n; } } vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF; @@ -961,17 +992,14 @@ static void vnc_disconnect_finish(VncState *vs) { vnc_qmp_event(vs, QEVENT_VNC_DISCONNECTED); - if (vs->input.buffer) { - qemu_free(vs->input.buffer); - vs->input.buffer = NULL; - } - if (vs->output.buffer) { - qemu_free(vs->output.buffer); - vs->output.buffer = NULL; - } + buffer_free(&vs->input); + buffer_free(&vs->output); qobject_decref(vs->info); + vnc_zlib_clear(vs); + vnc_tight_clear(vs); + #ifdef CONFIG_VNC_TLS vnc_tls_client_cleanup(vs); #endif /* CONFIG_VNC_TLS */ @@ -1642,35 +1670,37 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) int i; unsigned int enc = 0; - vnc_zlib_init(vs); vs->features = 0; - vs->vnc_encoding = -1; + vs->vnc_encoding = 0; vs->tight_compression = 9; vs->tight_quality = 9; vs->absolute = -1; + /* + * Start from the end because the encodings are sent in order of preference. + * This way the prefered encoding (first encoding defined in the array) + * will be set at the end of the loop. + */ for (i = n_encodings - 1; i >= 0; i--) { enc = encodings[i]; switch (enc) { case VNC_ENCODING_RAW: - if (vs->vnc_encoding != -1) { - vs->vnc_encoding = enc; - } + vs->vnc_encoding = enc; break; case VNC_ENCODING_COPYRECT: vs->features |= VNC_FEATURE_COPYRECT_MASK; break; case VNC_ENCODING_HEXTILE: vs->features |= VNC_FEATURE_HEXTILE_MASK; - if (vs->vnc_encoding != -1) { - vs->vnc_encoding = enc; - } + vs->vnc_encoding = enc; + break; + case VNC_ENCODING_TIGHT: + vs->features |= VNC_FEATURE_TIGHT_MASK; + vs->vnc_encoding = enc; break; case VNC_ENCODING_ZLIB: vs->features |= VNC_FEATURE_ZLIB_MASK; - if (vs->vnc_encoding != -1) { - vs->vnc_encoding = enc; - } + vs->vnc_encoding = enc; break; case VNC_ENCODING_DESKTOPRESIZE: vs->features |= VNC_FEATURE_RESIZE_MASK; @@ -1701,6 +1731,7 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) break; } } + vnc_desktop_resize(vs); check_pointer_type_change(&vs->mouse_mode_notifier); } @@ -1949,8 +1980,10 @@ static int protocol_client_init(VncState *vs, uint8_t *data, size_t len) char buf[1024]; int size; - vnc_write_u16(vs, ds_get_width(vs->ds)); - vnc_write_u16(vs, ds_get_height(vs->ds)); + vs->client_width = ds_get_width(vs->ds); + vs->client_height = ds_get_height(vs->ds); + vnc_write_u16(vs, vs->client_width); + vnc_write_u16(vs, vs->client_height); pixel_format_message(vs); diff --git a/vnc.h b/vnc.h index 0d39897115..7b64cf730b 100644 --- a/vnc.h +++ b/vnc.h @@ -134,6 +134,8 @@ struct VncState int absolute; int last_x; int last_y; + int client_width; + int client_height; uint32_t vnc_encoding; @@ -170,6 +172,12 @@ struct VncState /* 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]; /* Hextile */ VncSendHextileTile *send_hextile_tile; @@ -177,7 +185,8 @@ struct VncState /* Zlib */ Buffer zlib; Buffer zlib_tmp; - z_stream zlib_stream[4]; + z_stream zlib_stream; + int zlib_level; Notifier mouse_mode_notifier; @@ -381,6 +390,7 @@ void buffer_reserve(Buffer *buffer, size_t len); int buffer_empty(Buffer *buffer); uint8_t *buffer_end(Buffer *buffer); void buffer_reset(Buffer *buffer); +void buffer_free(Buffer *buffer); void buffer_append(Buffer *buffer, const void *data, size_t len); @@ -396,13 +406,19 @@ 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 */ -void vnc_raw_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); -void vnc_hextile_send_framebuffer_update(VncState *vs, int x, +int vnc_hextile_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); void vnc_hextile_set_pixel_conversion(VncState *vs, int generic); -void vnc_zlib_init(VncState *vs); -void vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); +void *vnc_zlib_zalloc(void *x, unsigned items, unsigned size); +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); +void vnc_tight_clear(VncState *vs); #endif /* __QEMU_VNC_H */ |