emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

MS Windows double buffering


From: Po Lu
Subject: MS Windows double buffering
Date: Thu, 28 Apr 2022 16:51:06 +0800

I took a stab at implementing double buffering on MS Windows without any
prior knowledge of programming for that platform.  It naturally follows
that the code is nowhere near complete, but it works with some major
problems:

  - It doesn't work on Windows XP or earlier.

  - Upon startup, part of the entire display is overwritten by the Emacs
    toolbar, until the first frame is resized.

  - w32_scroll_run doesn't work.

  - Paint buffers and their DCs are not released upon frame deletion.

  - BeginBufferedPaint is probably the wrong function to use to create
    the back buffer, since EndBufferedPaint isn't used.

I hope someone will finish implementing double buffering on MS Windows
based on this code.  Even in its present state, it already eliminates
all the flicker that was discussed earlier.

diff --git a/configure.ac b/configure.ac
index 7c8638a471..d42a6dd180 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2209,7 +2209,7 @@ AC_DEFUN
       W32_OBJ="$W32_OBJ w32image.o"
     fi
     W32_LIBS="$W32_LIBS -lwinmm -lusp10 -lgdi32 -lcomdlg32"
-    W32_LIBS="$W32_LIBS -lmpr -lwinspool -lole32 -lcomctl32"
+    W32_LIBS="$W32_LIBS -lmpr -lwinspool -lole32 -lcomctl32 -luxtheme"
     W32_RES_LINK="\$(EMACSRES)"
     CLIENTRES="emacsclient.res"
     CLIENTW="emacsclientw\$(EXEEXT)"
diff --git a/src/w32gui.h b/src/w32gui.h
index 4e8de84854..689346111a 100644
--- a/src/w32gui.h
+++ b/src/w32gui.h
@@ -19,6 +19,7 @@
 #ifndef EMACS_W32GUI_H
 #define EMACS_W32GUI_H
 #include <windows.h>
+#include <uxtheme.h>
 
 #include "systime.h" /* for Time */
 
diff --git a/src/w32term.c b/src/w32term.c
index 7837032304..e55910c9e8 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -275,6 +275,36 @@ XGetGCValues (void *ignore, XGCValues *gc,
 }
 #endif
 
