From 70fae756298ea78422b01001cda150686910a670 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Tue, 24 Mar 2026 19:09:52 +0200 Subject: Avoid segfaults in 'bidi_mirror_char' during startup * src/bidi.c (bidi_mirror_char): Make sure 'bidi_mirror_table' is initialized before accessing it. (Bug#80668) --- src/bidi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/bidi.c b/src/bidi.c index f4bca186177..9cf53787c4b 100644 --- a/src/bidi.c +++ b/src/bidi.c @@ -367,6 +367,8 @@ bidi_isolate_fmt_char (bidi_type_t ch_type) return (ch_type == LRI || ch_type == RLI || ch_type == PDI || ch_type == FSI); } +static void bidi_initialize (void); + /* Return the mirrored character of C, if it has one. If C has no mirrored counterpart, return C. Note: The conditions in UAX#9 clause L4 regarding the surrounding @@ -381,6 +383,14 @@ bidi_mirror_char (int c) if (c < 0 || c > MAX_CHAR) emacs_abort (); + /* We can be called at the very beginning of init_iterator, via + produce_special_glyphs, and the first such call in a session might + happen when the bidi-mirroring table was not yet initialized. Make + sure we do this now. */ + if (!CHAR_TABLE_P (bidi_mirror_table) + && !bidi_initialized) + bidi_initialize (); + val = CHAR_TABLE_REF (bidi_mirror_table, c); if (FIXNUMP (val)) { -- cgit v1.2.1 From cc4c8e6e9f41bc07cb3047e4ecc52ccfeaac0c75 Mon Sep 17 00:00:00 2001 From: Basil L. Contovounesios Date: Wed, 18 Mar 2026 12:16:37 +0100 Subject: Simplify some struct frame.output_method checks * src/lisp.h (inhibit_window_system): Fix typo in commentary. * src/dispnew.c (Fopen_termscript): (init_display_interactive) [!HAVE_ANDROID, !MSDOS]: * src/dosfns.c [MSDOS] (dos_cleanup): * src/frame.c (frame_inhibit_resize, frame_windows_min_size) (do_switch_frame, delete_frame): (make_terminal_frame) [MSDOS]: (Fmake_terminal_frame) [MSDOS, WINDOWSNT]: * src/keyboard.c (read_decoded_event_from_main_queue) [!WINDOWSNT]: (Fcurrent_input_mode): * src/menu.c (single_menu_item): * src/msdos.c [MSDOS] (internal_terminal_init): * src/sysdep.c (init_sys_modes): * src/term.c: [HAVE_GPM] (Fgpm_mouse_start, Fgpm_mouse_stop): [MSDOS] (tty_free_frame_resources): * src/w32xfns.c (get_frame_dc): * src/xdisp.c (clear_garbaged_frames, hscroll_window_tree) (redisplay_internal): * src/xfaces.c (Fdisplay_supports_face_attributes_p) (realize_default_face, realize_face, realize_tty_face): Use is_tty_frame, FRAME_INITIAL_P, FRAME_MSDOS_P, FRAME_TERMCAP_P, FRAME_W32_P, and FRAME_TTY as appropriate instead of open-coding them (bug#80629). --- src/dispnew.c | 7 +++---- src/dosfns.c | 3 +-- src/frame.c | 15 +++++++-------- src/keyboard.c | 4 ++-- src/lisp.h | 2 +- src/menu.c | 3 +-- src/msdos.c | 2 +- src/sysdep.c | 3 +-- src/term.c | 10 +++------- src/w32xfns.c | 2 +- src/xdisp.c | 14 +++++--------- src/xfaces.c | 10 +++++----- 12 files changed, 31 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/dispnew.c b/src/dispnew.c index 908345645bc..736647408cb 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -6776,8 +6776,7 @@ FILE = nil means just close any termscript file currently open. */) { struct tty_display_info *tty; - if (! FRAME_TERMCAP_P (SELECTED_FRAME ()) - && ! FRAME_MSDOS_P (SELECTED_FRAME ())) + if (!is_tty_frame (SELECTED_FRAME ())) error ("Current frame is not on a tty device"); tty = CURTTY (); @@ -7344,7 +7343,7 @@ init_display_interactive (void) t = init_tty (0, terminal_type, 1); /* Errors are fatal. */ /* Convert the initial frame to use the new display. */ - if (f->output_method != output_initial) + if (!FRAME_INITIAL_P (f)) emacs_abort (); f->output_method = t->type; f->terminal = t; @@ -7354,7 +7353,7 @@ init_display_interactive (void) f->output_data.tty = &the_only_tty_output; f->output_data.tty->display_info = &the_only_display_info; #else - if (f->output_method == output_termcap) + if (FRAME_TERMCAP_P (f)) create_tty_output (f); #endif t->display_info.tty->top_frame = selected_frame; diff --git a/src/dosfns.c b/src/dosfns.c index 414cc550510..07d553b0d78 100644 --- a/src/dosfns.c +++ b/src/dosfns.c @@ -681,8 +681,7 @@ dos_cleanup (void) { struct frame *sf = XFRAME (selected_frame); - if (FRAME_LIVE_P (sf) - && (FRAME_MSDOS_P (sf) || FRAME_TERMCAP_P (sf))) + if (FRAME_LIVE_P (sf) && is_tty_frame (sf)) { tty = CURTTY (); if (tty->termscript) diff --git a/src/frame.c b/src/frame.c index 89ea58b3fe2..2e98937fab3 100644 --- a/src/frame.c +++ b/src/frame.c @@ -213,7 +213,7 @@ frame_inhibit_resize (struct frame *f, bool horizontal, Lisp_Object parameter) && !NILP (fullscreen) && !EQ (fullscreen, Qfullheight)) || (!horizontal && !NILP (fullscreen) && !EQ (fullscreen, Qfullwidth)) - || FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)))); + || is_tty_frame (f)))); } @@ -561,7 +561,7 @@ frame_windows_min_size (Lisp_Object frame, Lisp_Object horizontal, /* Don't allow too small height of text-mode frames, or else cm.c might abort in cmcheckmagic. */ - if ((FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) && NILP (horizontal)) + if (is_tty_frame (f) && NILP (horizontal)) { int min_height = (FRAME_MENU_BAR_LINES (f) + FRAME_TAB_BAR_LINES (f) + FRAME_WANTS_MODELINE_P (f) @@ -1572,7 +1572,7 @@ make_terminal_frame (struct terminal *terminal, Lisp_Object parent, f->output_data.tty->display_info = &the_only_display_info; if (!inhibit_window_system && (!FRAMEP (selected_frame) || !FRAME_LIVE_P (XFRAME (selected_frame)) - || XFRAME (selected_frame)->output_method == output_msdos_raw)) + || FRAME_MSDOS_P (XFRAME (selected_frame)))) f->output_method = output_msdos_raw; else f->output_method = output_termcap; @@ -1762,13 +1762,12 @@ affects all frames on the same terminal device. */) struct frame *sf = SELECTED_FRAME (); #ifdef MSDOS - if (sf->output_method != output_msdos_raw - && sf->output_method != output_termcap) + if (!is_tty_frame (sf)) emacs_abort (); #else /* not MSDOS */ #ifdef WINDOWSNT /* This should work now! */ - if (sf->output_method != output_termcap) + if (!FRAME_TERMCAP_P (sf)) error ("Not using an ASCII terminal now; cannot make a new ASCII frame"); #endif #endif /* not MSDOS */ @@ -1985,7 +1984,7 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor if (!for_deletion && FRAME_HAS_MINIBUF_P (sf)) resize_mini_window (XWINDOW (FRAME_MINIBUF_WINDOW (sf)), 1); - if (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) + if (is_tty_frame (f)) { struct tty_display_info *tty = FRAME_TTY (f); Lisp_Object top_frame = tty->top_frame; @@ -2799,7 +2798,7 @@ delete_frame (Lisp_Object frame, Lisp_Object force) && FRAME_LIVE_P (f1) && !FRAME_TOOLTIP_P (f1)) { - if (FRAME_TERMCAP_P (f1) || FRAME_MSDOS_P (f1)) + if (is_tty_frame (f1)) { Lisp_Object top_frame = FRAME_TTY (f1)->top_frame; diff --git a/src/keyboard.c b/src/keyboard.c index c16cc5277e9..c64b2a6dd57 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -2436,7 +2436,7 @@ read_decoded_event_from_main_queue (struct timespec *end_time, #else struct frame *frame = XFRAME (selected_frame); struct terminal *terminal = frame->terminal; - if (!((FRAME_TERMCAP_P (frame) || FRAME_MSDOS_P (frame)) + if (!(is_tty_frame (frame) /* Don't apply decoding if we're just reading a raw event (e.g. reading bytes sent by the xterm to specify the position of a mouse click). */ @@ -12982,7 +12982,7 @@ The elements of this list correspond to the arguments of Lisp_Object interrupt = interrupt_input ? Qt : Qnil; Lisp_Object flow, meta; - if (FRAME_TERMCAP_P (sf) || FRAME_MSDOS_P (sf)) + if (is_tty_frame (sf)) { flow = FRAME_TTY (sf)->flow_control ? Qt : Qnil; meta = (FRAME_TTY (sf)->meta_key == 2 diff --git a/src/lisp.h b/src/lisp.h index cbd61126a74..223eb4df564 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -5166,7 +5166,7 @@ extern void *w32_daemon_event; /* True if handling a fatal error already. */ extern bool fatal_error_in_progress; -/* True means don't do use window-system-specific display code. */ +/* True means don't use window-system-specific display code. */ extern bool inhibit_window_system; /* True means that a filter or a sentinel is running. */ extern bool running_asynch_code; diff --git a/src/menu.c b/src/menu.c index 69fd3867002..ff4721d35f7 100644 --- a/src/menu.c +++ b/src/menu.c @@ -404,8 +404,7 @@ single_menu_item (Lisp_Object key, Lisp_Object item, Lisp_Object dummy, void *sk } } - if ((FRAME_TERMCAP_P (XFRAME (Vmenu_updating_frame)) - || FRAME_MSDOS_P (XFRAME (Vmenu_updating_frame))) + if (is_tty_frame (XFRAME (Vmenu_updating_frame)) && !NILP (map)) /* Indicate visually that this is a submenu. */ { diff --git a/src/msdos.c b/src/msdos.c index 7e89d549706..4d111b30969 100644 --- a/src/msdos.c +++ b/src/msdos.c @@ -1787,7 +1787,7 @@ internal_terminal_init (void) #endif /* If this is the initial terminal, we are done here. */ - if (sf->output_method == output_initial) + if (FRAME_INITIAL_P (sf)) return; internal_terminal diff --git a/src/sysdep.c b/src/sysdep.c index 8895655566e..10269e4d0ce 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -1341,8 +1341,7 @@ init_sys_modes (struct tty_display_info *tty_out) frame_garbaged = 1; FOR_EACH_FRAME (tail, frame) { - if ((FRAME_TERMCAP_P (XFRAME (frame)) - || FRAME_MSDOS_P (XFRAME (frame))) + if (is_tty_frame (XFRAME (frame)) && FRAME_TTY (XFRAME (frame)) == tty_out) FRAME_GARBAGED_P (XFRAME (frame)) = 1; } diff --git a/src/term.c b/src/term.c index 1d1fc05ac41..b5cf418450f 100644 --- a/src/term.c +++ b/src/term.c @@ -2968,9 +2968,7 @@ Gpm-mouse can only be activated for one tty at a time. */) (void) { struct frame *f = SELECTED_FRAME (); - struct tty_display_info *tty - = ((f)->output_method == output_termcap - ? (f)->terminal->display_info.tty : NULL); + struct tty_display_info *tty = FRAME_TERMCAP_P (f) ? FRAME_TTY (f) : NULL; Gpm_Connect connection; if (!tty) @@ -3016,9 +3014,7 @@ DEFUN ("gpm-mouse-stop", Fgpm_mouse_stop, Sgpm_mouse_stop, (void) { struct frame *f = SELECTED_FRAME (); - struct tty_display_info *tty - = ((f)->output_method == output_termcap - ? (f)->terminal->display_info.tty : NULL); + struct tty_display_info *tty = FRAME_TERMCAP_P (f) ? FRAME_TTY (f) : NULL; if (!tty || gpm_tty != tty) return Qnil; /* Not activated on this terminal, nothing to do. */ @@ -4198,7 +4194,7 @@ tty_free_frame_resources (struct frame *f) static void tty_free_frame_resources (struct frame *f) { - eassert (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)); + eassert (is_tty_frame (f)); free_frame_faces (f); /* Deleting a child frame means we have to thoroughly redisplay its root frame to make sure the child disappears from the display. */ diff --git a/src/w32xfns.c b/src/w32xfns.c index f920e407343..df3d42c9d28 100644 --- a/src/w32xfns.c +++ b/src/w32xfns.c @@ -177,7 +177,7 @@ get_frame_dc (struct frame *f) HGDIOBJ obj; struct w32_output *output; - if (f->output_method != output_w32) + if (!FRAME_W32_P (f)) emacs_abort (); enter_crit (); diff --git a/src/xdisp.c b/src/xdisp.c index 8af54ad49f1..179c1bae72b 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -13651,7 +13651,7 @@ clear_garbaged_frames (void) selected frame, and might leave the selected frame with corrupted display, if it happens not to be marked garbaged. */ - && !(f != sf && (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)))) + && !(f != sf && is_tty_frame (f))) redraw_frame (f); else clear_current_matrices (f); @@ -16639,11 +16639,8 @@ hscroll_window_tree (Lisp_Object window) } } if (cursor_row->truncated_on_left_p) - { - /* On TTY frames, don't count the left truncation glyph. */ - struct frame *f = XFRAME (WINDOW_FRAME (w)); - x_offset -= (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)); - } + /* On TTY frames, don't count the left truncation glyph. */ + x_offset -= is_tty_frame (XFRAME (WINDOW_FRAME (w))); text_area_width = window_box_width (w, TEXT_AREA); @@ -17377,7 +17374,7 @@ redisplay_internal (void) windows_or_buffers_changed = 47; struct frame *previous_frame; - if ((FRAME_TERMCAP_P (sf) || FRAME_MSDOS_P (sf)) + if (is_tty_frame (sf) && (previous_frame = FRAME_TTY (sf)->previous_frame, previous_frame != sf)) { @@ -17822,8 +17819,7 @@ redisplay_internal (void) } retry_frame: - if (FRAME_WINDOW_P (f) - || FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f) || f == sf) + if (FRAME_WINDOW_P (f) || is_tty_frame (f) || f == sf) { /* Only GC scrollbars when we redisplay the whole frame. */ bool gcscrollbars = f->redisplay || !REDISPLAY_SOME_P (); diff --git a/src/xfaces.c b/src/xfaces.c index a27e1c6b480..ab0934abde2 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -5776,7 +5776,7 @@ face for italic. */) } /* Dispatch to the appropriate handler. */ - if (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) + if (is_tty_frame (f)) supports = tty_supports_face_attributes_p (f, attrs, def_face); #ifdef HAVE_WINDOW_SYSTEM else @@ -6070,7 +6070,7 @@ realize_default_face (struct frame *f) ASET (lface, LFACE_FOREGROUND_INDEX, XCDR (color)); else if (FRAME_WINDOW_P (f)) return false; - else if (FRAME_INITIAL_P (f) || FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) + else if (FRAME_INITIAL_P (f) || is_tty_frame (f)) ASET (lface, LFACE_FOREGROUND_INDEX, build_string (unspecified_fg)); else emacs_abort (); @@ -6085,7 +6085,7 @@ realize_default_face (struct frame *f) ASET (lface, LFACE_BACKGROUND_INDEX, XCDR (color)); else if (FRAME_WINDOW_P (f)) return false; - else if (FRAME_INITIAL_P (f) || FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) + else if (FRAME_INITIAL_P (f) || is_tty_frame (f)) ASET (lface, LFACE_BACKGROUND_INDEX, build_string (unspecified_bg)); else emacs_abort (); @@ -6196,7 +6196,7 @@ realize_face (struct face_cache *cache, Lisp_Object attrs[LFACE_VECTOR_SIZE], if (FRAME_WINDOW_P (cache->f)) face = realize_gui_face (cache, attrs); - else if (FRAME_TERMCAP_P (cache->f) || FRAME_MSDOS_P (cache->f)) + else if (is_tty_frame (cache->f)) face = realize_tty_face (cache, attrs); else if (FRAME_INITIAL_P (cache->f)) { @@ -6705,7 +6705,7 @@ realize_tty_face (struct face_cache *cache, struct frame *f = cache->f; /* Frame must be a termcap frame. */ - eassert (FRAME_TERMCAP_P (cache->f) || FRAME_MSDOS_P (cache->f)); + eassert (is_tty_frame (cache->f)); /* Allocate a new realized face. */ face = make_realized_face (attrs); -- cgit v1.2.1 From dfeaf7fc009847ff9d664aa4084a78907641a079 Mon Sep 17 00:00:00 2001 From: Basil L. Contovounesios Date: Wed, 25 Mar 2026 12:02:35 +0100 Subject: ; Simplify Fframe_id frame decoding. The subsequent decode_live_frame already checks for nil. --- src/frame.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/frame.c b/src/frame.c index 2e98937fab3..d0d02eaeb1f 100644 --- a/src/frame.c +++ b/src/frame.c @@ -348,8 +348,6 @@ If FRAME is nil, use the selected frame. Return nil if the id has not been set. */) (Lisp_Object frame) { - if (NILP (frame)) - frame = selected_frame; struct frame *f = decode_live_frame (frame); if (f->id == 0) return Qnil; -- cgit v1.2.1 From d78000728351986b0083013728dc3ef366112c0b Mon Sep 17 00:00:00 2001 From: Basil L. Contovounesios Date: Wed, 18 Mar 2026 12:42:28 +0100 Subject: Add predicate for initial_terminal This introduces the predicate frame-initial-p, which uses struct frame.output_method or struct terminal.type to detect initial_terminal without relying on its name (bug#80629). For some prior discussion, see: https://lists.gnu.org/r/emacs-devel/2019-12/msg00480.html https://lists.gnu.org/r/emacs-devel/2020-01/msg00120.html * doc/lispref/frames.texi (Frames): Document frame-initial-p. (Finding All Frames): Fix grammar. * etc/NEWS (Lisp Changes in Emacs 31.1): Announce frame-initial-p. * lisp/desktop.el (desktop--check-dont-save): * lisp/emacs-lisp/debug.el (debug): * lisp/frameset.el (frameset-restore): * lisp/menu-bar.el (menu-bar-update-buffers): * lisp/xt-mouse.el (turn-on-xterm-mouse-tracking-on-terminal): Use frame-initial-p instead of checking the "initial_terminal" name. * lisp/emacs-lisp/byte-opt.el: Mark frame-initial-p as error-free. * src/pgtkterm.c (pgtk_focus_changed): Use IS_DAEMON in place of Fdaemonp, thus also accepting a named daemon session. * src/terminal.c (decode_tty_terminal): Clarify commentary. (Fframe_initial_p): New function. (syms_of_terminal): Expose it. (init_initial_terminal): Update commentary now that menu-bar-update-buffers uses frame-initial-p (bug#53740). * test/lisp/xt-mouse-tests.el (with-xterm-mouse-mode): Simulate the lack of an initial terminal by overriding frame-initial-p now that turn-on-xterm-mouse-tracking-on-terminal uses it. * test/src/terminal-tests.el: New file. --- src/pgtkterm.c | 5 +++-- src/terminal.c | 27 +++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/pgtkterm.c b/src/pgtkterm.c index bf482590bc5..6dc45604f01 100644 --- a/src/pgtkterm.c +++ b/src/pgtkterm.c @@ -5693,10 +5693,11 @@ pgtk_focus_changed (gboolean is_enter, int state, /* Don't stop displaying the initial startup message for a switch-frame event we don't need. */ - /* When run as a daemon, Vterminal_frame is always NIL. */ + /* When run as a daemon, Vterminal_frame is always nil. + FIXME: Isn't it actually the other way around? */ bufp->ie.arg = (((NILP (Vterminal_frame) || !FRAME_PGTK_P (XFRAME (Vterminal_frame)) - || EQ (Fdaemonp (), Qt)) + || IS_DAEMON) && CONSP (Vframe_list) && !NILP (XCDR (Vframe_list))) ? Qt : Qnil); bufp->ie.kind = FOCUS_IN_EVENT; diff --git a/src/terminal.c b/src/terminal.c index 44095acc2ab..f3a90740d8e 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -244,8 +244,8 @@ decode_live_terminal (Lisp_Object terminal) return t; } -/* Like decode_terminal, but ensure that the resulting terminal object refers - to a text-based terminal device. */ +/* Like decode_live_terminal, but ensure that the resulting terminal + object refers to a text-based terminal device. */ struct terminal * decode_tty_terminal (Lisp_Object terminal) @@ -479,6 +479,25 @@ return values. */) } } +DEFUN ("frame-initial-p", Fframe_initial_p, Sframe_initial_p, 0, 1, 0, + doc: /* Return non-nil if FRAME is the initial frame. +That is, the initial text frame used internally during daemon mode, +batch mode, and the early stages of startup. +If FRAME is a terminal object, return non-nil if it holds +the initial frame. FRAME defaults to the selected frame. */) + (Lisp_Object frame) +{ + if (NILP (frame)) + frame = selected_frame; + if (FRAMEP (frame)) + { + struct frame *f = XFRAME (frame); + return FRAME_LIVE_P (f) && FRAME_INITIAL_P (f) ? Qt : Qnil; + } + struct terminal *t = decode_terminal (frame); + return t && t->type == output_initial ? Qt : Qnil; +} + DEFUN ("terminal-list", Fterminal_list, Sterminal_list, 0, 0, 0, doc: /* Return a list of all terminal devices. */) (void) @@ -647,8 +666,6 @@ init_initial_terminal (void) emacs_abort (); initial_terminal = create_terminal (output_initial, NULL); - /* Note: menu-bar.el:menu-bar-update-buffers knows about this - special name of the initial terminal. */ initial_terminal->name = xstrdup ("initial_terminal"); initial_terminal->kboard = initial_kboard; initial_terminal->delete_terminal_hook = &delete_initial_terminal; @@ -688,12 +705,14 @@ or some time later. */); Vdelete_terminal_functions = Qnil; DEFSYM (Qterminal_live_p, "terminal-live-p"); + DEFSYM (Qframe_initial_p, "frame-initial-p"); DEFSYM (Qdelete_terminal_functions, "delete-terminal-functions"); DEFSYM (Qrun_hook_with_args, "run-hook-with-args"); defsubr (&Sdelete_terminal); defsubr (&Sframe_terminal); defsubr (&Sterminal_live_p); + defsubr (&Sframe_initial_p); defsubr (&Sterminal_list); defsubr (&Sterminal_name); defsubr (&Sterminal_parameters); -- cgit v1.2.1 From 887a7ec32d976e673a802e211b28a8c1b397b950 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Thu, 26 Mar 2026 19:13:15 +0200 Subject: ; * src/xdisp.c (produce_special_glyphs): Fix commentary. --- src/xdisp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/xdisp.c b/src/xdisp.c index 179c1bae72b..4f2c5c39900 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -32855,7 +32855,7 @@ produce_special_glyphs (struct it *it, enum display_element_type what, { c = XFIXNUM (val); - /* If something goes wrong defaults to '/'. */ + /* If something goes wrong, fall back to '/'. */ if (CHAR_VALID_P (c)) SET_GLYPH (glyph, c, face_id); else @@ -32916,7 +32916,7 @@ produce_special_glyphs (struct it *it, enum display_element_type what, { c = XFIXNUM (val); - /* If something goes wrong defaults to '$'. */ + /* If something goes wrong, fall back to '$'. */ if (CHAR_VALID_P (c)) SET_GLYPH (glyph, c, face_id); else -- cgit v1.2.1 From 3d8f68db3beffb8ea952b2e512e5e841c5b30835 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Thu, 26 Mar 2026 19:40:58 +0200 Subject: ; Fix comments and doc strings in some C files * src/charset.c (Fmap_charset_chars, Fdefine_charset_internal) (Ffind_charset_region, Ffind_charset_string, Fchar_charset): * src/chartab.c (UNIPROP_COMPRESSED_FORM_P): Fix commentary and doc strings. --- src/charset.c | 10 +++++----- src/chartab.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/charset.c b/src/charset.c index 16ff09a48bf..bcc15e0bd5d 100644 --- a/src/charset.c +++ b/src/charset.c @@ -798,7 +798,7 @@ Optional 4th and 5th arguments FROM-CODE and TO-CODE specify the range of code points (in CHARSET) of target characters on which to map the FUNCTION. Note that these are not character codes, but code points of CHARSET; for the difference see `decode-char' and -`list-charset-chars'. If FROM-CODE is nil or imitted, it stands for +`list-charset-chars'. If FROM-CODE is nil or omitted, it stands for the first code point of CHARSET; if TO-CODE is nil or omitted, it stands for the last code point of CHARSET. @@ -839,7 +839,7 @@ TO-CODE, which are CHARSET code points. */) /* Define a charset according to the arguments. The Nth argument is the Nth attribute of the charset (the last attribute `charset-id' is not included). See the docstring of `define-charset' for the - detail. */ + details. */ DEFUN ("define-charset-internal", Fdefine_charset_internal, Sdefine_charset_internal, charset_arg_max, MANY, 0, @@ -1529,7 +1529,7 @@ BEG and END are buffer positions. Optional arg TABLE if non-nil is a translation table to look up. If the current buffer is unibyte, the returned list may contain -only `ascii', `eight-bit-control', and `eight-bit-graphic'. */) +only `ascii' and `eight-bit'. */) (Lisp_Object beg, Lisp_Object end, Lisp_Object table) { Lisp_Object charsets; @@ -1580,7 +1580,7 @@ DEFUN ("find-charset-string", Ffind_charset_string, Sfind_charset_string, Optional arg TABLE if non-nil is a translation table to look up. If STR is unibyte, the returned list may contain -only `ascii', `eight-bit-control', and `eight-bit-graphic'. */) +only `ascii' amd `eight-bit'. */) (Lisp_Object str, Lisp_Object table) { CHECK_STRING (str); @@ -2035,7 +2035,7 @@ ASCII characters are an exception: for them, this function always returns `ascii'. If optional 2nd arg RESTRICTION is non-nil, it is a list of charsets from which to find the charset. It may also be a coding system. In -that case, find the charset from what supported by that coding system. */) +that case, find the charset in those supported by that coding system. */) (Lisp_Object ch, Lisp_Object restriction) { struct charset *charset; diff --git a/src/chartab.c b/src/chartab.c index 3076f72c06e..7d2710f20a3 100644 --- a/src/chartab.c +++ b/src/chartab.c @@ -78,7 +78,7 @@ sub_char_table_ref_and_range (Lisp_Object, int, int *, int *, /* Nonzero iff OBJ is a string representing uniprop values of 128 succeeding characters (the bottom level of a char-table) by a compressed format. We are sure that no property value has a string - starting with '\001' nor '\002'. */ + starting with '\001' or '\002'. */ #define UNIPROP_COMPRESSED_FORM_P(OBJ) \ (STRINGP (OBJ) && SCHARS (OBJ) > 0 \ && ((SREF (OBJ, 0) == 1 || (SREF (OBJ, 0) == 2)))) -- cgit v1.2.1 From 220ec44341fdbdde17e02a64b84239b391f36fb5 Mon Sep 17 00:00:00 2001 From: Manuel Giraud Date: Tue, 31 Mar 2026 10:47:51 +0200 Subject: ; * src/charset.c (Ffind_charset_string): Fix a typo (bug#80707). --- src/charset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/charset.c b/src/charset.c index bcc15e0bd5d..e4d95653e66 100644 --- a/src/charset.c +++ b/src/charset.c @@ -1580,7 +1580,7 @@ DEFUN ("find-charset-string", Ffind_charset_string, Sfind_charset_string, Optional arg TABLE if non-nil is a translation table to look up. If STR is unibyte, the returned list may contain -only `ascii' amd `eight-bit'. */) +only `ascii' and `eight-bit'. */) (Lisp_Object str, Lisp_Object table) { CHECK_STRING (str); -- cgit v1.2.1 From 956e77f9dde84b43f3969fcf7be5e46e56fbbd65 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Fri, 3 Apr 2026 04:25:58 +0300 Subject: pgtk_set_window_size: Clear out unused code * src/pgtkterm.c (pgtk_set_window_size): Remove unused code. Rename the last two parameters to pixelwidth and pixelheight so they can be used directly (bug#80698). And remove the xg_wm_set_size_hint call because it's called indirectly through xg_frame_set_char_size already. Update the commentary above. --- src/pgtkterm.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/pgtkterm.c b/src/pgtkterm.c index 6dc45604f01..757ff57a9f2 100644 --- a/src/pgtkterm.c +++ b/src/pgtkterm.c @@ -676,33 +676,16 @@ pgtk_set_offset (struct frame *f, int xoff, int yoff, int change_gravity) static void pgtk_set_window_size (struct frame *f, bool change_gravity, - int width, int height) + int pixelwidth, int pixelheight) /* -------------------------------------------------------------------------- - Adjust window pixel size based on given character grid size - Impl is a bit more complex than other terms, need to do some - internal clipping. + Adjust window pixel size based on given width and height. -------------------------------------------------------------------------- */ { - int pixelwidth, pixelheight; - block_input (); - gtk_widget_get_size_request (FRAME_GTK_WIDGET (f), &pixelwidth, - &pixelheight); - - pixelwidth = width; - pixelheight = height; - - for (GtkWidget * w = FRAME_GTK_WIDGET (f); w != NULL; - w = gtk_widget_get_parent (w)) - { - gint wd, hi; - gtk_widget_get_size_request (w, &wd, &hi); - } - f->output_data.pgtk->preferred_width = pixelwidth; f->output_data.pgtk->preferred_height = pixelheight; - xg_wm_set_size_hint (f, 0, 0); + xg_frame_set_char_size (f, pixelwidth, pixelheight); gtk_widget_queue_resize (FRAME_WIDGET (f)); -- cgit v1.2.1 From 52205e38d3ea670f2685e345591eac43d7a9b613 Mon Sep 17 00:00:00 2001 From: Stéphane Marks Date: Thu, 12 Mar 2026 14:23:51 -0400 Subject: Inform macOS Accessibility Zoom of cursor position (bug#80624) Enable cursor focus tracking for visually-impaired users that rely on macOS Zoom screen magnification. * src/nsterm.m: Import ApplicationServices.h. (ns_ua_zoom_enabled_p, ns_cg_rect_flip_y, ns_UAZoomChangeFocus): New static function. Advise UAZoomChangeFocus of potentially new cursor position. (ns_update_end): Call ns_UAZoomChangeFocus. (ns_draw_window_cursor): Cache the cursor position. (applicationDidFinishLaunching): NSLog Accessibility API permissions AXIsProcessTrusted. (windowDidBecomeKey): Schedule a call to ns_UAZoomChangeFocus. (deferred_UAZoomChangeFocus_handler): New view method to call ns_UAZoomChangeFocus. (accessibilityFrame): New view method to help UAZoomChangeFocus. (initFrameFromEmacs): Initialize ns_UAZoom_cursor_rect_new and ns_UAZoom_cursor_rect_old. * etc/NEWS: Announce the change. --- src/nsterm.m | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) (limited to 'src') diff --git a/src/nsterm.m b/src/nsterm.m index af38847b344..b16d020ebad 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -71,6 +71,12 @@ GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu) #include "macfont.h" #include #include +/* ApplicationServices provides the macOS accessibility Zoom API + UAZoomEnabled and UAZoomChangeFocus (UniversalAccess framework). + Carbon.h already pulls in ApplicationServices on most SDK versions, + but the explicit import makes the dependency visible and guards + against SDK changes. */ +#import #endif static EmacsMenu *dockMenu; @@ -1080,6 +1086,126 @@ ns_update_begin (struct frame *f) [view lockFocus]; } +/* -------------------------------------------------------------------------- + macOS Accessibility Zoom Support + -------------------------------------------------------------------------- */ +#ifdef NS_IMPL_COCOA + +static BOOL ns_is_UAZoomEnabled = NO; +static unsigned long ns_UAZoomEnabled_last_called_time_ns = 0; +static const unsigned long NS_UAZOOMENABLED_CACHE_INTERVAL_NS = + (unsigned long)(500 * NSEC_PER_MSEC); /* 500ms. */ +static NSTimeInterval NS_UAZOOMENABLED_DEFER_INTERVAL_SECS = 0.2; /* 200ms. */ +static NSTimer *ns_deferred_UAZoomChangeFocus_timer = nil; + +static BOOL +ns_ua_zoom_enabled_p (void) +/* -------------------------------------------------------------------------- + Return the cached result of UAZoomEnabled. Refresh the cache every + NS_UAZOOMENABLED_CACHE_INTERVAL_NS nanoseconds. + + We cache the result to avoid the macOS Mach IPC Accessibility Server + round trip cost on every Emacs cursor update. Since enabling Zoom + requires an explicit user UI action that takes real user time, the + cache TTL should be invisible to the user. + + Use clock_gettime_nsec_np not CFAbsoluteTimeGetCurrent which depends + on the wall clock which can be reset by the user or by NTP. + + Main-thread-only and called from ns_update_end, below. + -------------------------------------------------------------------------- */ +{ + /* User-space equivalent to mach_absolute_time. */ + unsigned long now_ns = clock_gettime_nsec_np (CLOCK_UPTIME_RAW); + if (now_ns - ns_UAZoomEnabled_last_called_time_ns + > NS_UAZOOMENABLED_CACHE_INTERVAL_NS) + { + ns_is_UAZoomEnabled = UAZoomEnabled (); + ns_UAZoomEnabled_last_called_time_ns = now_ns; + } + return ns_is_UAZoomEnabled; +} + +static inline CGRect +ns_cg_rect_flip_y (CGRect r) +/* -------------------------------------------------------------------------- + Convert a CGRect from Cocoa screen coordinates (origin at bottom-left + of the primary display) to CoreGraphics coordinates (origin at + top-left of the primary display). CoreGraphics defines its + coordinate origin at the top-left corner of the primary display and + all screens share this global coordinate space, so the flip always + uses the primary display height regardless of which screen R is on. + -------------------------------------------------------------------------- */ +{ + CGDirectDisplayID mainID = CGMainDisplayID (); + if (mainID == kCGNullDirectDisplay) + return r; + CGFloat primaryH = CGDisplayBounds (mainID).size.height; + if (primaryH <= 0) + return r; + r.origin.y = primaryH - r.origin.y - r.size.height; + return r; +} + +/* Cache cursor rects to call UAZoomChangeFocus only when the cursor + position has changed, not merely when the cursor is blinking. + See ns_draw_window_cursor and ns_update_end. */ +static NSRect ns_UAZoom_cursor_rect_new; +static NSRect ns_UAZoom_cursor_rect_old; + +/* Track Zoom state per display cycle. Update the macOS Zoom cursor + position when Zoom transitions to enabled. */ +static BOOL ns_update_was_UAZoomEnabled = NO; + +static void +ns_UAZoomChangeFocus (EmacsView *view, BOOL force) +/* -------------------------------------------------------------------------- + Advise macOS Accessibility Zoom UAZoomChangeFocus of a potentially + new cursor position. Force an updated position when Zoom transitions + to enabled, or when the frame gets focus. + -------------------------------------------------------------------------- */ +{ + if (ns_ua_zoom_enabled_p ()) + { + force = force || !ns_update_was_UAZoomEnabled; + ns_update_was_UAZoomEnabled = YES; + if (NSIsEmptyRect (ns_UAZoom_cursor_rect_new)) + return; + if (force || !NSEqualRects (ns_UAZoom_cursor_rect_new, + ns_UAZoom_cursor_rect_old)) + { + ns_UAZoom_cursor_rect_old = ns_UAZoom_cursor_rect_new; + NSRect windowRect = [view convertRect:ns_UAZoom_cursor_rect_new + toView:nil]; + NSRect screenRect = [[view window] convertRectToScreen:windowRect]; + CGRect cgRect = ns_cg_rect_flip_y (NSRectToCGRect (screenRect)); + /* Some versions of macOS can ignore tiny rects, so we + slightly expand a tiny one. Since we care mostly about its + origin, this should be innocuous. */ + cgRect.size.width = MAX (cgRect.size.width, 6); + cgRect.size.height = MAX (cgRect.size.height, 10); + if (force) + { + /* UAZoomChangeFocus needs old and new cursor positions to + be different, and also it sometimes needs a kick. In + both cases, we fake a cursor move followed by the real + cursor move. */ + CGRect cgRectJiggle = CGRectOffset (cgRect, 1.0, 1.0); + if (UAZoomChangeFocus (&cgRectJiggle, NULL, + kUAZoomFocusTypeInsertionPoint)) + NSLog (@"UAZoomChangeFocus jiggle failed"); + } + if (UAZoomChangeFocus (&cgRect, NULL, + kUAZoomFocusTypeInsertionPoint)) + NSLog (@"UAZoomChangeFocus failed"); + NSAccessibilityPostNotification + (view, NSAccessibilityFocusedUIElementChangedNotification); + } + } + else + ns_update_was_UAZoomEnabled = NO; +} +#endif /* NS_IMPL_COCOA */ static void ns_update_end (struct frame *f) @@ -1102,6 +1228,10 @@ ns_update_end (struct frame *f) [[view window] flushWindow]; #endif +#ifdef NS_IMPL_COCOA + ns_UAZoomChangeFocus (view, false); +#endif + unblock_input (); ns_updating_frame = NULL; } @@ -3232,6 +3362,16 @@ ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, /* Prevent the cursor from being drawn outside the text area. */ r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA)); +#ifdef NS_IMPL_COCOA + /* Cache the cursor rect for macOS Accessibility Zoom integration (see + ns_update_end). Only store the rect for the active cursor --- + inactive windows must not overwrite the value because redisplay may + draw multiple windows per frame and the drawing order is not + guaranteed. */ + if (active_p) + ns_UAZoom_cursor_rect_new = r; +#endif + ns_focus (f, NULL, 0); NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; @@ -6377,6 +6517,14 @@ ns_term_shutdown (int sig) } #endif +#ifdef NS_IMPL_COCOA + /* Is accessibility enabled for this process/bundle? */ + if (AXIsProcessTrusted()) + NSLog (@"Emacs is macOS AXIsProcessTrusted"); + else + NSLog (@"Emacs is not macOS AXIsProcessTrusted"); +#endif + ns_send_appdefined (-2); } @@ -7288,6 +7436,12 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action) return [self firstRectForCharacterRange: range]; } +- (NSRect)accessibilityFrame +{ + EmacsView *view = FRAME_NS_VIEW (emacsframe); + return [[view window] convertRectToScreen: ns_UAZoom_cursor_rect_new]; +} + #endif /* NS_IMPL_COCOA */ /*********************************************************************** @@ -8242,12 +8396,48 @@ ns_in_echo_area (void) ns_frame_rehighlight (emacsframe); [self adjustEmacsFrameRect]; +#ifdef NS_IMPL_COCOA + EmacsView *view = FRAME_NS_VIEW (emacsframe); + /* Make sure we have focus and the timer isn't already scheduled. */ + if (self.window.firstResponder == view + && !ns_deferred_UAZoomChangeFocus_timer) + { + /* Calls to ns_UAZoomChangeFocus are synchronous. We defer the + call to give macOS time to finish window compositing or the + calls can be silently ignored by the Zoom daemon and with no + errors reported. This also helps ensure ns_draw_window_cursor + has populated ns_UAZoom_cursor_rect_new. The 200 ms delay was + chosen as a balance between macOS headroom and user + perception. */ + ns_deferred_UAZoomChangeFocus_timer + = [[NSTimer + scheduledTimerWithTimeInterval: + NS_UAZOOMENABLED_DEFER_INTERVAL_SECS + target: self + selector: + @selector (deferred_UAZoomChangeFocus_handler:) + userInfo: 0 + repeats: NO] + retain]; + } +#endif + event.kind = FOCUS_IN_EVENT; XSETFRAME (event.frame_or_window, emacsframe); kbd_buffer_store_event (&event); ns_send_appdefined (-1); // Kick main loop } +#ifdef NS_IMPL_COCOA +- (void)deferred_UAZoomChangeFocus_handler: (NSTimer *)timer +{ + EmacsView *view = FRAME_NS_VIEW (emacsframe); + ns_UAZoomChangeFocus (view, true); + [ns_deferred_UAZoomChangeFocus_timer invalidate]; + [ns_deferred_UAZoomChangeFocus_timer release]; + ns_deferred_UAZoomChangeFocus_timer = nil; +} +#endif - (void)windowDidResignKey: (NSNotification *)notification /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */ @@ -8344,6 +8534,13 @@ ns_in_echo_area (void) FRAME_NS_VIEW (f) = self; emacsframe = f; + +#ifdef NS_IMPL_COCOA + /* macOS Accessibility Zoom Support. */ + ns_UAZoom_cursor_rect_new = NSZeroRect; + ns_UAZoom_cursor_rect_old = NSZeroRect; +#endif + #ifdef NS_IMPL_COCOA old_title = 0; maximizing_resize = NO; -- cgit v1.2.1 From e7751405d024134a0073e0ac47caef2b71418a1b Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 4 Apr 2026 09:56:32 +0300 Subject: ; Improve documentation of 'make-temp-file' * doc/lispref/files.texi (Unique File Names): * lisp/files.el (make-temp-file): * src/fileio.c (Fmake_temp_file_internal): Doc fixes. --- src/fileio.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/fileio.c b/src/fileio.c index beb020810be..eb64b59fcf2 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -774,6 +774,9 @@ Do not expand PREFIX; a non-absolute PREFIX is relative to the Emacs working directory. If TEXT is a string, insert it into the newly created file. +On Posix systems, the file/directory is created with access mode bits +that limit access to the current user. + Signal an error if the file could not be created. This function does not grok magic file names. */) -- cgit v1.2.1 From 01a9d78a7e4c7d7fa5b799e4fdc2caf77a012734 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 4 Apr 2026 18:22:05 +0300 Subject: Fix use of special glyphs with faces from display-table * src/xdisp.c (produce_special_glyphs): Fix the case where the display-table specifies a face for truncation/continuation glyphs. (Bug#80693) --- src/xdisp.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/xdisp.c b/src/xdisp.c index 4f2c5c39900..bd07f9983ff 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -32842,6 +32842,8 @@ produce_special_glyphs (struct it *it, enum display_element_type what, /* Mirror for R2L. */ if (direction == R2L) { + face_id = GLYPH_CODE_FACE (gc); + /* Try bidi mirroring first. */ int c = bidi_mirror_char (GLYPH_CODE_CHAR (gc)); @@ -32861,10 +32863,17 @@ produce_special_glyphs (struct it *it, enum display_element_type what, else SET_GLYPH (glyph, '/', face_id); } + else + SET_GLYPH_FROM_GLYPH_CODE (glyph, gc); } else + { + struct face *face = FACE_FROM_ID (it->f, face_id); + int id = FACE_FOR_CHAR (it->f, face, c, -1, Qnil); + /* Bidi mirroring. */ - SET_GLYPH (glyph, c, face_id); + SET_GLYPH (glyph, c, id); + } } else /* No mirroring. */ @@ -32903,6 +32912,8 @@ produce_special_glyphs (struct it *it, enum display_element_type what, if (((it->bidi_it.paragraph_dir == R2L) && !left_edge_p) || ((it->bidi_it.paragraph_dir == L2R) && left_edge_p)) { + face_id = GLYPH_CODE_FACE (gc); + /* Try bidi mirroring first. */ int c = bidi_mirror_char (GLYPH_CODE_CHAR (gc)); @@ -32922,6 +32933,8 @@ produce_special_glyphs (struct it *it, enum display_element_type what, else SET_GLYPH (glyph, '$', face_id); } + else + SET_GLYPH_FROM_GLYPH_CODE (glyph, gc); } else { -- cgit v1.2.1