emacs-devel
[Top][All Lists]
Advanced

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

Re: MS Windows double buffering


From: Po Lu
Subject: Re: MS Windows double buffering
Date: Thu, 28 Apr 2022 20:45:04 +0800
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.91 (gnu/linux)

> 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 via email to

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