Merge branches 'format-security' and 'module-ldnow-master' of https://github.com/twaugh/tigervnc
diff --git a/tests/encperf.cxx b/tests/encperf.cxx
index 60905c8..2628b46 100644
--- a/tests/encperf.cxx
+++ b/tests/encperf.cxx
@@ -1,15 +1,16 @@
 /* Copyright 2015 Pierre Ossman <ossman@cendio.se> for Cendio AB
- * 
+ * Copyright (C) 2015 D. R. Commander.  All Rights Reserved.
+ *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
- * 
+ *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with this software; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
@@ -48,9 +49,14 @@
 
 static rfb::IntParameter width("width", "Frame buffer width", 0);
 static rfb::IntParameter height("height", "Frame buffer height", 0);
+static rfb::IntParameter count("count", "Number of benchmark iterations", 9);
 
 static rfb::StringParameter format("format", "Pixel format (e.g. bgr888)", "");
 
+static rfb::BoolParameter translate("translate",
+                                    "Translate 8-bit and 16-bit datasets into 24-bit",
+                                    true);
+
 // The frame buffer (and output) is always this format
 static const rfb::PixelFormat fbPF(32, 24, false, true, 255, 255, 255, 0, 8, 16);
 
@@ -58,7 +64,8 @@
 static const rdr::S32 encodings[] = {
   rfb::encodingTight, rfb::encodingCopyRect, rfb::encodingRRE,
   rfb::encodingHextile, rfb::encodingZRLE, rfb::pseudoEncodingLastRect,
-  rfb::pseudoEncodingQualityLevel0 + 8 };
+  rfb::pseudoEncodingQualityLevel0 + 8,
+  rfb::pseudoEncodingCompressLevel0 + 2};
 
 class DummyOutStream : public rdr::OutStream {
 public:
@@ -79,7 +86,8 @@
   CConn(const char *filename);
   ~CConn();
 
-  double getRatio();
+  void getStats(double& ratio, unsigned long long& bytes,
+                unsigned long long& rawEquivalent);
 
   virtual void setDesktopSize(int w, int h);
   virtual void setCursor(int, int, const rfb::Point&, void*, void*);
@@ -96,7 +104,7 @@
 
 protected:
   rdr::FileInStream *in;
-  rfb::Decoder *decoders[rfb::encodingMax+1];
+  rfb::Decoder *decoders[rfb::encodingMax + 1];
   rfb::ManagedPixelBuffer pb;
   rfb::SimpleUpdateTracker updates;
   class SConn *sc;
@@ -106,7 +114,7 @@
 public:
   Manager(class rfb::SConnection *conn);
 
-  double getRatio();
+  void getStats(double&, unsigned long long&, unsigned long long&);
 };
 
 class SConn : public rfb::SConnection {
@@ -116,7 +124,7 @@
 
   void writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb);
 
-  double getRatio();
+  void getStats(double&, unsigned long long&, unsigned long long&);
 
   virtual void setAccessRights(AccessRights ar);
 
@@ -163,15 +171,13 @@
   setStreams(in, NULL);
 
   memset(decoders, 0, sizeof(decoders));
-  for (i = 0;i < rfb::encodingMax;i++) {
+  for (i = 0; i < rfb::encodingMax; i++) {
     if (!rfb::Decoder::supported(i))
       continue;
 
     decoders[i] = rfb::Decoder::createDecoder(i, this);
   }
 
-  pb.setPF(fbPF);
-
   // Need to skip the initial handshake and ServerInit
   setState(RFBSTATE_NORMAL);
   // That also means that the reader and writer weren't setup
@@ -182,9 +188,11 @@
   pf.parse(format);
   setPixelFormat(pf);
 
+  pb.setPF((bool)translate ? fbPF : pf);
+
   sc = new SConn();
   sc->cp.setPF(pb.getPF());
-  sc->setEncodings(sizeof(encodings)/sizeof(*encodings), encodings);
+  sc->setEncodings(sizeof(encodings) / sizeof(*encodings), encodings);
 }
 
 CConn::~CConn()
@@ -195,13 +203,14 @@
 
   delete in;
 
-  for (i = 0;i < rfb::encodingMax;i++)
+  for (i = 0; i < rfb::encodingMax; i++)
     delete decoders[i];
 }
 
-double CConn::getRatio()
+void CConn::getStats(double& ratio, unsigned long long& bytes,
+                     unsigned long long& rawEquivalent)
 {
-  return sc->getRatio();
+  sc->getStats(ratio, bytes, rawEquivalent);
 }
 
 void CConn::setDesktopSize(int w, int h)
@@ -266,21 +275,24 @@
 {
 }
 
-double Manager::getRatio()
+void Manager::getStats(double& ratio, unsigned long long& encodedBytes,
+                       unsigned long long& rawEquivalent)
 {
   StatsVector::iterator iter;
   unsigned long long bytes, equivalent;
 
   bytes = equivalent = 0;
-  for (iter = stats.begin();iter != stats.end();++iter) {
+  for (iter = stats.begin(); iter != stats.end(); ++iter) {
     StatsVector::value_type::iterator iter2;
-    for (iter2 = iter->begin();iter2 != iter->end();++iter2) {
+    for (iter2 = iter->begin(); iter2 != iter->end(); ++iter2) {
       bytes += iter2->bytes;
       equivalent += iter2->equivalent;
     }
   }
 
-  return (double)equivalent / bytes;
+  ratio = (double)equivalent / bytes;
+  encodedBytes = bytes;
+  rawEquivalent = equivalent;
 }
 
 SConn::SConn()
@@ -304,9 +316,10 @@
   manager->writeUpdate(ui, pb, NULL);
 }
 
-double SConn::getRatio()
+void SConn::getStats(double& ratio, unsigned long long& bytes,
+                     unsigned long long& rawEquivalent)
 {
-  return manager->getRatio();
+  manager->getStats(ratio, bytes, rawEquivalent);
 }
 
 void SConn::setAccessRights(AccessRights ar)
@@ -318,7 +331,8 @@
 {
 }
 
-static double runTest(const char *fn, double *ratio)
+static double runTest(const char *fn, double& ratio, unsigned long long& bytes,
+                      unsigned long long& rawEquivalent)
 {
   CConn *cc;
   double time;
@@ -335,7 +349,7 @@
   }
 
   time = cc->encodeTime;
-  *ratio = cc->getRatio();
+  cc->getStats(ratio, bytes, rawEquivalent);
 
   delete cc;
 
@@ -348,20 +362,18 @@
   int i;
   do {
     sorted = true;
-    for (i = 1;i < count;i++) {
+    for (i = 1; i < count; i++) {
       if (array[i-1] > array[i]) {
         double d;
         d = array[i];
-        array[i] = array[i-1];
-        array[i-1] = d;
+        array[i] = array[i - 1];
+        array[i - 1] = d;
         sorted = false;
       }
     }
   } while (!sorted);
 }
 
-static const int runCount = 9;
-
 static void usage(const char *argv0)
 {
   fprintf(stderr, "Syntax: %s [options] <rfb file>\n", argv0);
@@ -376,17 +388,14 @@
 
   const char *fn;
 
-  double times[runCount], dev[runCount];
-  double median, meddev, ratio;
-
   fn = NULL;
   for (i = 1; i < argc; i++) {
     if (rfb::Configuration::setParam(argv[i]))
       continue;
 
     if (argv[i][0] == '-') {
-      if (i+1 < argc) {
-        if (rfb::Configuration::setParam(&argv[i][1], argv[i+1])) {
+      if (i + 1 < argc) {
+        if (rfb::Configuration::setParam(&argv[i][1], argv[i + 1])) {
           i++;
           continue;
         }
@@ -400,6 +409,11 @@
     fn = argv[i];
   }
 
+  int runCount = count;
+  double times[runCount], dev[runCount];
+  double median, meddev, ratio;
+  unsigned long long bytes, equivalent;
+
   if (fn == NULL) {
     fprintf(stderr, "No file specified!\n\n");
     usage(argv[0]);
@@ -416,23 +430,25 @@
   }
 
   // Warmup
-  runTest(fn, &ratio);
+  runTest(fn, ratio, bytes, equivalent);
 
   // Multiple runs to get a good average
-  for (i = 0;i < runCount;i++)
-    times[i] = runTest(fn, &ratio);
+  for (i = 0; i < runCount; i++)
+    times[i] = runTest(fn, ratio, bytes, equivalent);
 
   // Calculate median and median deviation
   sort(times, runCount);
-  median = times[runCount/2];
+  median = times[runCount / 2];
 
-  for (i = 0;i < runCount;i++)
+  for (i = 0; i < runCount; i++)
     dev[i] = fabs((times[i] - median) / median) * 100;
 
   sort(dev, runCount);
-  meddev = dev[runCount/2];
+  meddev = dev[runCount / 2];
 
-  printf("CPU time: %g s (+/- %g %)\n", median, meddev);
+  printf("CPU time: %g s (+/- %g %%)\n", median, meddev);
+  printf("Encoded bytes: %lld\n", bytes);
+  printf("Raw equivalent bytes: %lld\n", equivalent);
   printf("Ratio: %g\n", ratio);
 
   return 0;
diff --git a/unix/xserver/hw/vnc/Makefile.am b/unix/xserver/hw/vnc/Makefile.am
index ab8becb..7940f1b 100644
--- a/unix/xserver/hw/vnc/Makefile.am
+++ b/unix/xserver/hw/vnc/Makefile.am
@@ -62,7 +62,7 @@
 	-I$(top_srcdir)/include \
 	${XSERVERLIBS_CFLAGS} -I$(includedir)
 
-libvnc_la_LDFLAGS = -module -avoid-version
+libvnc_la_LDFLAGS = -module -avoid-version -Wl,-z,now
 
 libvnc_la_LIBADD = libvnccommon.la $(COMMON_LIBS)
 
diff --git a/unix/xserver/hw/vnc/xorg-version.h b/unix/xserver/hw/vnc/xorg-version.h
index 057c31e..083c1b1 100644
--- a/unix/xserver/hw/vnc/xorg-version.h
+++ b/unix/xserver/hw/vnc/xorg-version.h
@@ -48,8 +48,10 @@
 #define XORG 115
 #elif XORG_VERSION_CURRENT < ((1 * 10000000) + (16 * 100000) + (99 * 1000))
 #define XORG 116
+#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (17 * 100000) + (99 * 1000))
+#define XORG 117
 #else
-#error "X.Org newer than 1.16 is not supported"
+#error "X.Org newer than 1.17 is not supported"
 #endif
 
 #endif
diff --git a/unix/xserver/hw/vnc/xvnc.c b/unix/xserver/hw/vnc/xvnc.c
index 04dd0b9..cc87efb 100644
--- a/unix/xserver/hw/vnc/xvnc.c
+++ b/unix/xserver/hw/vnc/xvnc.c
@@ -87,11 +87,6 @@
 #endif
 #include "site.h"
 
-#if XORG >= 110
-#define Xalloc malloc
-#define Xfree free
-#endif
-
 #define XVNCVERSION "TigerVNC 1.4.80"
 #define XVNCCOPYRIGHT ("Copyright (C) 1999-2015 TigerVNC Team and many others (see README.txt)\n" \
                        "See http://www.tigervnc.org for information on TigerVNC.\n")
@@ -346,6 +341,13 @@
     return TRUE;
 }
 
+#define fail_unless_args(_argc,_i,_n)		\
+    if (_i + _n >= _argc)			\
+    {						\
+        UseMsg();				\
+        return 0;				\
+    }
+
 int 
 ddxProcessArgument(int argc, char *argv[], int i)
 {
@@ -365,12 +367,13 @@
     if (strcmp (argv[i], "-screen") == 0)	/* -screen n WxHxD */
     {
 	int screenNum;
-	if (i + 2 >= argc) UseMsg();
+	fail_unless_args(argc, i, 2);
 	screenNum = atoi(argv[i+1]);
 	if (screenNum < 0 || screenNum >= MAXSCREENS)
 	{
 	    ErrorF("Invalid screen number %d\n", screenNum);
 	    UseMsg();
+	    return 0;
 	}
 	if (3 != sscanf(argv[i+2], "%dx%dx%d",
 			&vfbScreens[screenNum].fb.width,
@@ -379,6 +382,7 @@
 	{
 	    ErrorF("Invalid screen configuration %s\n", argv[i+2]);
 	    UseMsg();
+	    return 0;
 	}
 
 	if (screenNum >= vfbNumScreens)
@@ -391,13 +395,15 @@
     {
 	int depth, ret = 1;
 
-	if (++i >= argc) UseMsg();
+	fail_unless_args(argc, i, 1);
+	++i;
 	while ((i < argc) && (depth = atoi(argv[i++])) != 0)
 	{
 	    if (depth < 0 || depth > 32)
 	    {
 		ErrorF("Invalid pixmap depth %d\n", depth);
 		UseMsg();
+		return 0;
 	    }
 	    vfbPixmapDepths[depth] = TRUE;
 	    ret++;
@@ -420,7 +426,8 @@
     if (strcmp (argv[i], "-blackpixel") == 0)	/* -blackpixel n */
     {
 	Pixel pix;
-	if (++i >= argc) UseMsg();
+	fail_unless_args(argc, i, 1);
+	++i;
 	pix = atoi(argv[i]);
 	if (-1 == lastScreen)
 	{
@@ -440,7 +447,8 @@
     if (strcmp (argv[i], "-whitepixel") == 0)	/* -whitepixel n */
     {
 	Pixel pix;
-	if (++i >= argc) UseMsg();
+	fail_unless_args(argc, i, 1);
+	++i;
 	pix = atoi(argv[i]);
 	if (-1 == lastScreen)
 	{
@@ -460,7 +468,8 @@
     if (strcmp (argv[i], "-linebias") == 0)	/* -linebias n */
     {
 	unsigned int linebias;
-	if (++i >= argc) UseMsg();
+	fail_unless_args(argc, i, 1);
+	++i;
 	linebias = atoi(argv[i]);
 	if (-1 == lastScreen)
 	{
@@ -487,18 +496,21 @@
     
     if (strcmp(argv[i], "-geometry") == 0)
     {
-	if (++i >= argc) UseMsg();
+	fail_unless_args(argc, i, 1);
+	++i;
 	if (sscanf(argv[i],"%dx%d",&vfbScreens[0].fb.width,
 		   &vfbScreens[0].fb.height) != 2) {
 	    ErrorF("Invalid geometry %s\n", argv[i]);
 	    UseMsg();
+	    return 0;
 	}
 	return 2;
     }
     
     if (strcmp(argv[i], "-depth") == 0)
     {
-	if (++i >= argc) UseMsg();
+	fail_unless_args(argc, i, 1);
+	++i;
 	vfbScreens[0].fb.depth = atoi(argv[i]);
 	return 2;
     }
@@ -507,10 +519,12 @@
     {
 	char rgbbgr[4];
 	int bits1, bits2, bits3;
-	if (++i >= argc) UseMsg();
+	fail_unless_args(argc, i, 1);
+	++i;
 	if (sscanf(argv[i], "%3s%1d%1d%1d", rgbbgr,&bits1,&bits2,&bits3) < 4) {
 	    ErrorF("Invalid pixel format %s\n", argv[i]);
 	    UseMsg();
+	    return 0;
 	}
 
 #define SET_PIXEL_FORMAT(vfbScreen)                     \
@@ -528,6 +542,7 @@
     } else {                                            \
         ErrorF("Invalid pixel format %s\n", argv[i]);   \
         UseMsg();                                       \
+        return 0;					\
     }
 
 	if (-1 == lastScreen)
@@ -679,9 +694,11 @@
 	entries = pmap->pVisual->ColormapEntries;
 	pVisual = pmap->pVisual;
 
-	ppix = (Pixel *)xalloc(entries * sizeof(Pixel));
-	prgb = (xrgb *)xalloc(entries * sizeof(xrgb));
-	defs = (xColorItem *)xalloc(entries * sizeof(xColorItem));
+	ppix = (Pixel *)calloc(entries, sizeof(Pixel));
+	prgb = (xrgb *)calloc(entries, sizeof(xrgb));
+	defs = (xColorItem *)calloc(entries, sizeof(xColorItem));
+	if (!ppix || !prgb || !defs)
+	  FatalError ("Not enough memory for color map\n");
 
 	for (i = 0; i < entries; i++)  ppix[i] = i;
 	/* XXX truecolor */
@@ -700,9 +717,9 @@
 	}
 	(*pmap->pScreen->StoreColors)(pmap, entries, defs);
 	
-	xfree(ppix);
-	xfree(prgb);
-	xfree(defs);
+	free(ppix);
+	free(prgb);
+	free(defs);
     }
 }
 
@@ -786,7 +803,7 @@
         break;
 #endif
     case NORMAL_MEMORY_FB:
-        pfb->pfbMemory = Xalloc(pfb->sizeInBytes);
+        pfb->pfbMemory = malloc(pfb->sizeInBytes);
         break;
     }
 
@@ -813,7 +830,7 @@
         break;
 #endif /* HAS_SHM */
     case NORMAL_MEMORY_FB:
-        Xfree(pfb->pfbMemory);
+        free(pfb->pfbMemory);
         break;
     }
 
diff --git a/unix/xserver117.patch b/unix/xserver117.patch
new file mode 100644
index 0000000..8a21040
--- /dev/null
+++ b/unix/xserver117.patch
@@ -0,0 +1,137 @@
+diff -up xorg-server-1.17.1/configure.ac.vnc xorg-server-1.17.1/configure.ac
+--- xorg-server-1.17.1/configure.ac.vnc	2015-02-10 22:43:52.000000000 +0000
++++ xorg-server-1.17.1/configure.ac	2015-02-13 16:14:05.074515927 +0000
+@@ -74,6 +74,7 @@ dnl forcing an entire recompile.x
+ AC_CONFIG_HEADERS(include/version-config.h)
+ 
+ AM_PROG_AS
++AC_PROG_CXX
+ AC_PROG_LN_S
+ LT_PREREQ([2.2])
+ LT_INIT([disable-static win32-dll])
+@@ -1795,6 +1796,10 @@ if test "x$XVFB" = xyes; then
+ 	AC_SUBST([XVFB_SYS_LIBS])
+ fi
+ 
++dnl Xvnc DDX
++AC_SUBST([XVNC_CPPFLAGS], ["-DHAVE_DIX_CONFIG_H $XSERVER_CFLAGS"])
++AC_SUBST([XVNC_LIBS], ["$FB_LIB $FIXES_LIB $XEXT_LIB $CONFIG_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $PRESENT_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB $MAIN_LIB"])
++AC_SUBST([XVNC_SYS_LIBS], ["$GLX_SYS_LIBS"])
+ 
+ dnl Xnest DDX
+ 
+@@ -1830,6 +1835,8 @@ if test "x$XORG" = xauto; then
+ fi
+ AC_MSG_RESULT([$XORG])
+ 
++AC_DEFINE_UNQUOTED(XORG_VERSION_CURRENT, [$VENDOR_RELEASE], [Current Xorg version])
++
+ if test "x$XORG" = xyes; then
+ 	XORG_DDXINCS='-I$(top_srcdir)/hw/xfree86 -I$(top_srcdir)/hw/xfree86/include -I$(top_srcdir)/hw/xfree86/common'
+ 	XORG_OSINCS='-I$(top_srcdir)/hw/xfree86/os-support -I$(top_srcdir)/hw/xfree86/os-support/bus -I$(top_srcdir)/os'
+@@ -2059,7 +2066,6 @@ if test "x$XORG" = xyes; then
+ 	AC_DEFINE(XORG_SERVER, 1, [Building Xorg server])
+ 	AC_DEFINE(XORGSERVER, 1, [Building Xorg server])
+ 	AC_DEFINE(XFree86Server, 1, [Building XFree86 server])
+-	AC_DEFINE_UNQUOTED(XORG_VERSION_CURRENT, [$VENDOR_RELEASE], [Current Xorg version])
+ 	AC_DEFINE(NEED_XF86_TYPES, 1, [Need XFree86 typedefs])
+ 	AC_DEFINE(NEED_XF86_PROTOTYPES, 1, [Need XFree86 helper functions])
+ 	AC_DEFINE(__XSERVERNAME__, "Xorg", [Name of X server])
+@@ -2599,6 +2605,7 @@ hw/dmx/Makefile
+ hw/dmx/man/Makefile
+ hw/vfb/Makefile
+ hw/vfb/man/Makefile
++hw/vnc/Makefile
+ hw/xnest/Makefile
+ hw/xnest/man/Makefile
+ hw/xwin/Makefile
+diff -up xorg-server-1.17.1/hw/Makefile.am.vnc xorg-server-1.17.1/hw/Makefile.am
+--- xorg-server-1.17.1/hw/Makefile.am.vnc	2014-04-16 21:24:00.000000000 +0100
++++ xorg-server-1.17.1/hw/Makefile.am	2015-02-13 16:14:05.131516821 +0000
+@@ -38,7 +38,8 @@ SUBDIRS =			\
+ 	$(DMX_SUBDIRS)		\
+ 	$(KDRIVE_SUBDIRS)	\
+ 	$(XQUARTZ_SUBDIRS)	\
+-	$(XWAYLAND_SUBDIRS)
++	$(XWAYLAND_SUBDIRS)	\
++	vnc
+ 
+ DIST_SUBDIRS = dmx xfree86 vfb xnest xwin xquartz kdrive xwayland
+ 
+diff -up xorg-server-1.17.1/mi/miinitext.c.vnc xorg-server-1.17.1/mi/miinitext.c
+--- xorg-server-1.17.1/mi/miinitext.c.vnc	2015-01-17 23:42:52.000000000 +0000
++++ xorg-server-1.17.1/mi/miinitext.c	2015-02-13 16:14:05.131516821 +0000
+@@ -111,6 +111,10 @@ SOFTWARE.
+ #include "micmap.h"
+ #include "globals.h"
+ 
++#ifdef TIGERVNC
++extern void vncExtensionInit(INITARGS);
++#endif
++
+ /* The following is only a small first step towards run-time
+  * configurable extensions.
+  */
+@@ -235,6 +239,9 @@ EnableDisableExtensionError(const char *
+ 
+ /* List of built-in (statically linked) extensions */
+ static const ExtensionModule staticExtensions[] = {
++#ifdef TIGERVNC
++    {vncExtensionInit, "VNC-EXTENSION", NULL},
++#endif
+     {GEExtensionInit, "Generic Event Extension", &noGEExtension},
+     {ShapeExtensionInit, "SHAPE", NULL},
+ #ifdef MITSHM
+diff -up xorg-server-1.17.1/os/WaitFor.c.vnc xorg-server-1.17.1/os/WaitFor.c
+--- xorg-server-1.17.1/os/WaitFor.c.vnc	2015-01-26 18:40:30.000000000 +0000
++++ xorg-server-1.17.1/os/WaitFor.c	2015-02-13 16:14:05.132516837 +0000
+@@ -125,6 +125,9 @@ static void DoTimer(OsTimerPtr timer, CA
+ static void CheckAllTimers(void);
+ static volatile OsTimerPtr timers = NULL;
+ 
++extern void vncWriteBlockHandler(fd_set *fds);
++extern void vncWriteWakeupHandler(int nfds, fd_set *fds);
++
+ /*****************
+  * WaitForSomething:
+  *     Make the server suspend until there is
+@@ -150,6 +153,7 @@ WaitForSomething(int *pClientsReady)
+     INT32 timeout = 0;
+     fd_set clientsReadable;
+     fd_set clientsWritable;
++    fd_set socketsWritable;
+     int curclient;
+     int selecterr;
+     static int nready;
+@@ -212,6 +216,9 @@ WaitForSomething(int *pClientsReady)
+             XFD_COPYSET(&AllSockets, &LastSelectMask);
+         }
+ 
++        FD_ZERO(&socketsWritable);
++        vncWriteBlockHandler(&socketsWritable);
++
+         BlockHandler((void *) &wt, (void *) &LastSelectMask);
+         if (NewOutputPending)
+             FlushAllOutput();
+@@ -223,10 +230,20 @@ WaitForSomething(int *pClientsReady)
+             i = Select(MaxClients, &LastSelectMask, &clientsWritable, NULL, wt);
+         }
+         else {
+-            i = Select(MaxClients, &LastSelectMask, NULL, NULL, wt);
++	    if (AnyClientsWriteBlocked)
++		XFD_ORSET(&socketsWritable, &ClientsWriteBlocked, &socketsWritable);
++
++	    if (XFD_ANYSET(&socketsWritable)) {
++		i = Select(MaxClients, &LastSelectMask, &socketsWritable, NULL, wt);
++		if (AnyClientsWriteBlocked)
++		    XFD_ANDSET(&clientsWritable, &socketsWritable, &ClientsWriteBlocked);
++	    } else {
++		i = Select(MaxClients, &LastSelectMask, NULL, NULL, wt);
++	    }
+         }
+         selecterr = GetErrno();
+         WakeupHandler(i, (void *) &LastSelectMask);
++	vncWriteWakeupHandler(i, &socketsWritable);
+         if (i <= 0) {           /* An error or timeout occurred */
+             if (dispatchException)
+                 return 0;
diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx
index f367840..9fa9a66 100644
--- a/vncviewer/OptionsDialog.cxx
+++ b/vncviewer/OptionsDialog.cxx
@@ -724,19 +724,9 @@
 
   menuKeyChoice = new Fl_Choice(LBLLEFT(tx, ty, 150, CHOICE_HEIGHT, _("Menu key")));
 
-  Fl_Menu_Item items[getMenuKeySymbolCount() + 2];
-
-  memset(items, 0, sizeof(items));
-
-  items[0].text = strdup(_("None"));
-  items[0].flags = FL_MENU_DIVIDER;
-
+  fltk_menu_add(menuKeyChoice, _("None"), 0, NULL, (void*)0, FL_MENU_DIVIDER);
   for (int i = 0; i < getMenuKeySymbolCount(); i++)
-      items[i+1].text = strdup(getMenuKeySymbols()[i].name);
-
-  items[getMenuKeySymbolCount()+1].text = NULL;
-
-  menuKeyChoice->copy(items);
+    fltk_menu_add(menuKeyChoice, getMenuKeySymbols()[i].name, 0, NULL, 0, 0);
 
   ty += CHOICE_HEIGHT + TIGHT_MARGIN;
 
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index 463523f..6c77e58 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -24,7 +24,6 @@
 #include <assert.h>
 #include <stdio.h>
 #include <string.h>
-#include <stdint.h>
 
 #include <rfb/CMsgWriter.h>
 #include <rfb/LogWriter.h>
@@ -1061,69 +1060,50 @@
 
 void Viewport::initContextMenu()
 {
-  size_t i;
-  Fl_Menu_Item items[] = {
-    { strdup(_("E&xit viewer")), 0, NULL, (void*)ID_EXIT, FL_MENU_DIVIDER },
+  contextMenu->clear();
+
+  fltk_menu_add(contextMenu, _("E&xit viewer"), 0, NULL,
+                (void*)ID_EXIT, FL_MENU_DIVIDER);
 
 #ifdef HAVE_FLTK_FULLSCREEN
-    { strdup(_("&Full screen")), 0, NULL, (void*)ID_FULLSCREEN, FL_MENU_TOGGLE },
+  fltk_menu_add(contextMenu, _("&Full screen"), 0, NULL, (void*)ID_FULLSCREEN,
+                FL_MENU_TOGGLE | (window()->fullscreen_active()?FL_MENU_VALUE:0));
 #endif
-    { strdup(_("Resize &window to session")), 0, NULL, (void*)ID_RESIZE, FL_MENU_DIVIDER },
-
-    { strdup(_("&Ctrl")), 0, NULL, (void*)ID_CTRL, FL_MENU_TOGGLE },
-    { strdup(_("&Alt")), 0, NULL, (void*)ID_ALT, FL_MENU_TOGGLE },
-
-    { strdup("Menu key"), 0, NULL, (void*)ID_MENUKEY, 0 },
-    { strdup("Secret shortcut menu key"), menuKeyCode, NULL, (void*)ID_MENUKEY, FL_MENU_INVISIBLE },
-    { strdup(_("Send Ctrl-Alt-&Del")), 0, NULL, (void*)ID_CTRLALTDEL, FL_MENU_DIVIDER },
-
-    { strdup(_("&Refresh screen")), 0, NULL, (void*)ID_REFRESH, FL_MENU_DIVIDER },
-
-    { strdup(_("&Options...")), 0, NULL, (void*)ID_OPTIONS, 0 },
-    { strdup(_("Connection &info...")), 0, NULL, (void*)ID_INFO, 0 },
-    { strdup(_("About &TigerVNC viewer...")), 0, NULL, (void*)ID_ABOUT, FL_MENU_DIVIDER },
-
-    { strdup(_("Dismiss &menu")), 0, NULL, (void*)ID_DISMISS, 0 },
-
-    {0}
-  };
-
-  // Update any state
-  for (i = 0;i < sizeof(items)/sizeof(items[0]);i++) {
-    switch ((intptr_t)items[i].user_data_) {
+  fltk_menu_add(contextMenu, _("Resize &window to session"), 0, NULL,
+                (void*)ID_RESIZE,
 #ifdef HAVE_FLTK_FULLSCREEN
-    case ID_FULLSCREEN:
-      if (window()->fullscreen_active())
-        items[i].flags |= FL_MENU_VALUE;
-      break;
-    case ID_RESIZE:
-      if (window()->fullscreen_active())
-        items[i].flags |= FL_MENU_INACTIVE;
-      break;
+                (window()->fullscreen_active()?FL_MENU_INACTIVE:0) |
 #endif
-    case ID_CTRL:
-      if (menuCtrlKey)
-        items[i].flags |= FL_MENU_VALUE;
-      break;
-    case ID_ALT:
-      if (menuAltKey)
-        items[i].flags |= FL_MENU_VALUE;
-      break;
-    case ID_MENUKEY:
-      // The menu key needs to have its fields updated, or the entries hidden
-      if (!menuKeySym)
-        items[i].flags |= FL_MENU_INACTIVE | FL_MENU_INVISIBLE;
-      else if (!(items[i].flags & FL_MENU_INVISIBLE)) {
-        char sendMenuKey[64];
-        snprintf(sendMenuKey, 64, _("Send %s"), (const char *)menuKey);
-        free((void*)items[i].text);
-        items[i].text = strdup(sendMenuKey);
-      }
-      break;
-    }
+                FL_MENU_DIVIDER);
+
+  fltk_menu_add(contextMenu, _("&Ctrl"), 0, NULL, (void*)ID_CTRL,
+                FL_MENU_TOGGLE | (menuCtrlKey?FL_MENU_VALUE:0));
+  fltk_menu_add(contextMenu, _("&Alt"), 0, NULL, (void*)ID_ALT,
+                FL_MENU_TOGGLE | (menuAltKey?FL_MENU_VALUE:0));
+
+  if (menuKeySym) {
+    char sendMenuKey[64];
+    snprintf(sendMenuKey, 64, _("Send %s"), (const char *)menuKey);
+    fltk_menu_add(contextMenu, sendMenuKey, 0, NULL, (void*)ID_MENUKEY, 0);
+    fltk_menu_add(contextMenu, "Secret shortcut menu key", menuKeyCode, NULL,
+                  (void*)ID_MENUKEY, FL_MENU_INVISIBLE);
   }
 
-  contextMenu->copy(items);
+  fltk_menu_add(contextMenu, _("Send Ctrl-Alt-&Del"), 0, NULL,
+                (void*)ID_CTRLALTDEL, FL_MENU_DIVIDER);
+
+  fltk_menu_add(contextMenu, _("&Refresh screen"), 0, NULL,
+                (void*)ID_REFRESH, FL_MENU_DIVIDER);
+
+  fltk_menu_add(contextMenu, _("&Options..."), 0, NULL,
+                (void*)ID_OPTIONS, 0);
+  fltk_menu_add(contextMenu, _("Connection &info..."), 0, NULL,
+                (void*)ID_INFO, 0);
+  fltk_menu_add(contextMenu, _("About &TigerVNC viewer..."), 0, NULL,
+                (void*)ID_ABOUT, FL_MENU_DIVIDER);
+
+  fltk_menu_add(contextMenu, _("Dismiss &menu"), 0, NULL,
+                (void*)ID_DISMISS, 0);
 }
 
 
diff --git a/vncviewer/cocoa.mm b/vncviewer/cocoa.mm
index e9e0968..0992100 100644
--- a/vncviewer/cocoa.mm
+++ b/vncviewer/cocoa.mm
@@ -203,7 +203,7 @@
 
   layout = NULL;
 
-#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
+#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) || defined(__x86_64__)
   TISInputSourceRef keyboard;
   CFDataRef uchr;
 
diff --git a/vncviewer/fltk_layout.h b/vncviewer/fltk_layout.h
index c16a359..21fb00a 100644
--- a/vncviewer/fltk_layout.h
+++ b/vncviewer/fltk_layout.h
@@ -20,6 +20,7 @@
 #define __FLTK_LAYOUT_H__
 
 #include <FL/fl_draw.H>
+#include <FL/Fl_Menu_.H>
 
 /* Calculates the width of a string as printed by FLTK (pixels) */
 static inline int gui_str_len(const char *str)
@@ -67,6 +68,53 @@
     return len;
 }
 
+/* Filter out unsafe characters for menu entries */
+static inline int fltk_menu_escape(const char *in, char *out, size_t maxlen)
+{
+    int len;
+
+    len = 0;
+
+    while (*in != '\0') {
+        if (*in == '/') {
+            if (maxlen >= 3) {
+                *out++ = '\\';
+                *out++ = '/';
+                maxlen -= 2;
+            }
+
+            len += 2;
+        } else {
+            if (maxlen >= 2) {
+                *out++ = *in;
+                maxlen--;
+            }
+
+            len += 1;
+        }
+
+        in++;
+    }
+
+    if (maxlen)
+        *out = '\0';
+
+    return len;
+}
+
+/* Helper to add menu entries safely */
+static inline void fltk_menu_add(Fl_Menu_ *menu, const char *text,
+                                 int shortcut, Fl_Callback *cb,
+                                 void *data = 0, int flags = 0)
+{
+    char buffer[1024];
+
+    if (fltk_menu_escape(text, buffer, sizeof(buffer)) >= sizeof(buffer))
+        return;
+
+    menu->add(buffer, shortcut, cb, data, flags);
+}
+
 /**** MARGINS ****/
 
 #define OUTER_MARGIN            10