unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Po Lu <luangruo@yahoo.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: emacs-devel@gnu.org
Subject: Re: MS Windows double buffering
Date: Thu, 28 Apr 2022 20:45:04 +0800	[thread overview]
Message-ID: <87sfpxz8a7.fsf@yahoo.com> (raw)
In-Reply-To: <83ilqtbl3p.fsf@gnu.org> (Eli Zaretskii's message of "Thu, 28 Apr 2022 12:42:02 +0300")


> Seconded.

Right... since the best way to get something done fast is to do it
yourself, I tried my best to fix all the issues mentioned above.  It
should work on old versions of MS Windows now as well.  If this works
for you, I'll install this after implementing the
`inhibit-double-buffering' frame parameter.  Thanks.

diff --git a/src/w32term.c b/src/w32term.c
index 7837032304..245d46a499 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -275,6 +275,57 @@ XGetGCValues (void *ignore, XGCValues *gc,
 }
 #endif
 
+static void
+w32_show_back_buffer (struct frame *f)
+{
+  struct w32_output *output;
+  HDC raw_dc;
+
+  output = FRAME_OUTPUT_DATA (f);
+
+  enter_crit ();
+
+  if (output->paint_buffer)
+    {
+      raw_dc = GetDC (output->window_desc);
+
+      if (!raw_dc)
+	emacs_abort ();
+
+      BitBlt (raw_dc, 0, 0, FRAME_PIXEL_WIDTH (f),
+	      FRAME_PIXEL_HEIGHT (f),
+	      output->paint_dc, 0, 0, SRCCOPY);
+      ReleaseDC (output->window_desc, raw_dc);
+
+      output->paint_buffer_dirty = 0;
+    }
+
+  leave_crit ();
+}
+
+void
+w32_release_paint_buffer (struct frame *f)
+{
+  /* Delete the back buffer so it gets created
+     again the next time we ask for the DC.  */
+
+  enter_crit ();
+  if (FRAME_OUTPUT_DATA (f)->paint_buffer)
+    {
+      SelectObject (FRAME_OUTPUT_DATA (f)->paint_dc,
+		    FRAME_OUTPUT_DATA (f)->paint_dc_object);
+      ReleaseDC (FRAME_OUTPUT_DATA (f)->window_desc,
+		 FRAME_OUTPUT_DATA (f)->paint_buffer_handle);
+      DeleteDC (FRAME_OUTPUT_DATA (f)->paint_dc);
+      DeleteObject (FRAME_OUTPUT_DATA (f)->paint_buffer);
+
+      FRAME_OUTPUT_DATA (f)->paint_buffer = NULL;
+      FRAME_OUTPUT_DATA (f)->paint_dc = NULL;
+      FRAME_OUTPUT_DATA (f)->paint_buffer_handle = NULL;
+    }
+  leave_crit ();
+}
+
 static void
 w32_get_mouse_wheel_vertical_delta (void)
 {
@@ -704,10 +755,18 @@ w32_update_end (struct frame *f)
 static void
 w32_frame_up_to_date (struct frame *f)
 {
-  if (FRAME_W32_P (f))
-    FRAME_MOUSE_UPDATE (f);
+  FRAME_MOUSE_UPDATE (f);
+
+  if (!buffer_flipping_blocked_p ()
+      && FRAME_OUTPUT_DATA (f)->paint_buffer_dirty)
+    w32_show_back_buffer (f);
 }
 
+static void
+w32_buffer_flipping_unblocked_hook (struct frame *f)
+{
+  w32_show_back_buffer (f);
+}
 
 /* Draw truncation mark bitmaps, continuation mark bitmaps, overlay
    arrow bitmaps, or clear the fringes if no bitmaps are required
@@ -2872,8 +2931,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 +2950,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 +2959,15 @@ 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, to_y, width, height, hdc, x, from_y, SRCCOPY);
+  release_frame_dc (f, hdc);
   unblock_input ();
-  DeleteObject (expect_dirty);
 }
 
 
@@ -4928,6 +4956,8 @@ w32_read_socket (struct terminal *terminal,
       /*            w32_name_of_message (msg.msg.message), */
       /*            msg.msg.time)); */
 
+      f = NULL;
+
       EVENT_INIT (inev);
       inev.kind = NO_EVENT;
       inev.arg = Qnil;
@@ -5659,6 +5689,8 @@ w32_read_socket (struct terminal *terminal,
 		  if (width != FRAME_PIXEL_WIDTH (f)
 		      || height != FRAME_PIXEL_HEIGHT (f))
 		    {
+		      w32_release_paint_buffer (f);
+
 		      change_frame_size
 			(f, width, height, false, true, false);
 		      SET_FRAME_GARBAGED (f);
@@ -5840,6 +5872,14 @@ w32_read_socket (struct terminal *terminal,
 	    }
 	  count++;
 	}
+
+      /* Event processing might have drawn to F outside redisplay.  If
+         that is the case, flush any changes that have been made to
+         the front buffer.  */
+
+      if (f && FRAME_OUTPUT_DATA (f)->paint_buffer_dirty
+	  && !f->garbaged)
+	w32_show_back_buffer (f);
     }
 
   /* If the focus was just given to an autoraising frame,
@@ -7054,6 +7094,9 @@ w32_free_frame_resources (struct frame *f)
      face.  */
   free_frame_faces (f);
 
+  /* Now release the back buffer if any exists.  */
+  w32_release_paint_buffer (f);
+
   if (FRAME_W32_WINDOW (f))
     my_destroy_window (f, FRAME_W32_WINDOW (f));
 
@@ -7350,6 +7393,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;
@@ -7505,6 +7549,7 @@ w32_delete_display (struct w32_display_info *dpyinfo)
     if (dpyinfo->palette)
       DeleteObject (dpyinfo->palette);
   }
+
   w32_reset_fringes ();
 }
 
diff --git a/src/w32term.h b/src/w32term.h
index 6c48323651..7df270eb5d 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -412,6 +412,24 @@ #define PIX_TYPE COLORREF
      geometry when 'fullscreen' is reset to nil.  */
   WINDOWPLACEMENT normal_placement;
   int prev_fsmode;
+
+  /* The back buffer if there is an ongoing double-buffered drawing
+     operation.  */
+  HBITMAP paint_buffer;
+
+  /* The handle of the back buffer and a DC that ought to be released
+     alongside the back buffer.  */
+  HDC paint_dc, paint_buffer_handle;
+
+  /* The object previously selected into `paint_dc'.  */
+  HGDIOBJ paint_dc_object;
+
+  /* The width and height of `paint_buffer'.  */
+  int paint_buffer_width, paint_buffer_height;
+
+  /* Whether or not some painting was done to this window that has not
+     yet been drawn.  */
+  unsigned paint_buffer_dirty : 1;
 };
 
 extern struct w32_output w32term_display;
