From 568920a5b703e80c43e1b6f31778ea5776218a1e Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sun, 25 Sep 2022 13:22:45 +0000 Subject: [PATCH] Implement font-use-system-font on Haiku * doc/emacs/frames.texi (Fonts): Update documentation to say what font-use-system-font really does and where it can be used. * src/haiku_font_support.cc (language_code_points): Fix coding style. (font_style_to_flags, be_font_style_to_flags): Accept const char *. (be_send_font_settings, be_listen_font_settings) (be_lock_font_defaults, be_unlock_font_defaults) (be_get_font_default, be_get_font_size): New functions used to retrieve default font data. * src/haiku_io.c (haiku_len): Handle FONT_CHANGE_EVENT. * src/haiku_support.h (enum haiku_event_type): New event type FONT_CHANGE_EVENT. (enum haiku_what_font): New enum. (struct haiku_font_change_event): New struct. * src/haikufont.c (Ffont_get_system_normal_font) (Ffont_get_system_font, haiku_handle_font_change_event): New functions. (syms_of_haikufont): Provide `dynamic-setting' and define new variables and subrs. * src/haikuterm.c (haiku_default_font_parameter): Use system font. (haiku_read_socket): Handle FONT_CHANGE_EVENTS. (haiku_term_init): Start listening for font configuration changes. * src/haikuterm.h: Update prototypes. * src/xsettings.c (Ffont_get_system_normal_font) (Ffont_get_system_font): Update doc string. (syms_of_xsettings): Replace calls to intern with a static string. --- doc/emacs/frames.texi | 11 +- src/haiku_font_support.cc | 282 +++++++++++++++++++++++++++++++++++++- src/haiku_io.c | 2 + src/haiku_support.h | 31 ++++- src/haikufont.c | 106 ++++++++++++++ src/haikuterm.c | 25 ++-- src/haikuterm.h | 6 + src/xsettings.c | 16 ++- 8 files changed, 449 insertions(+), 30 deletions(-) diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi index 8a255fa40fb..3ff47c6ffcf 100644 --- a/doc/emacs/frames.texi +++ b/doc/emacs/frames.texi @@ -652,14 +652,15 @@ resources file to take effect. @xref{Resources}. Do not quote font names in X resource files. @item -If you are running Emacs on the GNOME desktop, you can tell Emacs to -use the default system font by setting the variable +If you are running Emacs on the GNOME desktop or Haiku, you can tell +Emacs to adjust the frame's default font along with changes to the +default system font by setting the variable @code{font-use-system-font} to @code{t} (the default is @code{nil}). For this to work, Emacs must have been compiled with support for Gsettings (or the older Gconf). (To be specific, the Gsettings -configuration names used are -@samp{org.gnome.desktop.interface monospace-font-name} and -@samp{org.gnome.desktop.interface font-name}.) +configuration names used are @samp{org.gnome.desktop.interface +monospace-font-name} and @samp{org.gnome.desktop.interface +font-name}.) @item Use the command line option @samp{-fn} (or @samp{--font}). @xref{Font diff --git a/src/haiku_font_support.cc b/src/haiku_font_support.cc index d824cc59ae2..9a2492c9a13 100644 --- a/src/haiku_font_support.cc +++ b/src/haiku_font_support.cc @@ -21,6 +21,14 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -39,15 +47,57 @@ struct font_object_cache_bucket static struct font_object_cache_bucket *font_object_cache[2048]; +/* The current global monospace family and style. */ +static char *fixed_family, *fixed_style; + +/* The current global variable-width family and style. */ +static char *default_family, *default_style; + +/* The sizes of each of those fonts. */ +static float default_size, fixed_size; + +/* The locker controlling access to those variables. */ +static BLocker default_locker; + /* Haiku doesn't expose font language data in BFont objects. Thus, we select a few representative characters for each supported `:lang' (currently Chinese, Korean and Japanese,) and test for those instead. */ static int language_code_points[MAX_LANGUAGE][3] = - {{20154, 20754, 22996}, /* Chinese. */ - {51312, 49440, 44544}, /* Korean. */ - {26085, 26412, 12371}, /* Japanese. */}; + { + {20154, 20754, 22996}, /* Chinese. */ + {51312, 49440, 44544}, /* Korean. */ + {26085, 26412, 12371}, /* Japanese. */ + }; + +static void be_send_font_settings (void); + +/* Looper used to track changes to system-wide font settings. */ +class EmacsFontMonitorLooper : public BLooper +{ + void + MessageReceived (BMessage *msg) + { + int32 opcode; + + if (msg->what != B_NODE_MONITOR) + return; + + if (msg->FindInt32 ("opcode", &opcode) != B_OK) + return; + + if (opcode != B_STAT_CHANGED) + return; + + /* Wait a little for any message to be completely written after + the file's modification time changes. */ + snooze (10000); + + /* Read and apply font settings. */ + be_send_font_settings (); + } +}; static unsigned int hash_string (const char *name_or_style) @@ -288,12 +338,15 @@ BFont_nchar_bounds (void *font, const char *mb_str, int *advance, } static void -font_style_to_flags (char *st, struct haiku_font_pattern *pattern) +font_style_to_flags (const char *style_string, + struct haiku_font_pattern *pattern) { - char *style = strdup (st); + char *style; char *token; int tok = 0; + style = strdup (style_string); + if (!style) return; @@ -385,7 +438,8 @@ font_style_to_flags (char *st, struct haiku_font_pattern *pattern) pattern->specified &= ~FSPEC_WEIGHT; pattern->specified &= ~FSPEC_WIDTH; pattern->specified |= FSPEC_STYLE; - std::strncpy ((char *) &pattern->style, st, + std::strncpy ((char *) &pattern->style, + style_string, sizeof pattern->style - 1); pattern->style[sizeof pattern->style - 1] = '\0'; } @@ -887,7 +941,7 @@ be_evict_font_cache (void) } void -be_font_style_to_flags (char *style, struct haiku_font_pattern *pattern) +be_font_style_to_flags (const char *style, struct haiku_font_pattern *pattern) { pattern->specified = 0; @@ -939,3 +993,217 @@ be_set_font_antialiasing (void *font, bool antialias_p) ? B_FORCE_ANTIALIASING : B_DISABLE_ANTIALIASING); } + +static void +be_send_font_settings (void) +{ + struct haiku_font_change_event rq; + BFile file; + BPath path; + status_t rc; + BMessage message; + font_family family; + font_style style; + const char *new_family, *new_style; + float new_size; + + rc = find_directory (B_USER_SETTINGS_DIRECTORY, &path); + + if (rc < B_OK) + return; + + rc = path.Append ("system/app_server/fonts"); + + if (rc < B_OK) + return; + + if (file.SetTo (path.Path (), B_READ_ONLY) != B_OK) + return; + + if (message.Unflatten (&file) != B_OK) + return; + + /* Now, populate with new values. */ + if (!default_locker.Lock ()) + gui_abort ("Failed to lock font data locker"); + + /* Obtain default values. */ + be_fixed_font->GetFamilyAndStyle (&family, &style); + default_size = be_fixed_font->Size (); + + /* And the new values. */ + new_family = message.GetString ("fixed family", family); + new_style = message.GetString ("fixed style", style); + new_size = message.GetFloat ("fixed size", default_size); + + /* If it turns out the fixed family changed, send the new family and + style. */ + + if (!fixed_family || !fixed_style + || new_size != fixed_size + || strcmp (new_family, fixed_family) + || strcmp (new_style, fixed_style)) + { + memset (&rq, 0, sizeof rq); + strncpy (rq.new_family, (char *) new_family, + sizeof rq.new_family - 1); + strncpy (rq.new_style, (char *) new_style, + sizeof rq.new_style - 1); + rq.new_size = new_size; + rq.what = FIXED_FAMILY; + + haiku_write (FONT_CHANGE_EVENT, &rq); + } + + if (fixed_family) + free (fixed_family); + + if (fixed_style) + free (fixed_style); + + fixed_family = strdup (new_family); + fixed_style = strdup (new_style); + fixed_size = new_size; + + /* Obtain default values. */ + be_plain_font->GetFamilyAndStyle (&family, &style); + default_size = be_plain_font->Size (); + + /* And the new values. */ + new_family = message.GetString ("plain family", family); + new_style = message.GetString ("plain style", style); + new_size = message.GetFloat ("plain style", default_size); + + if (!default_family || !default_style + || new_size != default_size + || strcmp (new_family, default_family) + || strcmp (new_style, default_style)) + { + memset (&rq, 0, sizeof rq); + strncpy (rq.new_family, (char *) new_family, + sizeof rq.new_family - 1); + strncpy (rq.new_style, (char *) new_style, + sizeof rq.new_style - 1); + rq.new_size = new_size; + rq.what = DEFAULT_FAMILY; + + haiku_write (FONT_CHANGE_EVENT, &rq); + } + + if (default_family) + free (default_family); + + if (default_style) + free (default_style); + + default_family = strdup (new_family); + default_style = strdup (new_style); + default_size = new_size; + + default_locker.Unlock (); +} + +/* Begin listening to font settings changes, by installing a node + watcher. This relies on the settings file already being present + and has several inherent race conditions, but users shouldn't be + changing font settings very quickly. */ + +void +be_listen_font_settings (void) +{ + BPath path; + status_t rc; + BNode node; + node_ref node_ref; + EmacsFontMonitorLooper *looper; + font_family family; + font_style style; + + /* Set up initial values. */ + be_fixed_font->GetFamilyAndStyle (&family, &style); + fixed_family = strdup (family); + fixed_style = strdup (style); + fixed_size = be_fixed_font->Size (); + + be_plain_font->GetFamilyAndStyle (&family, &style); + default_family = strdup (family); + default_style = strdup (style); + default_size = be_plain_font->Size (); + + rc = find_directory (B_USER_SETTINGS_DIRECTORY, &path); + + if (rc < B_OK) + return; + + rc = path.Append ("system/app_server/fonts"); + + if (rc < B_OK) + return; + + rc = node.SetTo (path.Path ()); + + if (rc < B_OK) + return; + + if (node.GetNodeRef (&node_ref) < B_OK) + return; + + looper = new EmacsFontMonitorLooper; + + if (watch_node (&node_ref, B_WATCH_STAT, looper) < B_OK) + { + delete looper; + return; + } + + looper->Run (); +} + +bool +be_lock_font_defaults (void) +{ + return default_locker.Lock (); +} + +void +be_unlock_font_defaults (void) +{ + return default_locker.Unlock (); +} + +const char * +be_get_font_default (enum haiku_what_font what) +{ + switch (what) + { + case FIXED_FAMILY: + return fixed_family; + + case FIXED_STYLE: + return fixed_style; + + case DEFAULT_FAMILY: + return default_family; + + case DEFAULT_STYLE: + return default_style; + } + + return NULL; +} + +int +be_get_font_size (enum haiku_what_font what) +{ + switch (what) + { + case FIXED_FAMILY: + return fixed_size; + + case DEFAULT_FAMILY: + return default_size; + + default: + return 0; + } +} diff --git a/src/haiku_io.c b/src/haiku_io.c index 5cc70f6f71f..6ef6f2ebd06 100644 --- a/src/haiku_io.c +++ b/src/haiku_io.c @@ -109,6 +109,8 @@ haiku_len (enum haiku_event_type type) return sizeof (struct haiku_screen_changed_event); case CLIPBOARD_CHANGED_EVENT: return sizeof (struct haiku_clipboard_changed_event); + case FONT_CHANGE_EVENT: + return sizeof (struct haiku_font_change_event); } emacs_abort (); diff --git a/src/haiku_support.h b/src/haiku_support.h index d66dbc5fa60..e940e69bf11 100644 --- a/src/haiku_support.h +++ b/src/haiku_support.h @@ -115,6 +115,7 @@ enum haiku_event_type SCREEN_CHANGED_EVENT, MENU_BAR_LEFT, CLIPBOARD_CHANGED_EVENT, + FONT_CHANGE_EVENT, }; struct haiku_clipboard_changed_event @@ -442,6 +443,27 @@ struct haiku_menu_bar_state_event void *window; }; +enum haiku_what_font + { + FIXED_FAMILY, + FIXED_STYLE, + DEFAULT_FAMILY, + DEFAULT_STYLE, + }; + +struct haiku_font_change_event +{ + /* New family, style and size of the font. */ + haiku_font_family_or_style new_family; + haiku_font_family_or_style new_style; + int new_size; + + /* What changed. FIXED_FAMILY means this is the new fixed font. + DEFAULT_FAMILY means this is the new plain font. The other enums + have no meaning. */ + enum haiku_what_font what; +}; + struct haiku_session_manager_reply { bool quit_reply; @@ -697,7 +719,7 @@ extern int be_get_display_screens (void); extern bool be_use_subpixel_antialiasing (void); extern const char *be_find_setting (const char *); extern haiku_font_family_or_style *be_list_font_families (size_t *); -extern void be_font_style_to_flags (char *, struct haiku_font_pattern *); +extern void be_font_style_to_flags (const char *, struct haiku_font_pattern *); extern void *be_open_font_at_index (int, int, float); extern void be_set_font_antialiasing (void *, bool); extern int be_get_ui_color (const char *, uint32_t *); @@ -732,6 +754,13 @@ extern void be_unlock_window (void *); extern bool be_get_explicit_workarea (int *, int *, int *, int *); extern void be_clear_grab_view (void); extern void be_set_use_frame_synchronization (void *, bool); + +extern void be_listen_font_settings (void); + +extern bool be_lock_font_defaults (void); +extern const char *be_get_font_default (enum haiku_what_font); +extern int be_get_font_size (enum haiku_what_font); +extern void be_unlock_font_defaults (void); #ifdef __cplusplus } diff --git a/src/haikufont.c b/src/haikufont.c index 4af9ff9d77f..335c312cebe 100644 --- a/src/haikufont.c +++ b/src/haikufont.c @@ -1311,6 +1311,98 @@ in the font selection dialog. */) QCsize, lsize); } +DEFUN ("font-get-system-normal-font", Ffont_get_system_normal_font, + Sfont_get_system_normal_font, 0, 0, 0, + doc: /* SKIP: real doc in xsettings.c. */) + (void) +{ + Lisp_Object value; + const char *name, *style; + struct haiku_font_pattern pattern; + Lisp_Object lfamily, lweight, lslant, lwidth, ladstyle; + int size; + + if (!be_lock_font_defaults ()) + return Qnil; + + name = be_get_font_default (DEFAULT_FAMILY); + style = be_get_font_default (DEFAULT_STYLE); + size = be_get_font_size (DEFAULT_FAMILY); + + be_font_style_to_flags (style, &pattern); + + lfamily = build_string_from_utf8 (name); + lweight = (pattern.specified & FSPEC_WEIGHT + ? haikufont_weight_to_lisp (pattern.weight) : Qnil); + lslant = (pattern.specified & FSPEC_SLANT + ? haikufont_slant_to_lisp (pattern.slant) : Qnil); + lwidth = (pattern.specified & FSPEC_WIDTH + ? haikufont_width_to_lisp (pattern.width) : Qnil); + ladstyle = (pattern.specified & FSPEC_STYLE + ? intern (pattern.style) : Qnil); + + value = CALLN (Ffont_spec, QCfamily, lfamily, + QCweight, lweight, QCslant, lslant, + QCwidth, lwidth, QCadstyle, ladstyle, + QCsize, make_fixnum (size)); + be_unlock_font_defaults (); + + return value; +} + +DEFUN ("font-get-system-font", Ffont_get_system_font, + Sfont_get_system_font, 0, 0, 0, + doc: /* SKIP: real doc in xsettings.c. */) + (void) +{ + Lisp_Object value; + const char *name, *style; + struct haiku_font_pattern pattern; + Lisp_Object lfamily, lweight, lslant, lwidth, ladstyle; + int size; + + if (!be_lock_font_defaults ()) + return Qnil; + + name = be_get_font_default (FIXED_FAMILY); + style = be_get_font_default (FIXED_STYLE); + size = be_get_font_size (FIXED_FAMILY); + + be_font_style_to_flags (style, &pattern); + + lfamily = build_string_from_utf8 (name); + lweight = (pattern.specified & FSPEC_WEIGHT + ? haikufont_weight_to_lisp (pattern.weight) : Qnil); + lslant = (pattern.specified & FSPEC_SLANT + ? haikufont_slant_to_lisp (pattern.slant) : Qnil); + lwidth = (pattern.specified & FSPEC_WIDTH + ? haikufont_width_to_lisp (pattern.width) : Qnil); + ladstyle = (pattern.specified & FSPEC_STYLE + ? intern (pattern.style) : Qnil); + + value = CALLN (Ffont_spec, QCfamily, lfamily, + QCweight, lweight, QCslant, lslant, + QCwidth, lwidth, QCadstyle, ladstyle, + QCsize, make_fixnum (size)); + be_unlock_font_defaults (); + + return value; +} + +void +haiku_handle_font_change_event (struct haiku_font_change_event *event, + struct input_event *ie) +{ + ie->kind = CONFIG_CHANGED_EVENT; + + /* This is the name of the display. */ + ie->frame_or_window = XCAR (x_display_list->name_list_element); + + /* And this is the font that changed. */ + ie->arg = (event->what == FIXED_FAMILY + ? Qmonospace_font_name : Qfont_name); +} + static void syms_of_haikufont_for_pdumper (void) { @@ -1344,6 +1436,14 @@ syms_of_haikufont (void) DEFSYM (QCindices, ":indices"); + DEFSYM (Qmonospace_font_name, "monospace-font-name"); + DEFSYM (Qfont_name, "font-name"); + DEFSYM (Qdynamic_setting, "dynamic-setting"); + + DEFVAR_BOOL ("font-use-system-font", use_system_font, + doc: /* SKIP: real doc in xsettings.c. */); + use_system_font = false; + #ifdef USE_BE_CAIRO Fput (Qhaiku, Qfont_driver_superseded_by, Qftcr); #endif @@ -1353,6 +1453,12 @@ syms_of_haikufont (void) staticpro (&font_cache); defsubr (&Sx_select_font); + defsubr (&Sfont_get_system_normal_font); + defsubr (&Sfont_get_system_font); be_init_font_data (); + + /* This tells loadup to load dynamic-setting.el, which handles + config-changed events. */ + Fprovide (Qdynamic_setting, Qnil); } diff --git a/src/haikuterm.c b/src/haikuterm.c index b0832059bae..838eb128fa6 100644 --- a/src/haikuterm.c +++ b/src/haikuterm.c @@ -2988,18 +2988,11 @@ haiku_default_font_parameter (struct frame *f, Lisp_Object parms) font_param = Qnil; if (NILP (font_param)) - { - /* System font should take precedence over X resources. We suggest this - regardless of font-use-system-font because .emacs may not have been - read yet. */ - struct haiku_font_pattern ptn; - ptn.specified = 0; - - BFont_populate_fixed_family (&ptn); - - if (ptn.specified & FSPEC_FAMILY) - font = font_open_by_name (f, build_unibyte_string (ptn.family)); - } + /* System font should take precedence over X resources. We + suggest this regardless of font-use-system-font because .emacs + may not have been read yet. Returning a font-spec is Haiku + specific behavior. */ + font = font_open_by_spec (f, Ffont_get_system_font ()); if (NILP (font)) font = !NILP (font_param) ? font_param @@ -4027,6 +4020,11 @@ haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit) inev.kind = SAVE_SESSION_EVENT; inev.arg = Qt; break; + case FONT_CHANGE_EVENT: + /* This generates CONFIG_CHANGED_EVENTs, which are then + handled in Lisp. */ + haiku_handle_font_change_event (buf, &inev); + break; case KEY_UP: case DUMMY_EVENT: default: @@ -4417,6 +4415,9 @@ haiku_term_init (void) dpyinfo->default_name = build_string ("GNU Emacs"); haiku_start_watching_selections (); + + /* Start listening for font configuration changes. */ + be_listen_font_settings (); unblock_input (); return dpyinfo; diff --git a/src/haikuterm.h b/src/haikuterm.h index b603c0a482f..86274fd42a3 100644 --- a/src/haikuterm.h +++ b/src/haikuterm.h @@ -34,6 +34,9 @@ along with GNU Emacs. If not, see . */ #define HAVE_CHAR_CACHE_MAX 65535 +/* This is really defined in haiku_support.h. */ +struct haiku_font_change_event; + extern int popup_activated_p; struct haikufont_info @@ -361,4 +364,7 @@ extern void haiku_merge_cursor_foreground (struct glyph_string *, unsigned long unsigned long *); extern void haiku_handle_selection_clear (struct input_event *); extern void haiku_start_watching_selections (void); +extern void haiku_handle_font_change_event (struct haiku_font_change_event *, + struct input_event *); + #endif /* _HAIKU_TERM_H_ */ diff --git a/src/xsettings.c b/src/xsettings.c index 9c60ff825a4..e4a9865d686 100644 --- a/src/xsettings.c +++ b/src/xsettings.c @@ -1225,7 +1225,8 @@ xsettings_get_font_options (void) DEFUN ("font-get-system-normal-font", Ffont_get_system_normal_font, Sfont_get_system_normal_font, 0, 0, 0, - doc: /* Get the system default application font. */) + doc: /* Get the system default application font. +The font is returned as either a font-spec or font name. */) (void) { return current_font ? build_string (current_font) : Qnil; @@ -1233,7 +1234,8 @@ DEFUN ("font-get-system-normal-font", Ffont_get_system_normal_font, DEFUN ("font-get-system-font", Ffont_get_system_font, Sfont_get_system_font, 0, 0, 0, - doc: /* Get the system default fixed width font. */) + doc: /* Get the system default fixed width font. +The font is returned as either a font-spec or font name. */) (void) { return current_mono_font ? build_string (current_mono_font) : Qnil; @@ -1282,6 +1284,10 @@ syms_of_xsettings (void) DEFSYM (Qmonospace_font_name, "monospace-font-name"); DEFSYM (Qfont_name, "font-name"); DEFSYM (Qfont_render, "font-render"); + DEFSYM (Qdynamic_setting, "dynamic-setting"); + DEFSYM (Qfont_render_setting, "font-render-setting"); + DEFSYM (Qsystem_font_setting, "system-font-setting"); + defsubr (&Sfont_get_system_font); defsubr (&Sfont_get_system_normal_font); @@ -1297,9 +1303,9 @@ If this variable is nil, Emacs ignores system font changes. */); Vxft_settings = empty_unibyte_string; #if defined USE_CAIRO || defined HAVE_XFT - Fprovide (intern_c_string ("font-render-setting"), Qnil); + Fprovide (Qfont_render_setting, Qnil); #if defined (HAVE_GCONF) || defined (HAVE_GSETTINGS) - Fprovide (intern_c_string ("system-font-setting"), Qnil); + Fprovide (Qsystem_font_setting, Qnil); #endif #endif @@ -1307,5 +1313,5 @@ If this variable is nil, Emacs ignores system font changes. */); DEFSYM (Qtool_bar_style, "tool-bar-style"); defsubr (&Stool_bar_get_system_style); - Fprovide (intern_c_string ("dynamic-setting"), Qnil); + Fprovide (Qdynamic_setting, Qnil); } -- 2.39.2