unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Po Lu <luangruo@yahoo.com>
To: emacs-devel@gnu.org
Subject: MS Windows double buffering
Date: Thu, 28 Apr 2022 16:51:06 +0800	[thread overview]
Message-ID: <877d791thh.fsf@yahoo.com> (raw)
In-Reply-To: 877d791thh.fsf.ref@yahoo.com

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	other threads:[~2022-04-28  8:51 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <877d791thh.fsf.ref@yahoo.com>
2022-04-28  8:51 ` Po Lu [this message]
2022-04-28  9:42   ` MS Windows double buffering Eli Zaretskii
2022-04-28 12:45     ` Po Lu
2022-04-29  3:36       ` Po Lu
2022-04-30  5:41         ` Po Lu
2022-04-30  6:50           ` Eli Zaretskii
2022-04-30  7:46             ` Po Lu
2022-04-30  9:10               ` Eli Zaretskii
2022-04-30  9:55                 ` Po Lu
2022-04-30 10:23                   ` Eli Zaretskii
2022-04-30 10:33                     ` Po Lu
2022-04-30 10:55                       ` Eli Zaretskii
2022-04-30 11:01                         ` Po Lu
2022-04-30 11:32                           ` Eli Zaretskii
2022-04-30 11:54                             ` Po Lu
2022-05-03  8:23               ` Robert Pluim
2022-05-03  8:43                 ` Po Lu
2022-04-30 17:34             ` Ken Brown
2022-04-30 18:25               ` Eli Zaretskii
2022-05-01  0:35               ` Po Lu
2022-05-01 16:00               ` Arash Esbati
2022-05-01 16:07                 ` Eli Zaretskii
2022-05-01 16:11                   ` Arash Esbati

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=877d791thh.fsf@yahoo.com \
    --to=luangruo@yahoo.com \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).