@@ -876,6 +894,7 @@ #define GUI_SDATA(x) ((guichar_t*) SDATA (x))
 
 extern Lisp_Object w32_popup_dialog (struct frame *, Lisp_Object, Lisp_Object);
 extern void w32_arrow_cursor (void);
+extern void w32_release_paint_buffer (struct frame *);
 
 extern void syms_of_w32term (void);
 extern void syms_of_w32menu (void);
diff --git a/src/w32xfns.c b/src/w32xfns.c
index d5974b906e..e08b3519d3 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,65 @@ deselect_palette (struct frame *f, HDC hdc)
 HDC
 get_frame_dc (struct frame *f)
 {
-  HDC hdc;
+  HDC hdc, paint_dc;
+  HBITMAP back_buffer;
+  HGDIOBJ obj;
+  struct w32_output *output;
 
   if (f->output_method != output_w32)
     emacs_abort ();
 
   enter_crit ();
+  output = FRAME_OUTPUT_DATA (f);
+
+  if (output->paint_dc)
+    {
+      if (output->paint_buffer_width != FRAME_PIXEL_WIDTH (f)
+	  || output->paint_buffer_height != FRAME_PIXEL_HEIGHT (f))
+	w32_release_paint_buffer (f);
+      else
+	{
+	  output->paint_buffer_dirty = 1;
+	  return output->paint_dc;
+	}
+    }
 
-  hdc = GetDC (f->output_data.w32->window_desc);
+  hdc = GetDC (output->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);
+
+      back_buffer
+	= CreateCompatibleBitmap (hdc, FRAME_PIXEL_WIDTH (f),
+				  FRAME_PIXEL_HEIGHT (f));
+
+      if (back_buffer)
+	{
+	  paint_dc = CreateCompatibleDC (hdc);
+
+	  if (!paint_dc)
+	    DeleteObject (back_buffer);
+	  else
+	    {
+	      obj = SelectObject (paint_dc, back_buffer);
+
+	      output->paint_dc_object = obj;
+	      output->paint_dc = paint_dc;
+	      output->paint_buffer_handle = hdc;
+	      output->paint_buffer = back_buffer;
+	      output->paint_buffer_width = FRAME_PIXEL_WIDTH (f);
+	      output->paint_buffer_height = FRAME_PIXEL_HEIGHT (f);
+	      output->paint_buffer_dirty = 1;
+
+	      SET_FRAME_GARBAGED (f);
+
+	      return paint_dc;
+	    }
+	}
+    }
 
   return hdc;
 }
@@ -179,8 +225,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 12:45 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 ` MS Windows double buffering Po Lu
2022-04-28  9:42   ` Eli Zaretskii
2022-04-28 12:45     ` Po Lu [this message]
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=87sfpxz8a7.fsf@yahoo.com \
    --to=luangruo@yahoo.com \
    --cc=eliz@gnu.org \
    --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).