+static void
+w32_show_back_buffer (struct frame *f, BOOL update_frame)
+{
+  struct w32_output *output;
+
+  output = FRAME_OUTPUT_DATA (f);
+
+  if (output->paint_buffer)
+    {
+      if (!update_frame)
+       {
+         EndBufferedPaint (output->paint_buffer, update_frame);
+         deselect_palette (f, output->paint_dc);
+         ReleaseDC (output->window_desc, output->paint_dc);
+
+         output->paint_buffer = NULL;
+         output->paint_buffer_desc = NULL;
+         output->paint_dc = NULL;
+       }
+      else
+       {
+         BitBlt (output->paint_dc, 0, 0,
+                 FRAME_PIXEL_WIDTH (f),
+                 FRAME_PIXEL_HEIGHT (f),
+                 output->paint_buffer_desc,
+                 0, 0, SRCCOPY);
+       }
+    }
+}
+
 static void
 w32_get_mouse_wheel_vertical_delta (void)
 {
@@ -706,8 +736,16 @@ w32_frame_up_to_date (struct frame *f)
 {
   if (FRAME_W32_P (f))
     FRAME_MOUSE_UPDATE (f);
+
+  if (!buffer_flipping_blocked_p ())
+    w32_show_back_buffer (f, TRUE);
 }
 
+static void
+w32_buffer_flipping_unblocked_hook (struct frame *f)
+{
+  w32_show_back_buffer (f, TRUE);
+}
 
 /* Draw truncation mark bitmaps, continuation mark bitmaps, overlay
    arrow bitmaps, or clear the fringes if no bitmaps are required
@@ -2872,8 +2910,7 @@ w32_scroll_run (struct window *w, struct run *run)
 {
   struct frame *f = XFRAME (w->frame);
   int x, y, width, height, from_y, to_y, bottom_y;
-  HWND hwnd = FRAME_W32_WINDOW (f);
-  HRGN expect_dirty;
+  HDC hdc;
 
   /* Get frame-relative bounding box of the text display area of W,
      without mode lines.  Include in this box the left and right
@@ -2892,7 +2929,6 @@ w32_scroll_run (struct window *w, struct run *run)
        height = bottom_y - from_y;
       else
        height = run->height;
-      expect_dirty = CreateRectRgn (x, y + height, x + width, bottom_y);
     }
   else
     {
@@ -2902,44 +2938,16 @@ w32_scroll_run (struct window *w, struct run *run)
        height = bottom_y - to_y;
       else
        height = run->height;
-      expect_dirty = CreateRectRgn (x, y, x + width, to_y);
     }
 
   block_input ();
-
   /* Cursor off.  Will be switched on again in gui_update_window_end.  */
   gui_clear_cursor (w);
-
-  {
-    RECT from;
-    RECT to;
-    HRGN dirty = CreateRectRgn (0, 0, 0, 0);
-    HRGN combined = CreateRectRgn (0, 0, 0, 0);
-
-    from.left = to.left = x;
-    from.right = to.right = x + width;
-    from.top = from_y;
-    from.bottom = from_y + height;
-    to.top = y;
-    to.bottom = bottom_y;
-
-    ScrollWindowEx (hwnd, 0, to_y - from_y, &from, &to, dirty,
-                   NULL, SW_INVALIDATE);
-
-    /* Combine this with what we expect to be dirty. This covers the
-       case where not all of the region we expect is actually dirty.  */
-    CombineRgn (combined, dirty, expect_dirty, RGN_OR);
-
-    /* If the dirty region is not what we expected, redraw the entire frame.  
*/
-    if (!EqualRgn (combined, expect_dirty))
-      SET_FRAME_GARBAGED (f);
-
-    DeleteObject (dirty);
-    DeleteObject (combined);
-  }
-
+  hdc = get_frame_dc (f);
+  BitBlt (hdc, x, from_y, width, height,
+         hdc, x, to_y, SRCCOPY);
+  release_frame_dc (f, hdc);
   unblock_input ();
-  DeleteObject (expect_dirty);
 }
 
 
@@ -5659,6 +5667,10 @@ w32_read_socket (struct terminal *terminal,
                  if (width != FRAME_PIXEL_WIDTH (f)
                      || height != FRAME_PIXEL_HEIGHT (f))
                    {
+                     /* Delete the back buffer so it gets created
+                        again the next time we ask for the DC.  */
+                     w32_show_back_buffer (f, FALSE);
+
                      change_frame_size
                        (f, width, height, false, true, false);
                      SET_FRAME_GARBAGED (f);
@@ -7350,6 +7362,7 @@ w32_create_terminal (struct w32_display_info *dpyinfo)
   terminal->update_end_hook = w32_update_end;
   terminal->read_socket_hook = w32_read_socket;
   terminal->frame_up_to_date_hook = w32_frame_up_to_date;
+  terminal->buffer_flipping_unblocked_hook = 
w32_buffer_flipping_unblocked_hook;
   terminal->defined_color_hook = w32_defined_color;
   terminal->query_frame_background_color = w32_query_frame_background_color;
   terminal->query_colors = w32_query_colors;
@@ -7430,6 +7443,8 @@ w32_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
       w32_initialized = 1;
     }
 
+  BufferedPaintInit ();
+
   w32_initialize_display_info (display_name);
 
   dpyinfo = &one_w32_display_info;
@@ -7505,6 +7520,8 @@ w32_delete_display (struct w32_display_info *dpyinfo)
     if (dpyinfo->palette)
       DeleteObject (dpyinfo->palette);
   }
+
+  BufferedPaintUnInit ();
   w32_reset_fringes ();
 }
 
