From 1321b5989c9a8046ce40b8edf23e3e471d2b334a Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 30 Apr 2022 15:49:06 +0800 Subject: [PATCH] Adjustments to double buffering on MS Windows * src/w32fns.c (w32_set_inhibit_double_buffering): Add comment describing double buffering. (w32_wnd_proc): Respect `w32-disable-double-buffering'. (globals_of_w32fns): New variable `w32-disable-double-buffering'. * src/w32term.c (w32_show_back_buffer): Return immediately if double buffering is disabled on the frame. (w32_scroll_run): Use old scrolling code if `w32-disable-double-buffering' is enabled. (w32_scroll_bar_clear): Document why we don't clear scroll bars when double buffering is enabled. (w32_read_socket): Respect `w32-disable-double-buffering' and clean up some code. * src/w32xfns.c (get_frame_dc): Respect `w32-disable-double-buffering'. --- src/w32fns.c | 16 ++++++++++- src/w32term.c | 80 +++++++++++++++++++++++++++++++++++++++++++-------- src/w32xfns.c | 6 ++-- 3 files changed, 87 insertions(+), 15 deletions(-) diff --git a/src/w32fns.c b/src/w32fns.c index d4e4b2a30bf..fb71fecd95a 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -1802,9 +1802,16 @@ w32_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) w32_change_tool_bar_height (f, nlines * FRAME_LINE_HEIGHT (f)); } +/* Enable or disable double buffering on F. + + When double buffering is enabled, all drawing happens on a back + buffer (a bitmap), which is then displayed as a single operation + after redisplay is complete. This avoids flicker caused by the + results of an incomplete redisplay becoming visible. */ static void w32_set_inhibit_double_buffering (struct frame *f, Lisp_Object new_value, + /* This parameter is unused. */ Lisp_Object old_value) { block_input (); @@ -4114,7 +4121,8 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) f = w32_window_to_frame (dpyinfo, hwnd); enter_crit (); - if (f && !FRAME_OUTPUT_DATA (f)->paint_buffer) + if (f && (w32_disable_double_buffering + || !FRAME_OUTPUT_DATA (f)->paint_buffer)) { HDC hdc = get_frame_dc (f); GetUpdateRect (hwnd, &wmsg.rect, FALSE); @@ -11236,6 +11244,12 @@ see `w32-ansi-code-page'. */); w32_multibyte_code_page = _getmbcp (); #endif + DEFVAR_BOOL ("w32-disable-double-buffering", w32_disable_double_buffering, + doc: /* Completely disable double buffering. +This variable is used for debugging, and takes precedence over any +value of the `inhibit-double-buffering' frame parameter. */); + w32_disable_double_buffering = false; + if (os_subtype == OS_SUBTYPE_NT) w32_unicode_gui = 1; else diff --git a/src/w32term.c b/src/w32term.c index 205ac749669..8a98d2f1c67 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -283,6 +283,9 @@ w32_show_back_buffer (struct frame *f) output = FRAME_OUTPUT_DATA (f); + if (!output->want_paint_buffer || w32_disable_double_buffering) + return; + enter_crit (); if (output->paint_buffer) @@ -2935,6 +2938,8 @@ 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; HDC hdc; + HWND hwnd = FRAME_W32_WINDOW (f); + HRGN expect_dirty = NULL; /* Get frame-relative bounding box of the text display area of W, without mode lines. Include in this box the left and right @@ -2953,6 +2958,9 @@ w32_scroll_run (struct window *w, struct run *run) height = bottom_y - from_y; else height = run->height; + + if (w32_disable_double_buffering) + expect_dirty = CreateRectRgn (x, y + height, x + width, bottom_y); } else { @@ -2962,15 +2970,55 @@ w32_scroll_run (struct window *w, struct run *run) height = bottom_y - to_y; else height = run->height; + + if (w32_disable_double_buffering) + 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); - hdc = get_frame_dc (f); - BitBlt (hdc, x, to_y, width, height, hdc, x, from_y, SRCCOPY); - release_frame_dc (f, hdc); + if (!w32_disable_double_buffering) + { + hdc = get_frame_dc (f); + BitBlt (hdc, x, to_y, width, height, hdc, x, from_y, SRCCOPY); + release_frame_dc (f, hdc); + } + else + { + 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); + } + unblock_input (); + + if (w32_disable_double_buffering + && expect_dirty) + DeleteObject (expect_dirty); } @@ -4840,7 +4888,12 @@ w32_scroll_bar_clear (struct frame *f) { Lisp_Object bar; - if (FRAME_OUTPUT_DATA (f)->paint_buffer) + /* Return if double buffering is enabled, since clearing a frame + actually clears just the back buffer, so avoid clearing all of + the scroll bars, since that causes the scroll bars to + flicker. */ + if (!w32_disable_double_buffering + && FRAME_OUTPUT_DATA (f)->want_paint_buffer) return; /* We can have scroll bars even if this is 0, @@ -4958,6 +5011,11 @@ w32_read_socket (struct terminal *terminal, struct input_event inev; int do_help = 0; + /* WM_WINDOWPOSCHANGED makes the buffer dirty, but there's no + reason to flush the back buffer after receiving such an + event, and that also causes flicker. */ + bool ignore_dirty_back_buffer = false; + /* DebPrint (("w32_read_socket: %s time:%u\n", */ /* w32_name_of_message (msg.msg.message), */ /* msg.msg.time)); */ @@ -5005,8 +5063,8 @@ w32_read_socket (struct terminal *terminal, } else { - enter_crit (); - if (!FRAME_OUTPUT_DATA (f)->paint_buffer) + if (w32_disable_double_buffering + || !FRAME_OUTPUT_DATA (f)->paint_buffer) { /* Erase background again for safety. But don't do that if the frame's 'garbaged' flag is set, since @@ -5031,7 +5089,6 @@ w32_read_socket (struct terminal *terminal, } else w32_show_back_buffer (f); - leave_crit (); } } break; @@ -5484,6 +5541,7 @@ w32_read_socket (struct terminal *terminal, case WM_WINDOWPOSCHANGED: f = w32_window_to_frame (dpyinfo, msg.msg.hwnd); + ignore_dirty_back_buffer = true; if (f) { @@ -5894,11 +5952,9 @@ w32_read_socket (struct terminal *terminal, that is the case, flush any changes that have been made to the front buffer. */ - if (f && FRAME_OUTPUT_DATA (f)->paint_buffer_dirty - /* WM_WINDOWPOSCHANGED makes the buffer dirty, but there's - no reason to flush it here, and that also causes - flicker. */ - && !f->garbaged && msg.msg.message != WM_WINDOWPOSCHANGED) + if (f && !w32_disable_double_buffering + && FRAME_OUTPUT_DATA (f)->paint_buffer_dirty + && !f->garbaged && ignore_dirty_back_buffer) w32_show_back_buffer (f); } diff --git a/src/w32xfns.c b/src/w32xfns.c index 139985c5bdb..22d39ae0037 100644 --- a/src/w32xfns.c +++ b/src/w32xfns.c @@ -171,7 +171,8 @@ get_frame_dc (struct frame *f) if (output->paint_dc) { if (output->paint_buffer_width != FRAME_PIXEL_WIDTH (f) - || output->paint_buffer_height != FRAME_PIXEL_HEIGHT (f)) + || output->paint_buffer_height != FRAME_PIXEL_HEIGHT (f) + || w32_disable_double_buffering) w32_release_paint_buffer (f); else { @@ -188,7 +189,8 @@ get_frame_dc (struct frame *f) { select_palette (f, hdc); - if (FRAME_OUTPUT_DATA (f)->want_paint_buffer) + if (!w32_disable_double_buffering + && FRAME_OUTPUT_DATA (f)->want_paint_buffer) { back_buffer = CreateCompatibleBitmap (hdc, FRAME_PIXEL_WIDTH (f), -- 2.39.2