Merge branches 'fix-fullscreen-scroll' and 'fix-scrollbar-visibility' of https://github.com/LukeShu/tigervnc
diff --git a/java/com/tigervnc/rfb/CopyRectDecoder.java b/java/com/tigervnc/rfb/CopyRectDecoder.java
index a4298fd..40452e2 100644
--- a/java/com/tigervnc/rfb/CopyRectDecoder.java
+++ b/java/com/tigervnc/rfb/CopyRectDecoder.java
@@ -31,6 +31,20 @@
     os.copyBytes(is, 4);
   }
 
+  public void getAffectedRegion(Rect rect, Object buffer,
+                                int buflen, ConnParams cp,
+                                Region region)
+  {
+    MemInStream is = new MemInStream((byte[])buffer, 0, buflen);
+    int srcX = is.readU16();
+    int srcY = is.readU16();
+
+    super.getAffectedRegion(rect, buffer, buflen, cp, region);
+
+    region.assign_union(new Region(rect.translate(new Point(srcX-rect.tl.x,
+                                                            srcY-rect.tl.y))));
+  }
+
   public void decodeRect(Rect r, Object buffer,
                          int buflen, ConnParams cp,
                          ModifiablePixelBuffer pb)
diff --git a/java/com/tigervnc/rfb/DecodeManager.java b/java/com/tigervnc/rfb/DecodeManager.java
index c155746..9c7cacb 100644
--- a/java/com/tigervnc/rfb/DecodeManager.java
+++ b/java/com/tigervnc/rfb/DecodeManager.java
@@ -144,7 +144,6 @@
     entry.cp = conn.cp;
     entry.pb = pb;
     entry.bufferStream = bufferStream;