diff --git a/src/w32term.h b/src/w32term.h
index 6c48323651..5a4b6e627f 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -412,6 +412,13 @@ #define PIX_TYPE COLORREF
      geometry when 'fullscreen' is reset to nil.  */
   WINDOWPLACEMENT normal_placement;
   int prev_fsmode;
+
+  /* The paint buffer if there is an ongoing double-buffered drawing
+     operation.  */
+  HPAINTBUFFER paint_buffer;
+
+  /* The handle of the paint buffer.  */
+  HDC paint_buffer_desc, paint_dc;
 };
 
 extern struct w32_output w32term_display;
diff --git a/src/w32xfns.c b/src/w32xfns.c
index d5974b906e..320b8619b2 100644
--- a/src/w32xfns.c
+++ b/src/w32xfns.c
@@ -136,13 +136,13 @@ select_palette (struct frame *f, HDC hdc)
     f->output_data.w32->old_palette = NULL;
 
   if (RealizePalette (hdc) != GDI_ERROR)
-  {
-    Lisp_Object frame, framelist;
-    FOR_EACH_FRAME (framelist, frame)
     {
-      SET_FRAME_GARBAGED (XFRAME (frame));
+      Lisp_Object frame, framelist;
+      FOR_EACH_FRAME (framelist, frame)
+       {
+         SET_FRAME_GARBAGED (XFRAME (frame));
+       }
     }
-  }
 }
 
 void
@@ -157,19 +157,47 @@ deselect_palette (struct frame *f, HDC hdc)
 HDC
 get_frame_dc (struct frame *f)
 {
-  HDC hdc;
+  HDC hdc, dest;
+  HPAINTBUFFER paint_buffer;
+  RECT r;
 
   if (f->output_method != output_w32)
     emacs_abort ();
 
   enter_crit ();
 
-  hdc = GetDC (f->output_data.w32->window_desc);
+  if (FRAME_OUTPUT_DATA (f)->paint_buffer_desc)
+    return FRAME_OUTPUT_DATA (f)->paint_buffer_desc;
+
+  hdc = GetDC (FRAME_OUTPUT_DATA (f)->window_desc);
 
   /* If this gets called during startup before the frame is valid,
      there is a chance of corrupting random data or crashing. */
   if (hdc)
-    select_palette (f, hdc);
+    {
+      select_palette (f, hdc);
+
+      r.left = 0;
+      r.top = 0;
+      r.right = FRAME_PIXEL_WIDTH (f) - 1;
+      r.bottom = FRAME_PIXEL_HEIGHT (f) - 1;
+
+      paint_buffer = BeginBufferedPaint (hdc, &r,
+                                        BPBF_COMPATIBLEBITMAP,
+                                        NULL, &dest);
+
+      if (paint_buffer)
+       {
+         FRAME_OUTPUT_DATA (f)->paint_buffer = paint_buffer;
+         FRAME_OUTPUT_DATA (f)->paint_buffer_desc = dest;
+         FRAME_OUTPUT_DATA (f)->paint_dc = hdc;
+
+         BitBlt (dest, 0, 0, FRAME_PIXEL_WIDTH (f),
+                 FRAME_PIXEL_HEIGHT (f), hdc, 0, 0, SRCCOPY);
+
+         return dest;
+       }
+    }
 
   return hdc;
 }
@@ -179,8 +207,15 @@ release_frame_dc (struct frame *f, HDC hdc)
 {
   int ret;
 
-  deselect_palette (f, hdc);
-  ret = ReleaseDC (f->output_data.w32->window_desc, hdc);
+  /* Avoid releasing the double-buffered DC here, since it'll be
+     released upon the next buffer flip instead.  */
+  if (hdc != FRAME_OUTPUT_DATA (f)->paint_dc)
+    {
+      deselect_palette (f, hdc);
+      ret = ReleaseDC (f->output_data.w32->window_desc, hdc);
+    }
+  else
+    ret = 0;
 
   leave_crit ();
 


reply via email to

[Prev in Thread] Current Thread [Next in Thread]