commit 0ce48e2882ad73925f9b524d879d8e57909e6d38 (HEAD, refs/remotes/origin/master) Author: Po Lu Date: Sat Apr 30 14:29:33 2022 +0800 Handle exposure in the widget's expose proc on X * src/widget.c (emacsFrameClassRec): Don't inherit expose proc. (get_default_char_pixel_size): (pixel_to_char_size): (char_to_pixel_size): (round_size_to_char): (EmacsFrameInitialize): (EmacsFrameRealize): (EmacsFrameResize): Clean up coding style. (EmacsFrameExpose): New function. Expose the frame here to satisfy the toolkit when it calls the expose proc by hand. * src/xterm.c (handle_one_xevent): Handle exposure through the widget instead. diff --git a/src/widget.c b/src/widget.c index 4231aa71b5..b125b4caee 100644 --- a/src/widget.c +++ b/src/widget.c @@ -42,11 +42,13 @@ along with GNU Emacs. If not, see . */ #include #include "../lwlib/lwlib.h" -static void EmacsFrameInitialize (Widget request, Widget new, ArgList dum1, Cardinal *dum2); -static void EmacsFrameDestroy (Widget widget); -static void EmacsFrameRealize (Widget widget, XtValueMask *mask, XSetWindowAttributes *attrs); -static void EmacsFrameResize (Widget widget); -static XtGeometryResult EmacsFrameQueryGeometry (Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *result); +static void EmacsFrameInitialize (Widget, Widget, ArgList, Cardinal *); +static void EmacsFrameDestroy (Widget); +static void EmacsFrameRealize (Widget, XtValueMask *, XSetWindowAttributes *); +static void EmacsFrameResize (Widget); +static void EmacsFrameExpose (Widget, XEvent *, Region); +static XtGeometryResult EmacsFrameQueryGeometry (Widget, XtWidgetGeometry *, + XtWidgetGeometry *); #define offset(field) offsetof (EmacsFrameRec, emacs_frame.field) @@ -118,12 +120,12 @@ static EmacsFrameClassRec emacsFrameClassRec = { /* resource_count */ XtNumber (resources), /* xrm_class */ NULLQUARK, /* compress_motion */ TRUE, - /* compress_exposure */ TRUE, + /* compress_exposure */ XtExposeNoCompress, /* compress_enterleave */ TRUE, /* visible_interest */ FALSE, /* destroy */ EmacsFrameDestroy, /* resize */ EmacsFrameResize, - /* expose */ XtInheritExpose, + /* expose */ EmacsFrameExpose, /* Emacs never does XtSetvalues on this widget, so we have no code for it. */ @@ -156,33 +158,41 @@ static void get_default_char_pixel_size (EmacsFrame ew, int *pixel_width, int *pixel_height) { struct frame *f = ew->emacs_frame.frame; + *pixel_width = FRAME_COLUMN_WIDTH (f); *pixel_height = FRAME_LINE_HEIGHT (f); } static void -pixel_to_char_size (EmacsFrame ew, Dimension pixel_width, Dimension pixel_height, int *char_width, int *char_height) +pixel_to_char_size (EmacsFrame ew, Dimension pixel_width, + Dimension pixel_height, int *char_width, int *char_height) { struct frame *f = ew->emacs_frame.frame; + *char_width = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, (int) pixel_width); *char_height = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, (int) pixel_height); } static void -char_to_pixel_size (EmacsFrame ew, int char_width, int char_height, Dimension *pixel_width, Dimension *pixel_height) +char_to_pixel_size (EmacsFrame ew, int char_width, int char_height, + Dimension *pixel_width, Dimension *pixel_height) { struct frame *f = ew->emacs_frame.frame; + *pixel_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, char_width); *pixel_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, char_height); } static void -round_size_to_char (EmacsFrame ew, Dimension in_width, Dimension in_height, Dimension *out_width, Dimension *out_height) +round_size_to_char (EmacsFrame ew, Dimension in_width, Dimension in_height, + Dimension *out_width, Dimension *out_height) { int char_width; int char_height; - pixel_to_char_size (ew, in_width, in_height, &char_width, &char_height); - char_to_pixel_size (ew, char_width, char_height, out_width, out_height); + pixel_to_char_size (ew, in_width, in_height, + &char_width, &char_height); + char_to_pixel_size (ew, char_width, char_height, + out_width, out_height); } static Widget @@ -334,7 +344,8 @@ update_from_various_frame_slots (EmacsFrame ew) } static void -EmacsFrameInitialize (Widget request, Widget new, ArgList dum1, Cardinal *dum2) +EmacsFrameInitialize (Widget request, Widget new, + ArgList dum1, Cardinal *dum2) { EmacsFrame ew = (EmacsFrame) new; @@ -359,7 +370,8 @@ resize_cb (Widget widget, static void -EmacsFrameRealize (Widget widget, XtValueMask *mask, XSetWindowAttributes *attrs) +EmacsFrameRealize (Widget widget, XtValueMask *mask, + XSetWindowAttributes *attrs) { EmacsFrame ew = (EmacsFrame) widget; struct frame *f = ew->emacs_frame.frame; @@ -404,7 +416,8 @@ EmacsFrameResize (Widget widget) ew->core.width, ew->core.height, f->new_width, f->new_height); - change_frame_size (f, ew->core.width, ew->core.height, false, true, false); + change_frame_size (f, ew->core.width, ew->core.height, + false, true, false); if (get_wm_shell (widget)) update_wm_hints (get_wm_shell (widget), ew); @@ -462,6 +475,17 @@ EmacsFrameSetCharSize (Widget widget, int columns, int rows) rows * FRAME_LINE_HEIGHT (f)); } +static void +EmacsFrameExpose (Widget widget, XEvent *event, Region region) +{ + EmacsFrame ew = (EmacsFrame) widget; + struct frame *f = ew->emacs_frame.frame; + + expose_frame (f, event->xexpose.x, event->xexpose.y, + event->xexpose.width, event->xexpose.height); + flush_frame (f); +} + void widget_store_internal_border (Widget widget) diff --git a/src/xterm.c b/src/xterm.c index d442837bc6..e52f19a8e3 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -14680,6 +14680,12 @@ handle_one_xevent (struct x_display_info *dpyinfo, if (!FRAME_GARBAGED_P (f)) { +#ifdef USE_X_TOOLKIT + if (f->output_data.x->edit_widget) + /* The widget's expose proc will be run in this + case. */ + goto OTHER; +#endif #ifdef USE_GTK /* This seems to be needed for GTK 2.6 and later, see https://debbugs.gnu.org/cgi/bugreport.cgi?bug=15398. */ commit 4fb028be6c2de5a556f56bb40a3f948a143cef37 Author: Po Lu Date: Sat Apr 30 13:26:40 2022 +0800 Fix releasing the mouse on top of the tool bar on MS Windows * src/w32term.c (w32_read_socket): Don't handle tool bar clicks specially for button up events if no tool bar item was previously pressed. diff --git a/src/w32term.c b/src/w32term.c index 1937f94645..205ac74966 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -5365,7 +5365,9 @@ w32_read_socket (struct terminal *terminal, window = window_from_coordinates (f, x, y, 0, 1, 1); - if (EQ (window, f->tool_bar_window)) + if (EQ (window, f->tool_bar_window) + && (inev.modifiers & down_modifier + || f->last_tool_bar_item != -1)) { w32_handle_tool_bar_click (f, &inev); tool_bar_p = 1; commit 402c90c912bfb96a8826854f7e2c1becb085a973 Author: Po Lu Date: Sat Apr 30 11:33:38 2022 +0800 Fix palette freeing on MS Windows with double buffering * src/w32term.c (w32_release_paint_buffer): Also release target DC palette. diff --git a/src/w32term.c b/src/w32term.c index ca96320a5e..1937f94645 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -312,6 +312,8 @@ w32_release_paint_buffer (struct frame *f) enter_crit (); if (FRAME_OUTPUT_DATA (f)->paint_buffer) { + deselect_palette (f, FRAME_OUTPUT_DATA (f)->paint_buffer_handle); + SelectObject (FRAME_OUTPUT_DATA (f)->paint_dc, FRAME_OUTPUT_DATA (f)->paint_dc_object); ReleaseDC (FRAME_OUTPUT_DATA (f)->window_desc, commit bc44455f778a256a861c8063e87a662a13f603e1 Author: Po Lu Date: Fri Apr 29 11:33:41 2022 +0800 Implement double buffering on MS Windows * etc/NEWS: Announce changes. * src/w32fns.c (w32_set_inhibit_double_buffering): New function. (w32_wnd_proc): (Fx_create_frame): (w32_create_tip_frame): Set `inhibit-double-buffering' parameter. (w32_frame_parm_handlers): Add new handler. * src/w32term.c (w32_show_back_buffer): (w32_release_paint_buffer): New functions. (w32_frame_up_to_date): Show back buffer if applicable. (w32_buffer_flipping_unblocked_hook): New hook. (w32_scroll_run): Use BitBlt to scroll instead of window scrolling functions. (w32_scroll_bar_clear): Don't clear scroll bars when double buffered. (w32_read_socket): Flip buffers after reading input events in some cases. (w32_free_frame_resources): Free back buffer. (w32_create_terminal): Add new hook. * src/w32term.h (struct w32_output): New fields for handling back buffers. * src/w32xfns.c (select_palette): Fix indentation. (get_frame_dc, release_frame_dc): Return back buffer when appropriate and set dirty flag. diff --git a/etc/NEWS b/etc/NEWS index c796f605b1..5c2f152a12 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2146,6 +2146,13 @@ to preserve the old behavior, apply ** MS-Windows +--- +*** Emacs now supports double buffering on MS Windows to reduce flicker. +This leads to a noticable reduction in the amount of graphics flicker +during redisplay on many systems, but can also make painting slower. +If that happens, it can be disabled by setting the +'inhibit-double-buffering' frame parameter. + +++ *** Emacs now supports system dark mode. On Windows 10 (version 1809 and higher) and Windows 11, Emacs will now diff --git a/src/w32fns.c b/src/w32fns.c index a880136d0a..d4e4b2a30b 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -1802,6 +1802,25 @@ 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)); } +static void +w32_set_inhibit_double_buffering (struct frame *f, + Lisp_Object new_value, + Lisp_Object old_value) +{ + block_input (); + + if (NILP (new_value)) + FRAME_OUTPUT_DATA (f)->want_paint_buffer = 1; + else + { + FRAME_OUTPUT_DATA (f)->want_paint_buffer = 0; + w32_release_paint_buffer (f); + + SET_FRAME_GARBAGED (f); + } + + unblock_input (); +} /* Set the pixel height of the tool bar of frame F to HEIGHT. */ void @@ -4093,7 +4112,9 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { case WM_ERASEBKGND: f = w32_window_to_frame (dpyinfo, hwnd); - if (f) + + enter_crit (); + if (f && !FRAME_OUTPUT_DATA (f)->paint_buffer) { HDC hdc = get_frame_dc (f); GetUpdateRect (hwnd, &wmsg.rect, FALSE); @@ -4107,6 +4128,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) wmsg.rect.right, wmsg.rect.bottom)); #endif /* W32_DEBUG_DISPLAY */ } + leave_crit (); return 1; case WM_PALETTECHANGED: /* ignore our own changes */ @@ -6080,6 +6102,10 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, ? make_fixnum (0) : make_fixnum (1), NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parameters, Qinhibit_double_buffering, Qnil, + "inhibitDoubleBuffering", "InhibitDoubleBuffering", + RES_TYPE_BOOLEAN); + gui_default_parameter (f, parameters, Qbuffer_predicate, Qnil, "bufferPredicate", "BufferPredicate", RES_TYPE_SYMBOL); gui_default_parameter (f, parameters, Qtitle, Qnil, @@ -7096,6 +7122,9 @@ w32_create_tip_frame (struct w32_display_info *dpyinfo, Lisp_Object parms) "alpha", "Alpha", RES_TYPE_NUMBER); gui_default_parameter (f, parms, Qalpha_background, Qnil, "alphaBackground", "AlphaBackground", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil, + "inhibitDoubleBuffering", "InhibitDoubleBuffering", + RES_TYPE_BOOLEAN); /* Add `tooltip' frame parameter's default value. */ if (NILP (Fframe_parameter (frame, Qtooltip))) @@ -10432,7 +10461,7 @@ frame_parm_handler w32_frame_parm_handlers[] = gui_set_alpha, 0, /* x_set_sticky */ 0, /* x_set_tool_bar_position */ - 0, /* x_set_inhibit_double_buffering */ + w32_set_inhibit_double_buffering, w32_set_undecorated, w32_set_parent_frame, w32_set_skip_taskbar, diff --git a/src/w32term.c b/src/w32term.c index 7837032304..ca96320a5e 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,19 @@ 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) +{ + if (FRAME_OUTPUT_DATA (f)->paint_buffer_dirty) + 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 +2932,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 +2951,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 +2960,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); } @@ -4809,6 +4838,9 @@ w32_scroll_bar_clear (struct frame *f) { Lisp_Object bar; + if (FRAME_OUTPUT_DATA (f)->paint_buffer) + return; + /* We can have scroll bars even if this is 0, if we just turned off scroll bar mode. But in that case we should not clear them. */ @@ -4928,6 +4960,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; @@ -4969,24 +5003,33 @@ w32_read_socket (struct terminal *terminal, } else { - /* Erase background again for safety. But don't do - that if the frame's 'garbaged' flag is set, since - in that case expose_frame will do nothing, and if - the various redisplay flags happen to be unset, - we are left with a blank frame. */ - if (!FRAME_GARBAGED_P (f) || FRAME_PARENT_FRAME (f)) + enter_crit (); + if (!FRAME_OUTPUT_DATA (f)->paint_buffer) { - HDC hdc = get_frame_dc (f); - - w32_clear_rect (f, hdc, &msg.rect); - release_frame_dc (f, hdc); + /* Erase background again for safety. But don't do + that if the frame's 'garbaged' flag is set, since + in that case expose_frame will do nothing, and if + the various redisplay flags happen to be unset, + we are left with a blank frame. */ + + if (!FRAME_GARBAGED_P (f) || FRAME_PARENT_FRAME (f)) + { + HDC hdc = get_frame_dc (f); + + w32_clear_rect (f, hdc, &msg.rect); + release_frame_dc (f, hdc); + } + + expose_frame (f, + msg.rect.left, + msg.rect.top, + msg.rect.right - msg.rect.left, + msg.rect.bottom - msg.rect.top); + w32_clear_under_internal_border (f); } - expose_frame (f, - msg.rect.left, - msg.rect.top, - msg.rect.right - msg.rect.left, - msg.rect.bottom - msg.rect.top); - w32_clear_under_internal_border (f); + else + w32_show_back_buffer (f); + leave_crit (); } } break; @@ -5659,6 +5702,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 +5885,17 @@ 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 + /* 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) + w32_show_back_buffer (f); } /* If the focus was just given to an autoraising frame, @@ -7054,6 +7110,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 +7409,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 +7565,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..2dcc43fc59 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -412,6 +412,27 @@ struct w32_output 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; + + /* Whether or not this frame should be double buffered. */ + unsigned want_paint_buffer : 1; }; extern struct w32_output w32term_display; @@ -876,6 +897,7 @@ typedef char guichar_t; 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..139985c5bd 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,68 @@ 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); + + if (FRAME_OUTPUT_DATA (f)->want_paint_buffer) + { + 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 +228,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 (); commit a33bf0114920a67926761ec2f51c040265b8dfd1 Author: Po Lu Date: Sat Apr 30 02:34:18 2022 +0000 Prevent cursors from being set on tooltip frames on Haiku * src/haikuterm.c (haiku_show_hourglass, haiku_hide_hourglass) (haiku_define_frame_cursor, haiku_toggle_invisible_pointer): Ignore tooltip frames. Otherwise, the cursor changes every time a tooltip is mapped. diff --git a/src/haikuterm.c b/src/haikuterm.c index 393d359b66..1dbe3598ff 100644 --- a/src/haikuterm.c +++ b/src/haikuterm.c @@ -2005,7 +2005,8 @@ haiku_draw_window_cursor (struct window *w, static void haiku_show_hourglass (struct frame *f) { - if (FRAME_OUTPUT_DATA (f)->hourglass_p) + if (FRAME_TOOLTIP_P (f) + || FRAME_OUTPUT_DATA (f)->hourglass_p) return; block_input (); @@ -2020,7 +2021,8 @@ haiku_show_hourglass (struct frame *f) static void haiku_hide_hourglass (struct frame *f) { - if (!FRAME_OUTPUT_DATA (f)->hourglass_p) + if (FRAME_TOOLTIP_P (f) + || !FRAME_OUTPUT_DATA (f)->hourglass_p) return; block_input (); @@ -2659,8 +2661,9 @@ haiku_flush (struct frame *f) static void haiku_define_frame_cursor (struct frame *f, Emacs_Cursor cursor) { - if (f->tooltip) + if (FRAME_TOOLTIP_P (f)) return; + block_input (); if (!f->pointer_invisible && FRAME_HAIKU_VIEW (f) && !FRAME_OUTPUT_DATA (f)->hourglass_p) @@ -3852,12 +3855,12 @@ haiku_toggle_invisible_pointer (struct frame *f, bool invisible_p) { void *view = FRAME_HAIKU_VIEW (f); - if (view) + if (view && !FRAME_TOOLTIP_P (f)) { block_input (); - BView_set_view_cursor (view, invisible_p ? - FRAME_OUTPUT_DATA (f)->no_cursor : - FRAME_OUTPUT_DATA (f)->current_cursor); + BView_set_view_cursor (view, (invisible_p + ? FRAME_OUTPUT_DATA (f)->no_cursor + : FRAME_OUTPUT_DATA (f)->current_cursor)); f->pointer_invisible = invisible_p; unblock_input (); } commit 08108a856a544a80d11b1e9e437fe6c45e25adec Author: Stefan Monnier Date: Fri Apr 29 22:18:09 2022 -0400 debug-early: Print bytecode in a more manageable way * lisp/emacs-lisp/debug-early.el (debug-early-backtrace): Escape newlines to and bytecodes to make backtraces slightly more readable. Use `cl-prin1` when available. diff --git a/lisp/emacs-lisp/debug-early.el b/lisp/emacs-lisp/debug-early.el index 85ed5f2176..4f1f4b8155 100644 --- a/lisp/emacs-lisp/debug-early.el +++ b/lisp/emacs-lisp/debug-early.el @@ -35,30 +35,34 @@ (defalias 'debug-early-backtrace #'(lambda () - "Print a trace of Lisp function calls currently active. + "Print a trace of Lisp function calls currently active. The output stream used is the value of `standard-output'. This is a simplified version of the standard `backtrace' function, intended for use in debugging the early parts of the build process." - (princ "\n") - (mapbacktrace - #'(lambda (evald func args _flags) - (let ((args args)) - (if evald - (progn - (princ " ") - (prin1 func) - (princ "(")) - (progn - (princ " (") - (setq args (cons func args)))) - (if args - (while (progn - (prin1 (car args)) - (setq args (cdr args))) - (princ " "))) - (princ ")\n")))))) + (princ "\n") + (let ((print-escape-newlines t) + (print-escape-control-characters t) + (print-escape-nonascii t) + (prin1 (if (fboundp 'cl-prin1) #'cl-prin1 #'prin1))) + (mapbacktrace + #'(lambda (evald func args _flags) + (let ((args args)) + (if evald + (progn + (princ " ") + (funcall prin1 func) + (princ "(")) + (progn + (princ " (") + (setq args (cons func args)))) + (if args + (while (progn + (funcall prin1 (car args)) + (setq args (cdr args))) + (princ " "))) + (princ ")\n"))))))) (defalias 'debug-early #'(lambda (&rest args) @@ -76,7 +80,7 @@ superseded by `debug' after enough Lisp has been loaded to support the latter, except in batch mode which always uses `debug-early'. -(In versions of Emacs prior to Emacs 29, no backtrace was +\(In versions of Emacs prior to Emacs 29, no backtrace was available before `debug' was usable.)" (princ "\nError: ") (prin1 (car (car (cdr args)))) ; The error symbol. commit 73088b30cf5447c7aa459437c2f521ea9b443b0c Author: Stefan Monnier Date: Fri Apr 29 22:13:20 2022 -0400 * lisp/emacs-lisp/cl-preloaded.el (cl--typeof-types): Add `symbol-with-pos` diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el index ab7c56c4e0..2b32bc4844 100644 --- a/lisp/emacs-lisp/cl-preloaded.el +++ b/lisp/emacs-lisp/cl-preloaded.el @@ -53,7 +53,7 @@ (defconst cl--typeof-types ;; Hand made from the source code of `type-of'. '((integer number number-or-marker atom) - (symbol atom) (string array sequence atom) + (symbol-with-pos symbol atom) (symbol atom) (string array sequence atom) (cons list sequence) ;; Markers aren't `numberp', yet they are accepted wherever integers are ;; accepted, pretty much. diff --git a/src/data.c b/src/data.c index 0347ff363c..72dcf6f878 100644 --- a/src/data.c +++ b/src/data.c @@ -211,6 +211,7 @@ for example, (type-of 1) returns `integer'. */) return Qcons; case Lisp_Vectorlike: + /* WARNING!! Keep 'cl--typeof-types' in sync with this code!! */ switch (PSEUDOVECTOR_TYPE (XVECTOR (object))) { case PVEC_NORMAL_VECTOR: return Qvector; commit 834383f1e14d7a0e027bb315c46f4961093c7e2d Author: Po Lu Date: Sat Apr 30 09:29:56 2022 +0800 Fix focus reversion of Motif menus on XI2 * src/xmenu.c (create_and_show_popup_menu): Stop setting input focus on the menu window. diff --git a/src/xmenu.c b/src/xmenu.c index 316dacee5b..418628d491 100644 --- a/src/xmenu.c +++ b/src/xmenu.c @@ -1718,7 +1718,7 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv, XtSetArg (av[ac], (char *) XtNgeometry, 0); ac++; XtSetValues (menu, av, ac); -#if defined HAVE_XINPUT2 +#ifdef HAVE_XINPUT2 struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); bool any_xi_grab_p = false; @@ -1779,20 +1779,14 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv, XtDispatchEvent (&property_dummy); } #endif - - if (dpyinfo->supports_xi2) - XUngrabServer (dpyinfo->display); #endif /* Display the menu. */ lw_popup_menu (menu, &dummy); -#if defined HAVE_XINPUT2 && defined USE_MOTIF - /* This is needed to prevent XI_Enter events that set an implicit - focus from being sent. */ +#ifdef HAVE_XINPUT2 if (dpyinfo->supports_xi2) - XSetInputFocus (XtDisplay (menu), XtWindow (menu), - RevertToParent, CurrentTime); + XUngrabServer (dpyinfo->display); #endif popup_activated_flag = 1; @@ -1814,14 +1808,6 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv, unbind_to (specpdl_count, Qnil); } - -#if defined HAVE_XINPUT2 && defined USE_MOTIF - /* For some reason input focus isn't always restored to the outer - window after the menu pops down. */ - if (any_xi_grab_p) - XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), - RevertToParent, CurrentTime); -#endif } #endif /* not USE_GTK */ commit af84ea2b9e6b54d8d0e26773d3299b9efb5a39b0 Author: Po Lu Date: Sat Apr 30 09:22:20 2022 +0800 Fix mouse face bugs on Motif * src/xterm.c (handle_one_xevent): Fix handling LeaveNotify events from Motif menus. diff --git a/src/xterm.c b/src/xterm.c index 28c435afde..d442837bc6 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -15477,10 +15477,11 @@ handle_one_xevent (struct x_display_info *dpyinfo, #else f = x_top_window_to_frame (dpyinfo, event->xcrossing.window); #endif -#if defined USE_X_TOOLKIT && defined HAVE_XINPUT2 +#if defined USE_X_TOOLKIT && defined HAVE_XINPUT2 && !defined USE_MOTIF /* The XI2 event mask is set on the frame widget, so this event likely originates from the shell widget, which we aren't - interested in. */ + interested in. (But don't ignore this on Motif, since we + want to clear the mouse face when a popup is active.) */ if (dpyinfo->supports_xi2) f = NULL; #endif commit 94ecd2b3c664387cd703fb639d6909a9e6bf551b Author: Po Lu Date: Sat Apr 30 08:47:42 2022 +0800 Check display when handling XdndFinished events * src/xterm.c (handle_one_xevent): Check that the display is actually the one we want before finishing DND. diff --git a/src/xterm.c b/src/xterm.c index 51828795c5..28c435afde 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -14077,6 +14077,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, if (event->xclient.message_type == dpyinfo->Xatom_XdndFinished && (x_dnd_waiting_for_finish && !x_dnd_waiting_for_motif_finish) + /* Also check that the display is correct, since + `x_dnd_pending_finish_target' could still be valid on + another X server. */ + && dpyinfo->display == x_dnd_finish_display && event->xclient.data.l[0] == x_dnd_pending_finish_target) { x_dnd_waiting_for_finish = false; commit acc985ae7cab66ceb4e81ab403e39e933e851d9e Author: Stefan Monnier Date: Fri Apr 29 15:29:35 2022 -0400 CL types: Accept both `byte-code-function` and `compiled-function` `type-of` returns `compiled-function` for bytecode functions, but the predicate for those objects is called `byte-code-function-p`, So accept both `compiled-function` and `byte-code-function` as type names for those objects. * lisp/emacs-lisp/cl-preloaded.el (cl--typeof-types): Add `byte-code-function`. * lisp/emacs-lisp/cl-macs.el (byte-code-function, compiled-function, subr): New types. diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index c2f8c4d009..a9d422929f 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el @@ -3403,9 +3403,11 @@ Of course, we really can't know that for sure, so it's just a heuristic." (boolean . booleanp) (bool-vector . bool-vector-p) (buffer . bufferp) + (byte-code-function . byte-code-function-p) (character . natnump) (char-table . char-table-p) (command . commandp) + (compiled-function . byte-code-function-p) (hash-table . hash-table-p) (cons . consp) (fixnum . fixnump) @@ -3419,6 +3421,7 @@ Of course, we really can't know that for sure, so it's just a heuristic." (null . null) (real . numberp) (sequence . sequencep) + (subr . subrp) (string . stringp) (symbol . symbolp) (vector . vectorp) diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el index 93713f506d..ab7c56c4e0 100644 --- a/lisp/emacs-lisp/cl-preloaded.el +++ b/lisp/emacs-lisp/cl-preloaded.el @@ -59,7 +59,17 @@ ;; accepted, pretty much. (marker number-or-marker atom) (overlay atom) (float number atom) (window-configuration atom) - (process atom) (window atom) (subr atom) (compiled-function function atom) + (process atom) (window atom) + ;; FIXME: We'd want to put `function' here, but that's only true + ;; for those `subr's which aren't special forms! + (subr atom) + ;; FIXME: We should probably reverse the order between + ;; `compiled-function' and `byte-code-function' since arguably + ;; `subr' and also "compiled functions" but not "byte code functions", + ;; but it would require changing the value returned by `type-of' for + ;; byte code objects, which risks breaking existing code, which doesn't + ;; seem worth the trouble. + (compiled-function byte-code-function function atom) (module-function function atom) (buffer atom) (char-table array sequence atom) (bool-vector array sequence atom) diff --git a/lisp/emacs-lisp/nadvice.el b/lisp/emacs-lisp/nadvice.el index b20415a2d3..00c9e5438b 100644 --- a/lisp/emacs-lisp/nadvice.el +++ b/lisp/emacs-lisp/nadvice.el @@ -513,7 +513,7 @@ HOW can be one of: (t (symbol-function symbol))) function props) ;; FIXME: We could use a defmethod on `function-documentation' instead, - ;; except when (or (not nf) (autoloadp nf))! + ;; except when (autoloadp nf)! (put symbol 'function-documentation `(advice--make-docstring ',symbol)) (add-function :around (get symbol 'defalias-fset-function) #'advice--defalias-fset)) commit b566454449f3df4df9813eedd3a0934fc1b547eb Author: Juri Linkov Date: Fri Apr 29 20:40:15 2022 +0300 * test/lisp/replace-tests.el (query-replace-tests): Add more tests (bug#54733) (perform-replace-tests): New tests. (perform-replace--run-tests): New function. (perform-replace-tests): New test function. diff --git a/test/lisp/replace-tests.el b/test/lisp/replace-tests.el index 364e1f8b1d..ef1e5c3eaf 100644 --- a/test/lisp/replace-tests.el +++ b/test/lisp/replace-tests.el @@ -416,6 +416,7 @@ Each element has the format: ;; Empty inputs ("aaa" "M-% a RET RET !" "") ("aaa" "M-% RET 1 RET !" "1a1a1a") + ("aaa" "M-% RET RET !" "aaa") ;; Reuse the previous default ("aaa" "M-% a RET 1 RET . M-% RET !" "111") @@ -424,16 +425,21 @@ Each element has the format: ;; Empty inputs ("aaa" "C-M-% a* RET RET !" "") ("aaa" "C-M-% RET 1 RET !" "1a1a1a") + ("aaa" "C-M-% RET RET !" "aaa") ;; Empty matches ("aaa" "C-M-% b* RET 1 RET !" "1a1a1a") ;; Complete matches ("aaa" "C-M-% .* RET 1 RET !" "1") - ;; Adjacent matches + ;; Adjacent non-empty matches ("abaab" "C-M-% ab* RET 12 RET !" "121212") - + ;; Adjacent non-empty and empty matches + ("abab" "C-M-% a* RET 1 RET !" "1b1b") + ("abab" "C-M-% b* RET 1 RET !" "1a1a1") + ;; Test case from commit 5632eb272c7 + ("a a a " "C-M-% \\ba SPC RET c RET !" "ccc") ; not "ca c" )) -(defun query-replace--perform-tests (tests) +(defun query-replace--run-tests (tests) (with-temp-buffer (save-window-excursion ;; `execute-kbd-macro' is applied to window only @@ -448,11 +454,11 @@ Each element has the format: (should (equal (buffer-string) (nth 2 case))))))) (ert-deftest query-replace-tests () - (query-replace--perform-tests query-replace-tests)) + (query-replace--run-tests query-replace-tests)) (ert-deftest query-replace-search-function-tests () (let* ((replace-re-search-function #'re-search-forward)) - (query-replace--perform-tests query-replace-tests)) + (query-replace--run-tests query-replace-tests)) (let* ((pairs '((1 . 2) (3 . 4))) (replace-re-search-function @@ -469,7 +475,31 @@ Each element has the format: ;; FIXME: this test should pass after fixing bug#54733: ;; ("aaaa" "C-M-% .* RET 1 RET !" "1a1a") ))) - (query-replace--perform-tests tests))) + (query-replace--run-tests tests))) + + +;;; General tests for `perform-replace'. + +(defconst perform-replace-tests + '( + ;; Test case from commit 5632eb272c7 + ("a a a " "\\ba " "c" nil t nil nil nil nil nil nil nil "ccc") ; not "ca c" + ;; The same with region inside the second match + ;; FIXME: this test should pass after fixing bug#54733: + ;; ("a a a " "\\ba " "c" nil t nil nil nil 1 4 nil nil "ca a ") + )) + +(defun perform-replace--run-tests (tests) + (with-temp-buffer + (dolist (case tests) + (delete-region (point-min) (point-max)) + (insert (pop case)) + (goto-char (point-min)) + (apply 'perform-replace (butlast case)) + (should (equal (buffer-string) (car (last case))))))) + +(ert-deftest perform-replace-tests () + (perform-replace--run-tests perform-replace-tests)) ;;; Tests for `query-replace' undo feature. commit e065d08fa73ee128cb8373a5c246dc063bc41624 Author: Lars Ingebrigtsen Date: Fri Apr 29 15:52:21 2022 +0200 Simplify window-char-pixel-* code slightly * lisp/window.el (window-char-pixel-width) (window-char-pixel-height): Simplify code slightly. diff --git a/lisp/window.el b/lisp/window.el index bb4d51da5f..5ceec77bd3 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -10488,8 +10488,7 @@ If FACE is nil or omitted, the default face is used. If FACE is remapped (see `face-remapping-alist'), the function returns the information for the remapped face." (with-selected-window (window-normalize-window window t) - (let* ((face (if face face 'default)) - (info (font-info (face-font face))) + (let* ((info (font-info (face-font (or face 'default)))) (width (aref info 11))) (if (> width 0) width @@ -10503,9 +10502,7 @@ If FACE is nil or omitted, the default face is used. If FACE is remapped (see `face-remapping-alist'), the function returns the information for the remapped face." (with-selected-window (window-normalize-window window t) - (let* ((face (if face face 'default)) - (info (font-info (face-font face)))) - (aref info 3)))) + (aref (font-info (face-font (or face 'default))) 3))) (defun window-max-characters-per-line (&optional window face) "Return the number of characters that can be displayed on one line in WINDOW. commit 471c4ee4b6c74398cb7221ee9cce53021d92f9f6 Author: Lars Ingebrigtsen Date: Fri Apr 29 15:31:06 2022 +0200 Explain effects of setting a zero-width fringe * lisp/fringe.el (fringe-mode): Not non-obvious effects of setting a fringe to zero width. diff --git a/lisp/fringe.el b/lisp/fringe.el index 8c833f0242..1cfcce4542 100644 --- a/lisp/fringe.el +++ b/lisp/fringe.el @@ -244,10 +244,18 @@ When used in a Lisp program, MODE should be one of these: nil (meaning the default width). - a single integer, which specifies the pixel widths of both fringes. + This command may round up the left and right width specifications to ensure that their sum is a multiple of the character width of a frame. It never rounds up a fringe width of 0. +Note that removing a right or left fringe (by setting the width +to zero) makes Emacs reserve one column of the window body to +display a line continuation marker. (This happens for both the +left and right fringe, since Emacs can display both left-to-right +and right-to-left text.) You can use `window-max-characters-per-line' +to check the effective width. + Fringe widths set by `set-window-fringes' override the default fringe widths set by this command. This command applies to all frames that exist and frames to be created in the future. If you commit 91418d27e9c528fd9062c74a4ce58b657366a8c5 Author: Titus von der Malsburg Date: Fri Apr 29 15:14:09 2022 +0200 Add new functions for computing character metrics for windows * doc/lispref/display.texi (Size of Displayed Text): Document the char functions. * doc/lispref/windows.texi (Window Sizes): Document window-max-characters-per-line. * lisp/window.el (window-char-pixel-width) (window-char-pixel-height) (window-max-characters-per-line): New functions (bug#19395). diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index becf7ecd15..390d165025 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -2252,6 +2252,20 @@ This is a convenience function that uses @code{window-text-pixel-size} to compute the width of @var{string} (in pixels). @end defun +@defun window-char-pixel-width &optional window face +Return the average character width for the font used by @var{face} in +@var{window}. If @var{face} is @code{nil} or omitted, the +@code{default} face is used. If @var{windows} is @code{nil} or +omitted, the currently selected window is used. +@end defun + +@defun window-char-pixel-height &optional window face +Return the average character height for the font used by @var{face} in +@var{window}. If @var{face} is @code{nil} or omitted, the +@code{default} face is used. If @var{windows} is @code{nil} or +omitted, the currently selected window is used. +@end defun + @defun line-pixel-height This function returns the height in pixels of the line at point in the selected window. The value includes the line spacing of the line diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 0b3fa0c8b5..97908bea00 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -759,6 +759,15 @@ column and total width (@pxref{Coordinates and Windows}). The optional argument @var{round} behaves as it does for @code{window-total-height}. @end defun +@defun window-max-characters-per-line &optional window face +The maximum width of a line that can be displayed in a window (without +breaking the line) depends on many things, like the font used on the +line, and whether there are fringes around the window. This +convenience function can be used to calculate that number. If +@var{window} isn't given, this defaults to the currently selected +window. if @var{var} isn't given, the @code{default} face is used. +@end defun + @defun window-total-size &optional window horizontal round This function returns either the total height in lines or the total width in columns of the window @var{window}. If @var{horizontal} is diff --git a/etc/NEWS b/etc/NEWS index b0c0d0511a..c796f605b1 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1521,6 +1521,15 @@ functions. * Lisp Changes in Emacs 29.1 ++++ +** New function 'window-max-characters-per-line'. + ++++ +** New function 'window-char-pixel-width'. + ++++ +** New function 'window-char-pixel-width'. + --- ** New function 'current-cpu-time'. It gives access to the CPU time used by the Emacs process, for diff --git a/lisp/window.el b/lisp/window.el index dc33eb8a12..bb4d51da5f 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -10480,6 +10480,58 @@ displaying that processes's buffer." (put 'shrink-window-horizontally 'repeat-map 'resize-window-repeat-map) (put 'shrink-window 'repeat-map 'resize-window-repeat-map) +(defun window-char-pixel-width (&optional window face) + "Return average character width for the font of FACE used in WINDOW. +WINDOW must be a live window and defaults to the selected one. + +If FACE is nil or omitted, the default face is used. If FACE is +remapped (see `face-remapping-alist'), the function returns the +information for the remapped face." + (with-selected-window (window-normalize-window window t) + (let* ((face (if face face 'default)) + (info (font-info (face-font face))) + (width (aref info 11))) + (if (> width 0) + width + (aref info 10))))) + +(defun window-char-pixel-height (&optional window face) + "Return character height for the font of FACE used in WINDOW. +WINDOW must be a live window and defaults to the selected one. + +If FACE is nil or omitted, the default face is used. If FACE is +remapped (see `face-remapping-alist'), the function returns the +information for the remapped face." + (with-selected-window (window-normalize-window window t) + (let* ((face (if face face 'default)) + (info (font-info (face-font face)))) + (aref info 3)))) + +(defun window-max-characters-per-line (&optional window face) + "Return the number of characters that can be displayed on one line in WINDOW. +WINDOW must be a live window and defaults to the selected one. + +The character width of FACE is used for the calculation. If FACE +is nil or omitted, the default face is used. If FACE is +remapped (see `face-remapping-alist'), the function uses the +remapped face. + +This function is different from `window-body-width' in two +ways. First, it accounts for the portions of the line reserved +for the continuation glyph. Second, it accounts for the size of +the font, which may have been adjusted, e.g., using +`text-scale-increase')." + (with-selected-window (window-normalize-window window t) + (let* ((window-width (window-body-width window t)) + (font-width (window-char-pixel-width window face)) + (ncols (/ window-width font-width))) + (if (and (display-graphic-p) + overflow-newline-into-fringe + (/= (frame-parameter nil 'left-fringe) 0) + (/= (frame-parameter nil 'right-fringe) 0)) + ncols + (1- ncols))))) + (provide 'window) ;;; window.el ends here diff --git a/src/window.c b/src/window.c index ad0f54000c..cfe3977428 100644 --- a/src/window.c +++ b/src/window.c @@ -1079,7 +1079,9 @@ means that if a column at the right of the text area is only partially visible, that column is not counted. Note that the returned value includes the column reserved for the -continuation glyph. */) +continuation glyph. + +Also see `window-max-characters-per-line'. */) (Lisp_Object window, Lisp_Object pixelwise) { return make_fixnum (window_body_width (decode_live_window (window), commit fa52782f5c4eaef7138534766dfc8a29465785b2 Author: Pip Cet Date: Fri Apr 29 14:14:19 2022 +0200 Make timer_check even more resilient * src/keyboard.c (timer_check): Inhibit atimers while making the copy of the timer list (bug#21380). This prevents an extremely unlikely segfault. diff --git a/src/keyboard.c b/src/keyboard.c index 19c8fdf1dc..69e741070c 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -4633,6 +4633,8 @@ timer_check (void) Lisp_Object tem = Vinhibit_quit; Vinhibit_quit = Qt; + block_input (); + turn_on_atimers (false); /* We use copies of the timers' lists to allow a timer to add itself again, without locking up Emacs if the newly added timer is @@ -4646,6 +4648,8 @@ timer_check (void) else idle_timers = Qnil; + turn_on_atimers (true); + unblock_input (); Vinhibit_quit = tem; do commit 1b71c995da6a21c65c728b169a44113c969665dc Author: Lars Ingebrigtsen Date: Fri Apr 29 13:57:57 2022 +0200 Avoid binding mouse-1 in xref when mouse-1 doesn't follow links * lisp/progmodes/xref.el (xref--button-map): Avoid binding mouse-1 when `mouse-1-click-follows-link' is nil (bug#35353). diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index 43ab703da2..6fa9a5c8d6 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el @@ -965,7 +965,9 @@ beginning of the line." (defvar xref--button-map (let ((map (make-sparse-keymap))) - (define-key map [mouse-1] #'xref-goto-xref) + (when mouse-1-click-follows-link + (define-key map [mouse-1] #'xref-goto-xref)) + (define-key map [follow-link] 'mouse-face) (define-key map [mouse-2] #'xref-select-and-show-xref) map)) commit 20d3d62ec9a6048cacf1297622a8f7e48d6d5a4b Author: Po Lu Date: Fri Apr 29 11:06:52 2022 +0000 Fix colorspace calculations on Haiku * src/haiku_support.cc (be_get_display_planes) (be_get_display_color_cells): Handle grayscale basic colorspaces correctly. (be_is_display_grayscale): New function. * src/haiku_support.h: Update prototypes. * src/haikufns.c (haiku_set_no_accept_focus, haiku_iconify_frame) (Fxw_display_color_p, Fxw_color_values, Fx_display_grayscale_p): Actually handle grayscale colorspaces. (Fx_display_pixel_width, Fx_display_pixel_height) (Fx_display_mm_height, Fx_display_mm_width): Clean up coding style. (Fx_display_visual_class): Handle grayscale colorspaces. (syms_of_haikufns): New defsyms. diff --git a/src/haiku_support.cc b/src/haiku_support.cc index 6dea2d3620..8ad3c58a17 100644 --- a/src/haiku_support.cc +++ b/src/haiku_support.cc @@ -3587,24 +3587,35 @@ int be_get_display_planes (void) { color_space space = dpy_color_space; + BScreen screen; + if (space == B_NO_COLOR_SPACE) { - BScreen screen; /* This is actually a very slow operation. */ if (!screen.IsValid ()) gui_abort ("Invalid screen"); + space = dpy_color_space = screen.ColorSpace (); } - if (space == B_RGB32 || space == B_RGB24) - return 24; - if (space == B_RGB16) - return 16; - if (space == B_RGB15) - return 15; - if (space == B_CMAP8) - return 8; + switch (space) + { + case B_RGB32: + case B_RGB24: + return 24; + case B_RGB16: + return 16; + case B_RGB15: + return 15; + case B_CMAP8: + case B_GRAY8: + return 8; + case B_GRAY1: + return 1; + + default: + gui_abort ("Bad colorspace for screen"); + } - gui_abort ("Bad colorspace for screen"); /* https://www.haiku-os.org/docs/api/classBScreen.html says a valid screen can't be anything else. */ return -1; @@ -3614,28 +3625,58 @@ be_get_display_planes (void) int be_get_display_color_cells (void) { + BScreen screen; color_space space = dpy_color_space; + if (space == B_NO_COLOR_SPACE) { - BScreen screen; if (!screen.IsValid ()) gui_abort ("Invalid screen"); + space = dpy_color_space = screen.ColorSpace (); } - if (space == B_RGB32 || space == B_RGB24) - return 1677216; - if (space == B_RGB16) - return 65536; - if (space == B_RGB15) - return 32768; - if (space == B_CMAP8) - return 256; + switch (space) + { + case B_RGB32: + case B_RGB24: + return 16777216; + case B_RGB16: + return 65536; + case B_RGB15: + return 32768; + case B_CMAP8: + case B_GRAY8: + return 256; + case B_GRAY1: + return 2; + + default: + gui_abort ("Bad colorspace for screen"); + } - gui_abort ("Bad colorspace for screen"); return -1; } +/* Return whether or not the current display is only capable of + producing grayscale colors. */ +bool +be_is_display_grayscale (void) +{ + BScreen screen; + color_space space = dpy_color_space; + + if (space == B_NO_COLOR_SPACE) + { + if (!screen.IsValid ()) + gui_abort ("Invalid screen"); + + space = dpy_color_space = screen.ColorSpace (); + } + + return space == B_GRAY8 || space == B_GRAY1; +} + /* Warp the pointer to X by Y. */ void be_warp_pointer (int x, int y) diff --git a/src/haiku_support.h b/src/haiku_support.h index d442635476..88edc1ae14 100644 --- a/src/haiku_support.h +++ b/src/haiku_support.h @@ -605,6 +605,7 @@ extern void EmacsWindow_unzoom (void *); extern void be_get_version_string (char *, int); extern int be_get_display_planes (void); extern int be_get_display_color_cells (void); +extern bool be_is_display_grayscale (void); extern void be_warp_pointer (int, int); extern void EmacsView_set_up_double_buffering (void *); diff --git a/src/haikufns.c b/src/haikufns.c index 7ec6f576cf..fb79066b77 100644 --- a/src/haikufns.c +++ b/src/haikufns.c @@ -511,15 +511,13 @@ haiku_explicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval) static void haiku_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) { - block_input (); if (!EQ (new_value, old_value)) FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value); + block_input (); if (FRAME_HAIKU_WINDOW (f)) - { - BWindow_set_avoid_focus (FRAME_HAIKU_WINDOW (f), - FRAME_NO_ACCEPT_FOCUS (f)); - } + BWindow_set_avoid_focus (FRAME_HAIKU_WINDOW (f), + FRAME_NO_ACCEPT_FOCUS (f)); unblock_input (); } @@ -1626,13 +1624,11 @@ haiku_iconify_frame (struct frame *frame) if (FRAME_ICONIFIED_P (frame)) return; - block_input (); - SET_FRAME_VISIBLE (frame, false); SET_FRAME_ICONIFIED (frame, true); + block_input (); BWindow_iconify (FRAME_HAIKU_WINDOW (frame)); - unblock_input (); } @@ -1841,7 +1837,9 @@ DEFUN ("xw-display-color-p", Fxw_display_color_p, Sxw_display_color_p, 0, 1, 0, doc: /* SKIP: real doc in xfns.c. */) (Lisp_Object terminal) { - return Qt; + check_haiku_display_info (terminal); + + return be_is_display_grayscale () ? Qnil : Qt; } DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0, @@ -1861,20 +1859,19 @@ DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, 0, (Lisp_Object color, Lisp_Object frame) { Emacs_Color col; + int rc; CHECK_STRING (color); decode_window_system_frame (frame); block_input (); - if (haiku_get_color (SSDATA (color), &col)) - { - unblock_input (); - return Qnil; - } + rc = haiku_get_color (SSDATA (color), &col); unblock_input (); - return list3i (lrint (col.red), lrint (col.green), - lrint (col.blue)); + if (rc) + return Qnil; + + return list3i (col.red, col.green, col.blue); } DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p, Sx_display_grayscale_p, @@ -1882,7 +1879,9 @@ DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p, Sx_display_grayscale_p, doc: /* SKIP: real doc in xfns.c. */) (Lisp_Object terminal) { - return Qnil; + check_haiku_display_info (terminal); + + return be_is_display_grayscale () ? Qt : Qnil; } DEFUN ("x-open-connection", Fx_open_connection, Sx_open_connection, @@ -1923,9 +1922,9 @@ DEFUN ("x-display-pixel-width", Fx_display_pixel_width, Sx_display_pixel_width, (Lisp_Object terminal) { + int width, height; check_haiku_display_info (terminal); - int width, height; BScreen_px_dim (&width, &height); return make_fixnum (width); } @@ -1936,9 +1935,9 @@ DEFUN ("x-display-pixel-height", Fx_display_pixel_height, Sx_display_pixel_heigh (Lisp_Object terminal) { + int width, height; check_haiku_display_info (terminal); - int width, height; BScreen_px_dim (&width, &height); return make_fixnum (width); } @@ -1948,10 +1947,9 @@ DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, 0, 1, (Lisp_Object terminal) { struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal); - int width, height; - BScreen_px_dim (&width, &height); + BScreen_px_dim (&width, &height); return make_fixnum (height / (dpyinfo->resy / 25.4)); } @@ -1961,10 +1959,9 @@ DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0, (Lisp_Object terminal) { struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal); - int width, height; - BScreen_px_dim (&width, &height); + BScreen_px_dim (&width, &height); return make_fixnum (width / (dpyinfo->resx / 25.4)); } @@ -1981,14 +1978,20 @@ DEFUN ("x-display-visual-class", Fx_display_visual_class, doc: /* SKIP: real doc in xfns.c. */) (Lisp_Object terminal) { + int planes; + bool grayscale_p; + check_haiku_display_info (terminal); - int planes = be_get_display_planes (); + grayscale_p = be_is_display_grayscale (); + if (grayscale_p) + return Qstatic_gray; + planes = be_get_display_planes (); if (planes == 8) - return intern ("static-color"); + return Qstatic_color; - return intern ("true-color"); + return Qtrue_color; } DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, @@ -2742,6 +2745,10 @@ syms_of_haikufns (void) DEFSYM (Qwhen_mapped, "when-mapped"); DEFSYM (Qtooltip_reuse_hidden_frame, "tooltip-reuse-hidden-frame"); + DEFSYM (Qstatic_color, "static-color"); + DEFSYM (Qstatic_gray, "static-gray"); + DEFSYM (Qtrue_color, "true-color"); + defsubr (&Sx_hide_tip); defsubr (&Sxw_display_color_p); defsubr (&Sx_display_grayscale_p); commit e313cae71fc38e29849e68b421a16a1014626d04 Author: Lars Ingebrigtsen Date: Fri Apr 29 12:42:06 2022 +0200 Add helper function to remove title bar when maximizing frames * lisp/frame.el (toggle-frame-maximized): Mention it. (frame-hide-title-bar-when-maximized): New function (bug#31968). Adapted from code by Jonathan Kyle Mitchell. * src/window.c (syms_of_window): Mention it. diff --git a/lisp/frame.el b/lisp/frame.el index 83e67dac4e..49eabcf978 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -2960,6 +2960,12 @@ If the frame is in fullscreen state, don't change its state, but set the frame's `fullscreen-restore' parameter to `maximized', so the frame will be maximized after disabling fullscreen state. +If you wish to hide the title bar when the frame is maximized, you +can add something like the following to your init file: + + (add-hook \\='window-size-change-functions + #\\='frame-hide-title-bar-when-maximized) + Note that with some window managers you may have to set `frame-resize-pixelwise' to non-nil in order to make a frame appear truly maximized. In addition, you may have to set @@ -3075,6 +3081,13 @@ Offer NUMBER as default value, if it is a natural number." bidi-display-reordering bidi-inhibit-bpa)) +(defun frame-hide-title-bar-when-maximized (frame) + "Hide the title bar if FRAME is maximized. +If FRAME isn't maximized, show the title bar." + (set-frame-parameter + frame 'undecorated + (eq (alist-get 'fullscreen (frame-parameters frame)) 'maximized))) + (provide 'frame) ;;; frame.el ends here diff --git a/src/window.c b/src/window.c index 48da783931..ad0f54000c 100644 --- a/src/window.c +++ b/src/window.c @@ -8341,7 +8341,10 @@ In this case the window is passed as argument. Functions specified by the default value are called for each frame if at least one window on that frame has been added or changed its buffer or its total or body size since the last redisplay. In this case the -frame is passed as argument. */); +frame is passed as argument. + +For instance, to hide the title bar when the frame is maximized, you +can add `frame-hide-title-bar-when-maximized' to this variable. */); Vwindow_size_change_functions = Qnil; DEFVAR_LISP ("window-selection-change-functions", Vwindow_selection_change_functions, commit 5c606a46bfcb3851365de2d4ffbff9e88fc1c21c Author: Eli Zaretskii Date: Fri Apr 29 10:12:02 2022 +0300 ; Fix typo in documentation of 'current-time-list' * src/timefns.c (syms_of_timefns) : * etc/NEWS: * doc/lispref/os.texi (Time of Day): Fix a typo. diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index 20b6c1cec6..5356969b0b 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi @@ -1420,7 +1420,7 @@ This boolean variable is a transition aid. If @code{t}, form, typically @code{(@var{high} @var{low} @var{micro} @var{pico})}; otherwise, they use @code{(@var{ticks} . @var{hz})} form. Currently this variable defaults to @code{t}, for behavior compatible with -previous Emacs versions. Developers are encourage to test +previous Emacs versions. Developers are encouraged to test timestamp-related code with this variable set to @code{nil}, as it will default to @code{nil} in a future Emacs version, and will be removed in some version after that. diff --git a/etc/NEWS b/etc/NEWS index 9f93c4067f..b0c0d0511a 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -209,7 +209,7 @@ new variable is nil. The variable defaults to t, which means these functions default to timestamps of the forms (HI LO US PS), (HI LO US) or (HI LO), which are less regular and less efficient. This is part of a long-planned change first documented in Emacs 27. Developers are -encourage to test timestamp-related code with this variable set to +encouraged to test timestamp-related code with this variable set to nil, as it will default to nil in a future Emacs version and will be removed some time after that. diff --git a/src/timefns.c b/src/timefns.c index bca9a566e0..7d2e3f6414 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -2033,7 +2033,7 @@ This boolean variable is a transition aid. If t, `current-time' and related functions return timestamps in list form, typically \(HIGH LOW USEC PSEC); otherwise, they use (TICKS . HZ) form. Currently this variable defaults to t, for behavior compatible with -previous Emacs versions. Developers are encourage to test +previous Emacs versions. Developers are encouraged to test timestamp-related code with this variable set to nil, as it will default to nil in a future Emacs version, and will be removed in some version after that. */);