-    entry.affectedRegion = new Region(r);
 
     decoder.getAffectedRegion(r, bufferStream.data(),
                               bufferStream.length(), conn.cp,
@@ -209,6 +208,7 @@
   private class QueueEntry {
 
     public QueueEntry() {
+      affectedRegion = new Region();
     }
     public boolean active;
     public Rect rect;
@@ -228,7 +228,7 @@
 
       stopRequested = false;
 
-      (thread = new Thread(this)).start();
+      (thread = new Thread(this, "Decoder Thread")).start();
     }
 
     public void stop()
@@ -308,7 +308,7 @@
         return manager.workQueue.peek();
 
       next:for (iter = manager.workQueue.iterator(); iter.hasNext();) {
-        QueueEntry entry;
+        QueueEntry entry, entry2;
 
         Iterator<QueueEntry> iter2;
 
@@ -323,8 +323,9 @@
         // If this is an ordered decoder then make sure this is the first
         // rectangle in the queue for that decoder
         if ((entry.decoder.flags & DecoderOrdered) != 0) {
-          for (iter2 = manager.workQueue.iterator(); iter2.hasNext() && iter2 != iter;) {
-            if (entry.encoding == (iter2.next()).encoding) {
+          for (iter2 = manager.workQueue.iterator(); iter2.hasNext() &&
+               !(entry2 = iter2.next()).equals(entry);) {
+            if (entry.encoding == entry2.encoding) {
               lockedRegion.assign_union(entry.affectedRegion);
               continue next;
             }
@@ -334,8 +335,8 @@
         // For a partially ordered decoder we must ask the decoder for each
         // pair of rectangles.
         if ((entry.decoder.flags & DecoderPartiallyOrdered) != 0) {
-          for (iter2 = manager.workQueue.iterator(); iter2.hasNext() && iter2 != iter;) {
-            QueueEntry entry2 = iter2.next();
+          for (iter2 = manager.workQueue.iterator(); iter2.hasNext() &&
+               !(entry2 = iter2.next()).equals(entry);) {
             if (entry.encoding != entry2.encoding)
               continue;
             if (entry.decoder.doRectsConflict(entry.rect,
diff --git a/java/com/tigervnc/rfb/FullFramePixelBuffer.java b/java/com/tigervnc/rfb/FullFramePixelBuffer.java
index 1c3b095..2933726 100644
--- a/java/com/tigervnc/rfb/FullFramePixelBuffer.java
+++ b/java/com/tigervnc/rfb/FullFramePixelBuffer.java
@@ -32,8 +32,11 @@
 
   public WritableRaster getBufferRW(Rect r)
   {
-    return data.createWritableChild(r.tl.x, r.tl.y, r.width(), r.height(),
-                                    0, 0, null);
+    synchronized(image) {
+      return data.createWritableChild(r.tl.x, r.tl.y,
+                                      r.width(), r.height(),
+                                      0, 0, null);
+    }
   }
 
   public void commitBufferRW(Rect r)
@@ -42,12 +45,14 @@
 
   public Raster getBuffer(Rect r)
   {
-    Raster src =
-      data.createChild(r.tl.x, r.tl.y, r.width(), r.height(), 0, 0, null);
-    WritableRaster dst =
-      data.createCompatibleWritableRaster(r.width(), r.height());
-    dst.setDataElements(0, 0, src);
-    return dst;
+    synchronized(image) {
+      Raster src =
+        data.createChild(r.tl.x, r.tl.y, r.width(), r.height(), 0, 0, null);
+      WritableRaster dst =
+        data.createCompatibleWritableRaster(r.width(), r.height());
+      dst.setDataElements(0, 0, src);
+      return dst;
+    }
   }
 
   protected WritableRaster data;
diff --git a/java/com/tigervnc/rfb/JpegDecompressor.java b/java/com/tigervnc/rfb/JpegDecompressor.java
index 23c6f65..745ec85 100644
--- a/java/com/tigervnc/rfb/JpegDecompressor.java
+++ b/java/com/tigervnc/rfb/JpegDecompressor.java
@@ -40,10 +40,11 @@
       BufferedImage jpeg =
         ImageIO.read(new MemoryCacheImageInputStream(new ByteArrayInputStream(src)));
       jpeg.setAccelerationPriority(1);
-      BufferedImage image = (BufferedImage)pb.getImage();
-      Graphics2D g2 = image.createGraphics();
-      g2.drawImage(jpeg, r.tl.x, r.tl.y, r.width(), r.height(), null);
-      g2.dispose();
+      synchronized(pb.getImage()) {
+        Graphics2D g2 = (Graphics2D)pb.getImage().getGraphics();
+        g2.drawImage(jpeg, r.tl.x, r.tl.y, r.width(), r.height(), null);
+        g2.dispose();
+      }
       jpeg.flush();
     } catch (IOException e) {
       throw new Exception(e.getMessage());
diff --git a/java/com/tigervnc/rfb/ModifiablePixelBuffer.java b/java/com/tigervnc/rfb/ModifiablePixelBuffer.java
index 1a1dcc6..4925277 100644
--- a/java/com/tigervnc/rfb/ModifiablePixelBuffer.java
+++ b/java/com/tigervnc/rfb/ModifiablePixelBuffer.java
@@ -62,7 +62,7 @@
   // These operations DO NOT clip to the pixelbuffer area, or trap overruns.
 
   // Fill a rectangle
-  public synchronized void fillRect(Rect r, byte[] pix)
+  public void fillRect(Rect r, byte[] pix)
   {
     WritableRaster buf;
     int w, h;
@@ -80,13 +80,15 @@
     for (int i=0; i < r.area(); i++)
       src.put(pix);
     Raster raster = format.rasterFromBuffer(r, (ByteBuffer)src.rewind());
-    buf.setDataElements(0, 0, raster);
+    synchronized(image) {
+      buf.setDataElements(0, 0, raster);
+    }
 
     commitBufferRW(r);
   }
 
   // Copy pixel data to the buffer
-  public synchronized void imageRect(Rect r, byte[] pixels)
+  public void imageRect(Rect r, byte[] pixels)
   {
     WritableRaster dest = getBufferRW(r);
 
@@ -94,14 +96,15 @@
     ByteBuffer src =
       ByteBuffer.wrap(pixels, 0, length).order(format.getByteOrder());
     Raster raster = format.rasterFromBuffer(r, src);
-    dest.setDataElements(0, 0, raster);
+    synchronized(image) {
+      dest.setDataElements(0, 0, raster);
+    }
 
     commitBufferRW(r);
   }
 
   // Copy pixel data from one PixelBuffer location to another
-  public synchronized void copyRect(Rect rect,
-                                    Point move_by_delta)
+  public void copyRect(Rect rect, Point move_by_delta)
   {
     Raster srcData;
     WritableRaster dstData;
@@ -135,7 +138,9 @@
     srcData = getBuffer(srect);
     dstData = getBufferRW(drect);
 
-    dstData.setDataElements(0, 0, srcData);
+    synchronized(image) {
+      dstData.setDataElements(0, 0, srcData);
+    }
 
     commitBufferRW(rect);
   }
@@ -145,8 +150,7 @@
   //   maskPos specifies the pixel offset in the mask to start from.
   //   mask_ is a pointer to the mask bits at (0,0).
   //   pStride and mStride are the strides of the pixel and mask buffers.
-  public synchronized void maskRect(Rect r,
-                                    Object pixels, byte[] mask_)
+  public void maskRect(Rect r, Object pixels, byte[] mask_)
   {
     Rect cr = getRect().intersect(r);
     if (cr.is_empty()) return;
@@ -187,15 +191,17 @@
 
     int maskBytesPerRow = (w + 7) / 8;
 
-    for (int y = 0; y < h; y++) {
-      int cy = offset.y + y;
-      for (int x = 0; x < w; x++) {
-        int cx = offset.x + x;
-        int byte_ = cy * maskBytesPerRow + y / 8;
-        int bit = 7 - cx % 8;
+    synchronized(image) {
+      for (int y = 0; y < h; y++) {
+        int cy = offset.y + y;
+        for (int x = 0; x < w; x++) {
+          int cx = offset.x + x;
+          int byte_ = cy * maskBytesPerRow + y / 8;
+          int bit = 7 - cx % 8;
 
-        if ((mask_[byte_] & (1 << bit)) != 0)
-          data.setDataElements(x+cx, y+cy, t.getDataElements(x+cx, y+cy, null));
+          if ((mask_[byte_] & (1 << bit)) != 0)
+            data.setDataElements(x+cx, y+cy, t.getDataElements(x+cx, y+cy, null));
+        }
       }
     }
 
@@ -203,7 +209,7 @@
   }
 
   //   pixel is the Pixel value to be used where mask_ is set
-  public synchronized void maskRect(Rect r, int pixel, byte[] mask)
+  public void maskRect(Rect r, int pixel, byte[] mask)
   {
     // FIXME
   }
@@ -211,7 +217,7 @@
   // Render in a specific format
   //   Does the exact same thing as the above methods, but the given
   //   pixel values are defined by the given PixelFormat. 
-  public synchronized void fillRect(PixelFormat pf, Rect dest, byte[] pix)
+  public void fillRect(PixelFormat pf, Rect dest, byte[] pix)
   {
     WritableRaster dstBuffer = getBufferRW(dest);
 
@@ -226,13 +232,15 @@
         src.put(pix);
       Raster raster = pf.rasterFromBuffer(dest, (ByteBuffer)src.rewind());
       ColorConvertOp converter = format.getColorConvertOp(cm.getColorSpace());
-      converter.filter(raster, dstBuffer);
+      synchronized(image) {
+        converter.filter(raster, dstBuffer);
+      }
     }
 
     commitBufferRW(dest);
   }
 
-  public synchronized void imageRect(PixelFormat pf, Rect dest, byte[] pixels)
+  public void imageRect(PixelFormat pf, Rect dest, byte[] pixels)
   {
     WritableRaster dstBuffer = getBufferRW(dest);
 
@@ -245,23 +253,29 @@
       ByteBuffer src = ByteBuffer.wrap(pixels, 0, length).order(pf.getByteOrder());
       Raster raster = pf.rasterFromBuffer(dest, src);
       ColorConvertOp converter = format.getColorConvertOp(cm.getColorSpace());
-      converter.filter(raster, dstBuffer);
+      synchronized(image) {
+        converter.filter(raster, dstBuffer);
+      }
     }
 
     commitBufferRW(dest);
   }
 
-  public synchronized void imageRect(PixelFormat pf, Rect dest, Raster pixels)
+  public void imageRect(PixelFormat pf, Rect dest, Raster pixels)
   {
     WritableRaster dstBuffer = getBufferRW(dest);
 
     ColorModel cm = pf.getColorModel();
     if (cm.isCompatibleRaster(dstBuffer) &&
         cm.isCompatibleSampleModel(dstBuffer.getSampleModel())) {
-      dstBuffer.setDataElements(0, 0, pixels);
+      synchronized(image) {
+        dstBuffer.setDataElements(0, 0, pixels);
+      }
     } else {
       ColorConvertOp converter = format.getColorConvertOp(cm.getColorSpace());
-      converter.filter(pixels, dstBuffer);
+      synchronized(image) {
+        converter.filter(pixels, dstBuffer);
+      }
     }
 
     commitBufferRW(dest);
diff --git a/java/com/tigervnc/rfb/Region.java b/java/com/tigervnc/rfb/Region.java
index f7da91d..56a00af 100644
--- a/java/com/tigervnc/rfb/Region.java
+++ b/java/com/tigervnc/rfb/Region.java
@@ -35,7 +35,6 @@
 
   public Region(Region r) {
     super(r);
-    //intersect(r);
   }
 
   public void clear() { reset(); }
@@ -46,13 +45,6 @@
     } else {
       clear();
       assign_union(new Region(r));
-      /*
-      xrgn.numRects = 1;
-      xrgn.rects[0].x1 = xrgn.extents.x1 = r.tl.x;
-      xrgn.rects[0].y1 = xrgn.extents.y1 = r.tl.y;
-      xrgn.rects[0].x2 = xrgn.extents.x2 = r.br.x;
-      xrgn.rects[0].y2 = xrgn.extents.y2 = r.br.y;
-      */
     }
   }
 
@@ -63,33 +55,33 @@
   }
 
   public void assign_intersect(Region r) {
-    intersect(r);
+    super.intersect(r);
   }
 
   public void assign_union(Region r) {
-    add(r);
+    super.add(r);
   }
 
   public void assign_subtract(Region r) {
-    subtract(r);
+    super.subtract(r);
   }
 
   public Region intersect(Region r) {
-    Region ret = new Region(this);
-    ((Area)ret).intersect(this);
-    return ret;
+    Region reg = new Region(this);
+    ((Area)reg).intersect(r);
+    return reg;
   }
 
   public Region union(Region r) {
-    Region ret = new Region(r);
-    ((Area)ret).add(this);
-    return ret;
+    Region reg = new Region(this);
+    ((Area)reg).add(r);
+    return reg;
   }
 
   public Region subtract(Region r) {
-    Region ret = new Region(this);
-    ((Area)ret).subtract(r);
-    return ret;
+    Region reg = new Region(this);
+    ((Area)reg).subtract(r);
+    return reg;
   }
 
   public boolean is_empty() { return isEmpty(); }
diff --git a/java/com/tigervnc/vncviewer/JavaPixelBuffer.java b/java/com/tigervnc/vncviewer/JavaPixelBuffer.java
index 16242d0..016b8cf 100644
--- a/java/com/tigervnc/vncviewer/JavaPixelBuffer.java
+++ b/java/com/tigervnc/vncviewer/JavaPixelBuffer.java
@@ -24,6 +24,7 @@
 import java.nio.*;
 
 import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Point;
 
 public class JavaPixelBuffer extends PlatformPixelBuffer 
 {
@@ -31,25 +32,58 @@
   public JavaPixelBuffer(int w, int h) {
     super(getPreferredPF(), w, h,
           getPreferredPF().getColorModel().createCompatibleWritableRaster(w,h));
-    image = new BufferedImage(getPreferredPF().getColorModel(),
-                              getBufferRW(new Rect(0, 0, w, h)), true, null);
+    ColorModel cm = format.getColorModel();
+    image = new BufferedImage(cm, data, cm.isAlphaPremultiplied(), null);
     image.setAccelerationPriority(1);
   }
 
-  public synchronized void fillRect(Rect r, byte[] pix)
+  public WritableRaster getBufferRW(Rect r)
+  {
+    synchronized(image) {
+      return ((BufferedImage)image)
+        .getSubimage(r.tl.x, r.tl.y, r.width(), r.height()).getRaster();
+    }
+  }
+
+  public Raster getBuffer(Rect r)
+  {
+    Rectangle rect =
+      new Rectangle(r.tl.x, r.tl.y, r.width(), r.height());
+    synchronized(image) {
+      return ((BufferedImage)image).getData(rect);
+    }
+  }
+
+  public void fillRect(Rect r, byte[] pix)
   {
     ColorModel cm = format.getColorModel();
     int pixel =
       ByteBuffer.wrap(pix).order(format.getByteOrder()).asIntBuffer().get(0);
     Color c = new Color(cm.getRGB(pixel));
-    Graphics2D g2 = ((BufferedImage)image).createGraphics();
-    g2.setColor(c);
-    g2.fillRect(r.tl.x, r.tl.y, r.width(), r.height());
-    g2.dispose();
+    synchronized(image) {
+      Graphics2D g2 = (Graphics2D)image.getGraphics();
+      g2.setColor(c);
+      g2.fillRect(r.tl.x, r.tl.y, r.width(), r.height());
+      g2.dispose();
+    }
 
     commitBufferRW(r);
   }
 
+  public void copyRect(Rect rect, Point move_by_delta)
+  {
+    synchronized(image) {
+      Graphics2D g2 = (Graphics2D)image.getGraphics();
+      g2.copyArea(rect.tl.x - move_by_delta.x,
+                  rect.tl.y - move_by_delta.y,
+                  rect.width(), rect.height(),
+                  move_by_delta.x, move_by_delta.y);
+      g2.dispose();
+    }
+
+    commitBufferRW(rect);
+  }
+
   private static PixelFormat getPreferredPF()
   {
     GraphicsEnvironment ge =
diff --git a/java/com/tigervnc/vncviewer/Viewport.java b/java/com/tigervnc/vncviewer/Viewport.java
index fd1e6cf..01d46ca 100644
--- a/java/com/tigervnc/vncviewer/Viewport.java
+++ b/java/com/tigervnc/vncviewer/Viewport.java
@@ -29,6 +29,7 @@
 //
 
 package com.tigervnc.vncviewer;
+
 import java.awt.*;
 import java.awt.Color;
 import java.awt.color.ColorSpace;
@@ -100,8 +101,6 @@
   public void updateWindow() {
     Rect r = frameBuffer.getDamage();
     if (!r.is_empty()) {
-      if (image == null)
-        image = (BufferedImage)frameBuffer.getImage();
       if (cc.cp.width != scaledWidth ||
           cc.cp.height != scaledHeight) {
         AffineTransform t = new AffineTransform(); 
@@ -197,7 +196,6 @@
       frameBuffer = createFramebuffer(frameBuffer.getPF(), w, h);
       assert(frameBuffer != null);
       cc.setFramebuffer(frameBuffer);
-      image = (BufferedImage)frameBuffer.getImage();
     }
     setScaledSize(w, h);
   }
@@ -229,13 +227,16 @@
 
   public void paintComponent(Graphics g) {
     Graphics2D g2 = (Graphics2D)g;
-    if (cc.cp.width != scaledWidth ||
-        cc.cp.height != scaledHeight) {
-      g2.setRenderingHint(RenderingHints.KEY_RENDERING,
-                          RenderingHints.VALUE_RENDER_QUALITY);
-      g2.drawImage(image, 0, 0, scaledWidth, scaledHeight, null);
-    } else {
-      g2.drawImage(image, 0, 0, null);
+    synchronized(frameBuffer.getImage()) {
+      if (cc.cp.width != scaledWidth ||
+          cc.cp.height != scaledHeight) {
+        g2.setRenderingHint(RenderingHints.KEY_RENDERING,
+                            RenderingHints.VALUE_RENDER_QUALITY);
+        g2.drawImage(frameBuffer.getImage(), 0, 0,
+                     scaledWidth, scaledHeight, null);
+      } else {
+        g2.drawImage(frameBuffer.getImage(), 0, 0, null);
+      }
     }
     g2.dispose();
   }
@@ -415,7 +416,6 @@
 
   // access to cc by different threads is specified in CConn
   private CConn cc;
-  private BufferedImage image;
 
   // access to the following must be synchronized:
   public PlatformPixelBuffer frameBuffer;
diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java
index 7a76122..e123499 100644
--- a/java/com/tigervnc/vncviewer/VncViewer.java
+++ b/java/com/tigervnc/vncviewer/VncViewer.java
@@ -440,8 +440,7 @@
   }
 
   public void start() {
-    thread = new Thread(this);
-    thread.start();
+    (new Thread(this, "VncViewer Thread")).start();
   }
 
   public void exit(int n) {
@@ -555,6 +554,5 @@
   = new StringParameter("Config",
   "Specifies a configuration file to load.", null);
 
-  Thread thread;
   static LogWriter vlog = new LogWriter("VncViewer");
 }
diff --git a/po/da.po b/po/da.po
index f917aeb..2ab3881 100644
--- a/po/da.po
+++ b/po/da.po
@@ -1,20 +1,21 @@
 # Danish translation of tigervnc.
-# Copyright (C) 2016 the TigerVNC Team (msgids)
+# Copyright (C) 2017 the TigerVNC Team (msgids)
 # This file is distributed under the same license as the tigervnc package.
-# Joe Hansen <joedalton2@yahoo.dk>, 2015, 2016.
+# Joe Hansen <joedalton2@yahoo.dk>, 2015, 2016, 2017.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: tigervnc 1.6.90\n"
+"Project-Id-Version: tigervnc 1.7.90\n"
 "Report-Msgid-Bugs-To: tigervnc-devel@googlegroups.com\n"
-"POT-Creation-Date: 2016-07-01 10:15+0000\n"
-"PO-Revision-Date: 2016-07-04 15:00+0000\n"
+"POT-Creation-Date: 2017-04-19 13:05+0000\n"
+"PO-Revision-Date: 2017-05-22 15:00+0000\n"
 "Last-Translator: Joe Hansen <joedalton2@yahoo.dk>\n"
 "Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
 "Language: da\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
+"X-Bugs: Report translation errors to the Language-Team address.\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
 #: vncviewer/CConn.cxx:110
@@ -72,81 +73,77 @@
 msgid "Security method: %s"
 msgstr "Sikkerhedsmetode: %s"
 
-#: vncviewer/CConn.cxx:319
+#: vncviewer/CConn.cxx:343
 #, c-format
 msgid "SetDesktopSize failed: %d"
 msgstr "SetDesktopSize fejlede: %d"
 
-#: vncviewer/CConn.cxx:411
+#: vncviewer/CConn.cxx:413
 msgid "Invalid SetColourMapEntries from server!"
 msgstr "Ugyldig SetColourMapEntries fra server!"
 
-#: vncviewer/CConn.cxx:485
+#: vncviewer/CConn.cxx:489
 msgid "Enabling continuous updates"
 msgstr "Aktiverer fortsættende opdateringer"
 
-#: vncviewer/CConn.cxx:555
+#: vncviewer/CConn.cxx:559
 #, c-format
 msgid "Throughput %d kbit/s - changing to quality %d"
 msgstr "Gennemløb %d kbit/s - ændrer til kvalitet %d"
 
-#: vncviewer/CConn.cxx:577
+#: vncviewer/CConn.cxx:581
 #, c-format
 msgid "Throughput %d kbit/s - full color is now %s"
 msgstr "Gennemløb %d kbit/s - fuld farve er nu %s"
 
-#: vncviewer/CConn.cxx:579
+#: vncviewer/CConn.cxx:583
 msgid "disabled"
 msgstr "deaktiveret"
 
-#: vncviewer/CConn.cxx:579
+#: vncviewer/CConn.cxx:583
 msgid "enabled"
 msgstr "aktiveret"
 
-#: vncviewer/CConn.cxx:589
+#: vncviewer/CConn.cxx:593
 #, c-format
 msgid "Using %s encoding"
 msgstr "Bruger %s-kodning"
 
-#: vncviewer/CConn.cxx:636
+#: vncviewer/CConn.cxx:640
 #, c-format
 msgid "Using pixel format %s"
 msgstr "Bruger billedpunktsformat %s"
 
-#: vncviewer/DesktopWindow.cxx:106
+#: vncviewer/DesktopWindow.cxx:121
 msgid "Invalid geometry specified!"
 msgstr "Ugyldig geometri angivet!"
 
-#: vncviewer/DesktopWindow.cxx:303
+#: vncviewer/DesktopWindow.cxx:434
 msgid "Adjusting window size to avoid accidental full screen request"
 msgstr "Justerer vinduesstørrelse for at undgå utilsigtet anmodning om fuld skærm"
 
-#: vncviewer/DesktopWindow.cxx:485 vncviewer/DesktopWindow.cxx:491
-#: vncviewer/DesktopWindow.cxx:504
+#: vncviewer/DesktopWindow.cxx:478
+#, c-format
+msgid "Press %s to open the context menu"
+msgstr "Tryk %s for at åbne kontekstmenuen"
+
+#: vncviewer/DesktopWindow.cxx:741 vncviewer/DesktopWindow.cxx:747
+#: vncviewer/DesktopWindow.cxx:760
 msgid "Failure grabbing keyboard"
 msgstr "Kunne ikke fange tastatur"
 
-#: vncviewer/DesktopWindow.cxx:516
+#: vncviewer/DesktopWindow.cxx:772
 msgid "Failure grabbing mouse"
 msgstr "Kunne ikke fange mus"
 
-#: vncviewer/DesktopWindow.cxx:746
+#: vncviewer/DesktopWindow.cxx:1002
 msgid "Invalid screen layout computed for resize request!"
 msgstr "Ugyldig skærmlayout beregnet for anmodning om ny størrelse!"
 
-#: vncviewer/FLTKPixelBuffer.cxx:33 vncviewer/OSXPixelBuffer.cxx:48
-#: vncviewer/X11PixelBuffer.cxx:117
+#: vncviewer/FLTKPixelBuffer.cxx:33
 msgid "Not enough memory for framebuffer"
 msgstr "Ikke nok hukommelse for framebuffer"
 
-#: vncviewer/OSXPixelBuffer.cxx:52
-msgid "Could not create framebuffer device"
-msgstr "Kunne ikke oprette framebuffer-enhed"
-
-#: vncviewer/OSXPixelBuffer.cxx:58
-msgid "Could not create framebuffer bitmap"
-msgstr "Kunne ikke oprettet framebuffer-bitmap"
-
 #: vncviewer/OptionsDialog.cxx:57
 msgid "VNC Viewer: Connection Options"
 msgstr "VNC-fremviser: Forbindelsesindstillinger"
@@ -217,7 +214,7 @@
 msgstr "Kryptering"
 
 #: vncviewer/OptionsDialog.cxx:604 vncviewer/OptionsDialog.cxx:657
-#: vncviewer/OptionsDialog.cxx:735
+#: vncviewer/OptionsDialog.cxx:737
 msgid "None"
 msgstr "Ingen"
 
@@ -261,55 +258,55 @@
 msgid "Accept clipboard from server"
 msgstr "Accepter udklipsholderen fra serveren"
 
-#: vncviewer/OptionsDialog.cxx:709
+#: vncviewer/OptionsDialog.cxx:710
 msgid "Also set primary selection"
 msgstr "Angiv også primær markering"
 
-#: vncviewer/OptionsDialog.cxx:716
+#: vncviewer/OptionsDialog.cxx:717
 msgid "Send clipboard to server"
 msgstr "Send udklipsholderen til serveren"
 
-#: vncviewer/OptionsDialog.cxx:723
+#: vncviewer/OptionsDialog.cxx:725
 msgid "Send primary selection as clipboard"
 msgstr "Send primær markering som udklipsholder"
 
-#: vncviewer/OptionsDialog.cxx:730
+#: vncviewer/OptionsDialog.cxx:732
 msgid "Pass system keys directly to server (full screen)"
 msgstr "Videresend systemnøgler direkte til serveren (fuld skærm)"
 
-#: vncviewer/OptionsDialog.cxx:733
+#: vncviewer/OptionsDialog.cxx:735
 msgid "Menu key"
 msgstr "Menutast"
 
-#: vncviewer/OptionsDialog.cxx:749
+#: vncviewer/OptionsDialog.cxx:751
 msgid "Screen"
 msgstr "Skærm"
 
-#: vncviewer/OptionsDialog.cxx:757
+#: vncviewer/OptionsDialog.cxx:759
 msgid "Resize remote session on connect"
 msgstr "Ændr størrelse for ekstern session ved forbind"
 
-#: vncviewer/OptionsDialog.cxx:770
+#: vncviewer/OptionsDialog.cxx:772
 msgid "Resize remote session to the local window"
 msgstr "Ændr størrelse for ekstern session til det lokale vindue"
 
-#: vncviewer/OptionsDialog.cxx:776
+#: vncviewer/OptionsDialog.cxx:778
 msgid "Full-screen mode"
 msgstr "Tilstand for fuld skærm"
 
-#: vncviewer/OptionsDialog.cxx:782
+#: vncviewer/OptionsDialog.cxx:784
 msgid "Enable full-screen mode over all monitors"
 msgstr "Aktiver tilstand for fuld skærm over alle skærme"
 
-#: vncviewer/OptionsDialog.cxx:791
+#: vncviewer/OptionsDialog.cxx:793
 msgid "Misc."
 msgstr "Div."
 
-#: vncviewer/OptionsDialog.cxx:799
+#: vncviewer/OptionsDialog.cxx:801
 msgid "Shared (don't disconnect other viewers)"
 msgstr "Delt (afbryd ikke andre fremvisere)"
 
-#: vncviewer/OptionsDialog.cxx:805
+#: vncviewer/OptionsDialog.cxx:807
 msgid "Show dot when no cursor"
 msgstr "Vis punktum når ingen markør"
 
@@ -361,156 +358,106 @@
 msgid "Username:"
 msgstr "Brugernavn:"
 
-#: vncviewer/Viewport.cxx:391
-#, c-format
-msgid "Unable to create platform specific framebuffer: %s"
-msgstr "Kan ikke oprette platformspecifik framebuffer: %s"
-
-#: vncviewer/Viewport.cxx:392
-msgid "Using platform independent framebuffer"
-msgstr "Bruger framebuffer uafhængig af platform"
-
-#: vncviewer/Viewport.cxx:628
+#: vncviewer/Viewport.cxx:586
 #, c-format
 msgid "No scan code for extended virtual key 0x%02x"
 msgstr "Ingen skanningskode for udvidet virtuel nøgle 0x%02x"
 
-#: vncviewer/Viewport.cxx:630
+#: vncviewer/Viewport.cxx:588
 #, c-format
 msgid "No scan code for virtual key 0x%02x"
 msgstr "Ingen skanningskode for virtuel nøgle 0x%02x"
 
-#: vncviewer/Viewport.cxx:647
+#: vncviewer/Viewport.cxx:605
 #, c-format
 msgid "No symbol for extended virtual key 0x%02x"
 msgstr "Intet symbol for udvidet virtuel nøgle 0x%02x"
 
-#: vncviewer/Viewport.cxx:649
+#: vncviewer/Viewport.cxx:607
 #, c-format
 msgid "No symbol for virtual key 0x%02x"
 msgstr "Intet symbol for virtuel nøgle 0x%02x"
 
-#: vncviewer/Viewport.cxx:687
+#: vncviewer/Viewport.cxx:645
 #, c-format
 msgid "No symbol for key code 0x%02x (in the current state)"
 msgstr "Intet symbol for nøglekode 0x%02x (i den nuværende tilstand)"
 
-#: vncviewer/Viewport.cxx:713
+#: vncviewer/Viewport.cxx:671
 #, c-format
 msgid "No symbol for key code %d (in the current state)"
 msgstr "Intet symbol for nøglekode %d (i den nuværende tilstand)"
 
-#: vncviewer/Viewport.cxx:750
+#: vncviewer/Viewport.cxx:708
 msgctxt "ContextMenu|"
 msgid "E&xit viewer"
 msgstr "&Afslut fremviser"
 
-#: vncviewer/Viewport.cxx:753
+#: vncviewer/Viewport.cxx:711
 msgctxt "ContextMenu|"
 msgid "&Full screen"
 msgstr "&Fuld skærm"
 
-#: vncviewer/Viewport.cxx:756
+#: vncviewer/Viewport.cxx:714
 msgctxt "ContextMenu|"
 msgid "Minimi&ze"
 msgstr "&Minimer"
 
-#: vncviewer/Viewport.cxx:758
+#: vncviewer/Viewport.cxx:716
 msgctxt "ContextMenu|"
 msgid "Resize &window to session"
 msgstr "Ændr størrelse for &vindue til session"
 
-#: vncviewer/Viewport.cxx:763
+#: vncviewer/Viewport.cxx:721
 msgctxt "ContextMenu|"
 msgid "&Ctrl"
 msgstr "&Ctrl"
 
-#: vncviewer/Viewport.cxx:766
+#: vncviewer/Viewport.cxx:724
 msgctxt "ContextMenu|"
 msgid "&Alt"
 msgstr "&Alt"
 
-#: vncviewer/Viewport.cxx:772
+#: vncviewer/Viewport.cxx:730
 #, c-format
 msgctxt "ContextMenu|"
 msgid "Send %s"
 msgstr "Send %s"
 
-#: vncviewer/Viewport.cxx:778
+#: vncviewer/Viewport.cxx:736
 msgctxt "ContextMenu|"
 msgid "Send Ctrl-Alt-&Del"
 msgstr "Send Ctrl-Alt-&Slet"
 
-#: vncviewer/Viewport.cxx:781
+#: vncviewer/Viewport.cxx:739
 msgctxt "ContextMenu|"
 msgid "&Refresh screen"
 msgstr "&Opdater skærm"
 
-#: vncviewer/Viewport.cxx:784
+#: vncviewer/Viewport.cxx:742
 msgctxt "ContextMenu|"
 msgid "&Options..."
 msgstr "&Indstillinger ..."
 
-#: vncviewer/Viewport.cxx:786
+#: vncviewer/Viewport.cxx:744
 msgctxt "ContextMenu|"
 msgid "Connection &info..."
 msgstr "Forbindelses&info ..."
 
-#: vncviewer/Viewport.cxx:788
+#: vncviewer/Viewport.cxx:746
 msgctxt "ContextMenu|"
 msgid "About &TigerVNC viewer..."
 msgstr "Om &TigerVNC-fremviseren ..."
 
-#: vncviewer/Viewport.cxx:791
+#: vncviewer/Viewport.cxx:749
 msgctxt "ContextMenu|"
 msgid "Dismiss &menu"
 msgstr "Fjern %menu"
 
-#: vncviewer/Viewport.cxx:875
+#: vncviewer/Viewport.cxx:833
 msgid "VNC connection info"
 msgstr "VNC-forbindelsesinfo"
 
-#: vncviewer/Win32PixelBuffer.cxx:62
-msgid "unable to create DIB section"
-msgstr "kan ikke oprette DIB-sektion"
-
-#: vncviewer/Win32PixelBuffer.cxx:79
-msgid "CreateCompatibleDC failed"
-msgstr "CreateCompatibleDC mislykkedes"
-
-#: vncviewer/Win32PixelBuffer.cxx:82
-msgid "SelectObject failed"
-msgstr "SelectObject mislykkedes"
-
-#: vncviewer/Win32PixelBuffer.cxx:91
-msgid "BitBlt failed"
-msgstr "BitBlt mislykkedes"
-
-#. TRANSLATORS: "pixmap" is an X11 concept and may not be suitable
-#. to translate.
-#: vncviewer/X11PixelBuffer.cxx:65
-msgid "Display lacks pixmap format for default depth"
-msgstr "Skærm mangler pixmap-format for standarddybde"
-
-#. TRANSLATORS: "pixmap" is an X11 concept and may not be suitable
-#. to translate.
-#: vncviewer/X11PixelBuffer.cxx:76
-msgid "Couldn't find suitable pixmap format"
-msgstr "Kunne ikke finde et egnet pixmap-format"
-
-#: vncviewer/X11PixelBuffer.cxx:85
-msgid "Only true colour displays supported"
-msgstr "Kun skærme med »true colour« er understøttet"
-
-#: vncviewer/X11PixelBuffer.cxx:87
-#, c-format
-msgid "Using default colormap and visual, TrueColor, depth %d."
-msgstr "Bruger standardfarvekort og visuel, TrueColor, dybde %d."
-
-#: vncviewer/X11PixelBuffer.cxx:113
-msgid "Could not create framebuffer image"
-msgstr "Kunne ikke oprette framebuffer-billede"
-
 #: vncviewer/parameters.cxx:286 vncviewer/parameters.cxx:320
 #, c-format
 msgid "The name of the parameter %s was too large to write to the registry"
@@ -709,9 +656,3 @@
 #, c-format
 msgid "Listening on port %d"
 msgstr "Lytter på port %d"
-
-#~ msgid "Unknown encoding %d"
-#~ msgstr "Ukendt kodning %d"
-
-#~ msgid "Unknown encoding"
-#~ msgstr "Ukendt kodning"
diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx
index 946b162..408efd1 100644
--- a/vncviewer/DesktopWindow.cxx
+++ b/vncviewer/DesktopWindow.cxx
@@ -1046,15 +1046,35 @@
 
   // Scrollbars visbility
 
-  if (!fullscreen_active() && (w() < viewport->w()))
-    hscroll->show();
-  else
+  if (fullscreen_active()) {
     hscroll->hide();
-
-  if (!fullscreen_active() && (h() < viewport->h()))
-    vscroll->show();
-  else
     vscroll->hide();
+  } else {
+    // Decide whether to show a scrollbar by checking if the window
+    // size (possibly minus scrollbar_size) is less than the viewport
+    // (remote framebuffer) size.
+    //
+    // We decide whether to subtract scrollbar_size on an axis by
+    // checking if the other axis *definitely* needs a scrollbar.  You
+    // might be tempted to think that this becomes a weird recursive
+    // problem, but it isn't: If the window size is less than the
+    // viewport size (without subtracting the scrollbar_size), then
+    // that axis *definitely* needs a scrollbar; if the check changes
+    // when we subtract scrollbar_size, then that axis only *maybe*
+    // needs a scrollbar.  If both axes only "maybe" need a scrollbar,
+    // then neither does; so we don't need to recurse on the "maybe"
+    // cases.
+
+    if (w() - (h() < viewport->h() ? Fl::scrollbar_size() : 0) < viewport->w())
+      hscroll->show();
+    else
+      hscroll->hide();
+
+    if (h() - (w() < viewport->w() ? Fl::scrollbar_size() : 0) < viewport->h())
+      vscroll->show();
+    else
+      vscroll->hide();
+  }
 
   // Scrollbars positions
 
diff --git a/vncviewer/cocoa.mm b/vncviewer/cocoa.mm
index 85b736b..6e464fa 100644
--- a/vncviewer/cocoa.mm
+++ b/vncviewer/cocoa.mm
@@ -37,8 +37,11 @@
 
 #define NoSymbol 0
 
-// These are undocumented for unknown reasons
+// This wasn't added until 10.12
+#if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
 const int kVK_RightCommand = 0x36;
+#endif
+// And this is still missing
 const int kVK_Menu = 0x6E;
 
 static bool captured = false;