From 637fa9880877f485842b7aca18146a1f12c05b96 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jan=20Dj=C3=A4rv?= Date: Tue, 17 Nov 2009 08:21:23 +0000 Subject: [PATCH] Handle system default font and changing font parameters. * xterm.h (struct x_display_info): Add atoms and Window for xsettings. * xterm.c (handle_one_xevent): Call xft_settings_event for ClientMessage, PropertyNotify and DestroyNotify. (x_term_init): If we have XFT, get DPI from Xft.dpi. Call xsettings_initialize. * xftfont.c (xftfont_fix_match): New function. (xftfont_open): Call XftDefaultSubstitute before XftFontMatch. Call xftfont_fix_match after XftFontMatch. * xfont.c (xfont_driver): Initialize all members. * xfns.c (x_default_font_parameter): Try font from Ffont_get_system_font. Do not get font from x_default_parameter if we got one from Ffont_get_system_font. (Fx_select_font): Get the defaut font name from :name of FRAME_FONT (f). * w32font.c (w32font_driver): Initialize all members. * termhooks.h (enum event_kind): CONFIG_CHANGED_EVENT is new. * lisp.h: Declare syms_of_xsettings. * keyboard.c (kbd_buffer_get_event, make_lispy_event): Handle CONFIG_CHANGED_EVENT. * ftfont.c (ftfont_filter_properties): New function. * frame.c (x_set_font): Remove unused variable lval. * font.h (struct font_driver): filter_properties is new. * font.c (font_put_extra): Don't return if val is nil, it means boolean option is off. (font_parse_fcname): Collect all extra properties in extra_props and call filter_properties for all drivers with extra_props and font as parameter. (font_open_entity): Do not use cache, it does not pick up new fontconfig settings like hinting. (font_load_for_lface): If spec had a name in it, store it in entity. * emacs.c (main): Call syms_of_xsettings * config.in: HAVE_GCONF is new. * Makefile.in (GCONF_CFLAGS, GCONF_LIBS): New variables for HAVE_GCONF. xsettings.o is new. * menu-bar.el: Put "Use system font" in Option-menu. * loadup.el: If feature system-font-setting or font-render-setting is there, load font-setting. * Makefile.in (ELCFILES): font-settings.el is new. * font-setting.el: New file. * NEWS: Mention dynamic font changes (font-use-system-font). * configure.in: New option: --with(out)-gconf. Set HAVE_GCONF if we find gconf. --- ChangeLog | 5 + configure | 118 ++++++++ configure.in | 12 + etc/ChangeLog | 4 + etc/NEWS | 12 + lisp/ChangeLog | 10 + lisp/Makefile.in | 1 + lisp/font-setting.el | 109 ++++++++ lisp/loadup.el | 4 + lisp/menu-bar.el | 10 + src/ChangeLog | 51 ++++ src/Makefile.in | 18 +- src/config.in | 3 + src/emacs.c | 3 +- src/font.c | 40 ++- src/font.h | 2 + src/frame.c | 3 +- src/ftfont.c | 97 ++++++- src/keyboard.c | 17 ++ src/lisp.h | 3 + src/termhooks.h | 2 + src/w32font.c | 5 +- src/xfns.c | 45 ++-- src/xfont.c | 4 +- src/xftfont.c | 54 +++- src/xsettings.c | 627 +++++++++++++++++++++++++++++++++++++++++++ src/xsettings.h | 29 ++ src/xterm.c | 44 ++- src/xterm.h | 4 + 29 files changed, 1288 insertions(+), 48 deletions(-) create mode 100644 lisp/font-setting.el create mode 100644 src/xsettings.c create mode 100644 src/xsettings.h diff --git a/ChangeLog b/ChangeLog index 316d5a84ee..5b96dc43e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2009-11-17 Jan Djärv + + * configure.in: New option: --with(out)-gconf. + Set HAVE_GCONF if we find gconf. + 2009-11-17 Glenn Morris * Makefile.in (INFO_FILES): Add semantic. diff --git a/configure b/configure index 01dd6f1cfb..3f1b2bcde6 100755 --- a/configure +++ b/configure @@ -716,6 +716,8 @@ GTK_CFLAGS GTK_LIBS DBUS_CFLAGS DBUS_LIBS +GCONF_CFLAGS +GCONF_LIBS FONTCONFIG_CFLAGS FONTCONFIG_LIBS XFT_CFLAGS @@ -791,6 +793,7 @@ with_xim with_ns with_gpm with_dbus +with_gconf with_makeinfo with_gtk with_gcc @@ -1499,6 +1502,7 @@ Optional Packages: --without-gpm don't use -lgpm for mouse support on a GNU/Linux console --without-dbus don't compile with D-Bus support + --without-gconf don't compile with GConf support --without-makeinfo don't require makeinfo for building manuals --with-pkg-config-prog=PATH @@ -2240,6 +2244,14 @@ else fi +# Check whether --with-gconf was given. +if test "${with_gconf+set}" = set; then + withval=$with_gconf; +else + with_gconf=yes +fi + + ## For the times when you want to build Emacs but don't have ## a suitable makeinfo, and can live without the manuals. @@ -12519,6 +12531,111 @@ done fi fi +HAVE_GCONF=no +if test "${with_gconf}" = "yes"; then + + succeeded=no + + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_path_PKG_CONFIG+set}" = set; then + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + + test -z "$ac_cv_path_PKG_CONFIG" && ac_cv_path_PKG_CONFIG="no" + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:$LINENO: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + if test "$PKG_CONFIG" = "no" ; then + HAVE_GCONF=no + else + PKG_CONFIG_MIN_VERSION=0.9.0 + if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then + { $as_echo "$as_me:$LINENO: checking for gconf-2.0 >= 2.13" >&5 +$as_echo_n "checking for gconf-2.0 >= 2.13... " >&6; } + + if $PKG_CONFIG --exists "gconf-2.0 >= 2.13" 2>&5; then + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + succeeded=yes + + { $as_echo "$as_me:$LINENO: checking GCONF_CFLAGS" >&5 +$as_echo_n "checking GCONF_CFLAGS... " >&6; } + GCONF_CFLAGS=`$PKG_CONFIG --cflags "gconf-2.0 >= 2.13"|sed -e 's,///*,/,g'` + { $as_echo "$as_me:$LINENO: result: $GCONF_CFLAGS" >&5 +$as_echo "$GCONF_CFLAGS" >&6; } + + { $as_echo "$as_me:$LINENO: checking GCONF_LIBS" >&5 +$as_echo_n "checking GCONF_LIBS... " >&6; } + GCONF_LIBS=`$PKG_CONFIG --libs "gconf-2.0 >= 2.13"|sed -e 's,///*,/,g'` + { $as_echo "$as_me:$LINENO: result: $GCONF_LIBS" >&5 +$as_echo "$GCONF_LIBS" >&6; } + else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + GCONF_CFLAGS="" + GCONF_LIBS="" + ## If we have a custom action on failure, don't print errors, but + ## do set a variable so people can do so. + GCONF_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "gconf-2.0 >= 2.13"` + + fi + + + + else + echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer." + echo "*** See http://www.freedesktop.org/software/pkgconfig" + fi + fi + + if test $succeeded = yes; then + HAVE_GCONF=yes + else + HAVE_GCONF=no + fi + + if test "$HAVE_GCONF" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GCONF 1 +_ACEOF + + fi +fi + HAVE_XAW3D=no if test x"${USE_X_TOOLKIT}" = xmaybe || test x"${USE_X_TOOLKIT}" = xLUCID; then if test "$with_xaw3d" != no; then @@ -25513,6 +25630,7 @@ echo " Does Emacs use -lpng? ${HAVE_PNG}" echo " Does Emacs use -lrsvg-2? ${HAVE_RSVG}" echo " Does Emacs use -lgpm? ${HAVE_GPM}" echo " Does Emacs use -ldbus? ${HAVE_DBUS}" +echo " Does Emacs use -lgconf? ${HAVE_GCONF}" echo " Does Emacs use -lfreetype? ${HAVE_FREETYPE}" echo " Does Emacs use -lm17n-flt? ${HAVE_M17N_FLT}" diff --git a/configure.in b/configure.in index c2f680106d..d469cb13d4 100644 --- a/configure.in +++ b/configure.in @@ -144,6 +144,7 @@ OPTION_DEFAULT_OFF([ns],[use nextstep (Cocoa or GNUstep) windowing system]) OPTION_DEFAULT_ON([gpm],[don't use -lgpm for mouse support on a GNU/Linux console]) OPTION_DEFAULT_ON([dbus],[don't compile with D-Bus support]) +OPTION_DEFAULT_ON([gconf],[don't compile with GConf support]) ## For the times when you want to build Emacs but don't have ## a suitable makeinfo, and can live without the manuals. @@ -1739,6 +1740,16 @@ if test "${with_dbus}" = "yes"; then fi fi +dnl GConf has been tested under GNU/Linux only. +dnl The version is really arbitrary, it is about the same age as Gtk+ 2.6. +HAVE_GCONF=no +if test "${with_gconf}" = "yes"; then + PKG_CHECK_MODULES(GCONF, gconf-2.0 >= 2.13, HAVE_GCONF=yes, HAVE_GCONF=no) + if test "$HAVE_GCONF" = yes; then + AC_DEFINE(HAVE_GCONF, 1, [Define to 1 if using GConf.]) + fi +fi + dnl Do not put whitespace before the #include statements below. dnl Older compilers (eg sunos4 cc) choke on it. HAVE_XAW3D=no @@ -2985,6 +2996,7 @@ echo " Does Emacs use -lpng? ${HAVE_PNG}" echo " Does Emacs use -lrsvg-2? ${HAVE_RSVG}" echo " Does Emacs use -lgpm? ${HAVE_GPM}" echo " Does Emacs use -ldbus? ${HAVE_DBUS}" +echo " Does Emacs use -lgconf? ${HAVE_GCONF}" echo " Does Emacs use -lfreetype? ${HAVE_FREETYPE}" echo " Does Emacs use -lm17n-flt? ${HAVE_M17N_FLT}" diff --git a/etc/ChangeLog b/etc/ChangeLog index 0752d6571b..0b13457697 100644 --- a/etc/ChangeLog +++ b/etc/ChangeLog @@ -1,3 +1,7 @@ +2009-11-17 Jan Djärv + + * NEWS: Mention dynamic font changes (font-use-system-font). + 2009-11-15 Carsten Dominik * refcards/orgcard.tex: Push version number to 6.33a. diff --git a/etc/NEWS b/etc/NEWS index 7fbb7d2b63..aad0d2a337 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -78,6 +78,18 @@ frame parameter fullscreen makes the Emacs frame maximized. ** The pointer now becomes invisible when typing. Customize make-pointer-invisible to turn it off. +** Emacs can use the system default monospaced font in Gnome. +The use of the system default font can be turned on or off by customizing +the variable 'font-use-system-font'. It is off by default. +If the system default is changed, Emacs changes also. +This requires that gconf-support is built in. If configure finds the +gconf-libraries, that support is included. Gconf-support can be +turned off with the configure option --without-gconf. + +** Emacs now reacts to Xft-changes made by configuration tools on X11. +Changes to antialias, hinting, hintstyle, RGBA, DPI and lcdfilter are +handeled. The XSETTINGS mechanism is used to implement this. + ** Killing a buffer with a running process now asks for confirmation. You can remove this query in two ways: either remove `process-kill-buffer-query-function' from `kill-buffer-query-functions', diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 67add62f6f..61f1b5d97a 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,13 @@ +2009-11-17 Jan Djärv + + * menu-bar.el: Put "Use system font" in Option-menu. + + * loadup.el: If feature system-font-setting or font-render-setting is + there, load font-setting. + + * Makefile.in (ELCFILES): font-settings.el is new. + * font-setting.el: New file. + 2009-11-17 Glenn Morris * vc-svn.el (vc-svn-print-log): Fix typo in previous. diff --git a/lisp/Makefile.in b/lisp/Makefile.in index ecd358840c..3faa6a7564 100644 --- a/lisp/Makefile.in +++ b/lisp/Makefile.in @@ -680,6 +680,7 @@ ELCFILES = \ $(lisp)/follow.elc \ $(lisp)/font-core.elc \ $(lisp)/font-lock.elc \ + $(lisp)/font-setting.elc \ $(lisp)/format-spec.elc \ $(lisp)/format.elc \ $(lisp)/forms.elc \ diff --git a/lisp/font-setting.el b/lisp/font-setting.el new file mode 100644 index 0000000000..f426a141bb --- /dev/null +++ b/lisp/font-setting.el @@ -0,0 +1,109 @@ +;;; xsettings.el --- Support dynamic font changes + +;; Copyright (C) 2009 Free Software Foundation, Inc. + +;; Author: Jan Djärv +;; Maintainer: FSF +;; Keywords: font, system-font + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; This file provides the lisp part of the GConf and XSetting code in +;; xsetting.c. But it is nothing that prevents it from being used by +;; other configuration schemes. + +;;; Code: + +;;; Customizable variables + +(defun font-setting-set-system-font (symbol value) + (set-default symbol value) + (if (symbol-value symbol) + (let ((f (selected-frame))) + (if (display-graphic-p f) + (font-setting-change-default-font f t))))) + +(defcustom font-use-system-font nil + "If non-nil, use the system monospaced font" + :version "23.2" + :type 'boolean + :group 'font-selection + :set 'font-setting-set-system-font) + +(declare-function font-get-system-font "xsettings.c" ()) + +(defun font-setting-change-default-font (display-or-frame set-font) + "Change font and/or font settings for frames on display DISPLAY-OR-FRAME. +If DISPLAY-OR-FRAME is a frame, the display is the one for that frame. + +If set-font is non-nil, change the font for frames. Otherwise re-apply the +current form for the frame (i.e. hinting or somesuch changed)." + + (let ((new-font (and (fboundp 'font-get-system-font) + (font-get-system-font)))) + (when new-font + ;; Be careful here: when set-face-attribute is called for the + ;; :font attribute, Emacs tries to guess the best matching font + ;; by examining the other face attributes (Bug#2476). + + (clear-font-cache) + ;; Set for current frames. Only change font for those that have + ;; the old font now. If they don't have the old font, the user + ;; probably changed it. + (dolist (f (frames-on-display-list display-or-frame)) + (if (display-graphic-p f) + (let* ((frame-font + (or (font-get (face-attribute 'default :font f + 'default) :name) + (frame-parameter f 'font-parameter))) + (font-to-set + (if set-font new-font + ;; else set font again, hinting etc. may have changed. + frame-font))) + (progn + (set-frame-parameter f 'font-parameter font-to-set) + (set-face-attribute 'default f + :width 'normal + :weight 'normal + :slant 'normal + :font font-to-set))))) + + ;; Set for future frames. + (set-face-attribute 'default t :font new-font) + (let ((spec (list (list t (face-attr-construct 'default))))) + (progn + (put 'default 'customized-face spec) + (custom-push-theme 'theme-face 'default 'user 'set spec) + (put 'default 'face-modified nil)))))) + +(defun font-setting-handle-config-changed-event (event) + "Handle config-changed-event to change fonts on the display in EVENT. +If `font-use-system-font' is nil, the font is not changed." + (interactive "e") + (let ((type (nth 1 event)) ;; font-name or font-render + (display-name (nth 2 event))) + (if (or (not (eq type 'font-name)) + font-use-system-font) + (font-setting-change-default-font display-name + (eq type 'font-name))))) + +(if (or (featurep 'system-font-setting) (featurep 'font-render-setting)) + (define-key special-event-map [config-changed-event] + 'font-setting-handle-config-changed-event)) + +(provide 'font-setting) diff --git a/lisp/loadup.el b/lisp/loadup.el index e12d88447c..dcb61378a9 100644 --- a/lisp/loadup.el +++ b/lisp/loadup.el @@ -198,6 +198,10 @@ (load "international/fontset") (load "dnd") (load "tool-bar"))) + +(if (or (featurep 'system-font-setting) (featurep 'font-render-setting)) + (load "font-setting")) + (if (featurep 'x) (progn (load "x-dnd") diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el index 21495b32f5..13319f4126 100644 --- a/lisp/menu-bar.el +++ b/lisp/menu-bar.el @@ -660,6 +660,8 @@ by \"Save Options\" in Custom buffers.") (custom-push-theme 'theme-face 'default 'user 'set spec) (put 'default 'face-modified nil)))) + + ;;; Assemble all the top-level items of the "Options" menu (define-key menu-bar-options-menu [customize] `(menu-item ,(purecopy "Customize Emacs") ,menu-bar-custom-menu)) @@ -713,6 +715,14 @@ by \"Save Options\" in Custom buffers.") :visible (display-multi-font-p) :help ,(purecopy "Select a default font"))) +(if (featurep 'system-font-setting) + (define-key menu-bar-options-menu [menu-system-font] + (menu-bar-make-toggle toggle-use-system-font font-use-system-font + "Use system font" + "Use system font: %s" + "Use the monospaced font defined by the system"))) + + ;; The "Show/Hide" submenu of menu "Options" (defvar menu-bar-showhide-menu (make-sparse-keymap "Show/Hide")) diff --git a/src/ChangeLog b/src/ChangeLog index 0bc2ad403e..c09d11d5ec 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,54 @@ +2009-11-17 Jan Djärv + + * xterm.h (struct x_display_info): Add atoms and Window for xsettings. + + * xterm.c (handle_one_xevent): Call xft_settings_event for + ClientMessage, PropertyNotify and DestroyNotify. + (x_term_init): If we have XFT, get DPI from Xft.dpi. + Call xsettings_initialize. + + * xftfont.c (xftfont_fix_match): New function. + (xftfont_open): Call XftDefaultSubstitute before XftFontMatch. + Call xftfont_fix_match after XftFontMatch. + + * xfont.c (xfont_driver): Initialize all members. + + * xfns.c (x_default_font_parameter): Try font from Ffont_get_system_font. + Do not get font from x_default_parameter if we got one from + Ffont_get_system_font. + (Fx_select_font): Get the defaut font name from :name of FRAME_FONT (f). + + * w32font.c (w32font_driver): Initialize all members. + + * termhooks.h (enum event_kind): CONFIG_CHANGED_EVENT is new. + + * lisp.h: Declare syms_of_xsettings. + + * keyboard.c (kbd_buffer_get_event, make_lispy_event): Handle + CONFIG_CHANGED_EVENT. + + * ftfont.c (ftfont_filter_properties): New function. + + * frame.c (x_set_font): Remove unused variable lval. + + * font.h (struct font_driver): filter_properties is new. + + * font.c (font_put_extra): Don't return if val is nil, it means + boolean option is off. + (font_parse_fcname): Collect all extra properties in extra_props + and call filter_properties for all drivers with extra_props and + font as parameter. + (font_open_entity): Do not use cache, it does not pick up new fontconfig + settings like hinting. + (font_load_for_lface): If spec had a name in it, store it in entity. + + * emacs.c (main): Call syms_of_xsettings + + * config.in: HAVE_GCONF is new. + + * Makefile.in (GCONF_CFLAGS, GCONF_LIBS): New variables for HAVE_GCONF. + xsettings.o is new. + 2009-11-17 Kenichi Handa * xdisp.c (x_produce_glyphs): Consider face-remapping when falling diff --git a/src/Makefile.in b/src/Makefile.in index 038689c635..824381c812 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -248,6 +248,11 @@ DBUS_LIBS = @DBUS_LIBS@ DBUS_OBJ = dbusbind.o #endif +#ifdef HAVE_GCONF +GCONF_CFLAGS = @GCONF_CFLAGS@ +GCONF_LIBS = @GCONF_LIBS@ +#endif + /* DO NOT use -R. There is a special hack described in lastfile.c which is used instead. Some initialized data areas are modified at initial startup, then labeled as part of the text area when @@ -261,7 +266,7 @@ DBUS_OBJ = dbusbind.o /* C_SWITCH_X_SITE must come before C_SWITCH_X_MACHINE and C_SWITCH_X_SYSTEM since it may have -I options that should override those two. */ -ALL_CFLAGS=-Demacs -DHAVE_CONFIG_H $(MYCPPFLAGS) -I. -I${srcdir} C_SWITCH_MACHINE C_SWITCH_SYSTEM C_SWITCH_X_SITE C_SWITCH_X_MACHINE C_SWITCH_X_SYSTEM C_SWITCH_SYSTEM_TEMACS ${CFLAGS_SOUND} ${RSVG_CFLAGS} ${DBUS_CFLAGS} ${CFLAGS} @FREETYPE_CFLAGS@ @FONTCONFIG_CFLAGS@ @LIBOTF_CFLAGS@ @M17N_FLT_CFLAGS@ ${DEPFLAGS} +ALL_CFLAGS=-Demacs -DHAVE_CONFIG_H $(MYCPPFLAGS) -I. -I${srcdir} C_SWITCH_MACHINE C_SWITCH_SYSTEM C_SWITCH_X_SITE C_SWITCH_X_MACHINE C_SWITCH_X_SYSTEM C_SWITCH_SYSTEM_TEMACS ${CFLAGS_SOUND} ${RSVG_CFLAGS} ${DBUS_CFLAGS} ${GCONF_CFLAGS} ${CFLAGS} @FREETYPE_CFLAGS@ @FONTCONFIG_CFLAGS@ @LIBOTF_CFLAGS@ @M17N_FLT_CFLAGS@ ${DEPFLAGS} ALL_OBJC_CFLAGS=$(ALL_CFLAGS) @GNU_OBJC_CFLAGS@ .SUFFIXES: .m @@ -286,7 +291,8 @@ ALL_OBJC_CFLAGS=$(ALL_CFLAGS) @GNU_OBJC_CFLAGS@ #ifdef HAVE_X_WINDOWS XMENU_OBJ = xmenu.o -XOBJ= xterm.o xfns.o xselect.o xrdb.o fontset.o xsmfns.o fringe.o image.o +XOBJ= xterm.o xfns.o xselect.o xrdb.o fontset.o xsmfns.o fringe.o image.o \ + xsettings.o #ifdef HAVE_MENUS @@ -904,7 +910,7 @@ SOME_MACHINE_LISP = ../lisp/mouse.elc \ LIBES = $(LOADLIBES) $(LIBS) $(LIBX) $(LIBSOUND) $(RSVG_LIBS) $(DBUS_LIBS) \ LIBGPM LIBRESOLV LIBS_SYSTEM LIBS_MACHINE LIBS_TERMCAP \ - LIBS_DEBUG $(GETLOADAVG_LIBS) \ + LIBS_DEBUG $(GETLOADAVG_LIBS) ${GCONF_LIBS} \ @FREETYPE_LIBS@ @FONTCONFIG_LIBS@ @LIBOTF_LIBS@ @M17N_FLT_LIBS@ \ $(GNULIB_VAR) LIB_MATH LIB_STANDARD $(GNULIB_VAR) @@ -1212,7 +1218,7 @@ xfaces.o: xfaces.c dispextern.h frame.h xterm.h buffer.h blockinput.h \ xfns.o: xfns.c buffer.h frame.h window.h keyboard.h xterm.h dispextern.h \ $(srcdir)/../lwlib/lwlib.h blockinput.h atimer.h systime.h epaths.h \ character.h charset.h coding.h gtkutil.h lisp.h $(config_h) termhooks.h \ - fontset.h termchar.h font.h + fontset.h termchar.h font.h xsettings.h xfont.o: dispextern.h xterm.h frame.h blockinput.h character.h charset.h \ font.h lisp.h $(config_h) xftfont.o: dispextern.h xterm.h frame.h blockinput.h character.h charset.h \ @@ -1228,13 +1234,13 @@ xmenu.o: xmenu.c xterm.h termhooks.h window.h dispextern.h frame.h buffer.h \ xterm.o: xterm.c xterm.h termhooks.h termopts.h termchar.h window.h buffer.h \ dispextern.h frame.h disptab.h blockinput.h atimer.h systime.h syssignal.h \ keyboard.h emacs-icon.h character.h charset.h ccl.h fontset.h composite.h \ - coding.h process.h gtkutil.h font.h fontset.h lisp.h $(config_h) + coding.h process.h gtkutil.h font.h fontset.h lisp.h $(config_h) xsettings.h xselect.o: xselect.c process.h dispextern.h frame.h xterm.h blockinput.h \ buffer.h atimer.h systime.h termhooks.h lisp.h $(config_h) xrdb.o: xrdb.c lisp.h $(config_h) epaths.h xsmfns.o: xsmfns.c lisp.h $(config_h) systime.h sysselect.h termhooks.h xterm.h \ lisp.h termopts.h - +xsettings.o: xterm.h xsettings.h lisp.h frame.h termhooks.h $(config_h) /* The files of Lisp proper */ alloc.o: alloc.c process.h frame.h window.h buffer.h puresize.h syssignal.h keyboard.h \ diff --git a/src/config.in b/src/config.in index 04b6de5714..e7e1c9d086 100644 --- a/src/config.in +++ b/src/config.in @@ -201,6 +201,9 @@ along with GNU Emacs. If not, see . */ /* Define to 1 if you have the `gai_strerror' function. */ #undef HAVE_GAI_STRERROR +/* Define to 1 if using GConf. */ +#undef HAVE_GCONF + /* Define to 1 if you have the `gdk_display_open' function. */ #undef HAVE_GDK_DISPLAY_OPEN diff --git a/src/emacs.c b/src/emacs.c index 4099a4f479..d96b3a1daf 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1669,6 +1669,7 @@ main (int argc, char **argv) syms_of_xfns (); syms_of_xmenu (); syms_of_fontset (); + syms_of_xsettings (); #ifdef HAVE_X_SM syms_of_xsmfns (); #endif @@ -1749,7 +1750,7 @@ main (int argc, char **argv) #endif init_window (); init_font (); - + if (!initialized) { char *file; diff --git a/src/font.c b/src/font.c index bab3d9f821..1c0a9dfb23 100644 --- a/src/font.c +++ b/src/font.c @@ -718,8 +718,6 @@ font_put_extra (font, prop, val) { Lisp_Object prev = Qnil; - if (NILP (val)) - return val; while (CONSP (extra) && NILP (Fstring_lessp (prop, XCAR (XCAR (extra))))) prev = extra, extra = XCDR (extra); @@ -1431,6 +1429,8 @@ font_parse_fcname (name, font) if (family_end) { + Lisp_Object extra_props = Qnil; + /* A fontconfig name with size and/or property data. */ if (family_end > name) { @@ -1504,13 +1504,25 @@ font_parse_fcname (name, font) if (prop >= FONT_FOUNDRY_INDEX && prop < FONT_EXTRA_INDEX) - ASET (font, prop, font_prop_validate (prop, Qnil, val)); - else - Ffont_put (font, key, val); + ASET (font, prop, font_prop_validate (prop, Qnil, val)); + else + { + extra_props = nconc2 (extra_props, + Fcons (Fcons (key, val), Qnil)); + } } p = q; } } + + if (! NILP (extra_props)) + { + struct font_driver_list *driver_list = font_driver_list; + for ( ; driver_list; driver_list = driver_list->next) + if (driver_list->driver->filter_properties) + (*driver_list->driver->filter_properties) (font, extra_props); + } + } else { @@ -2975,11 +2987,15 @@ font_open_entity (f, entity, pixel_size) else if (CONSP (Vface_font_rescale_alist)) scaled_pixel_size = pixel_size * font_rescale_ratio (entity); +#if 0 + /* This doesn't work if you have changed hinting or any other parameter. + We need to make a new object in every case to be sure. */ for (objlist = AREF (entity, FONT_OBJLIST_INDEX); CONSP (objlist); objlist = XCDR (objlist)) if (! NILP (AREF (XCAR (objlist), FONT_TYPE_INDEX)) && XFONT_OBJECT (XCAR (objlist))->pixel_size == pixel_size) return XCAR (objlist); +#endif val = AREF (entity, FONT_TYPE_INDEX); for (driver_list = f->font_driver_list; @@ -3155,12 +3171,14 @@ font_clear_prop (attrs, prop) if (! FONTP (font)) return; +#if 0 if (! NILP (Ffont_get (font, QCname))) { font = Fcopy_font_spec (font); font_put_extra (font, QCname, Qnil); } +#endif if (NILP (AREF (font, prop)) && prop != FONT_FAMILY_INDEX && prop != FONT_FOUNDRY_INDEX @@ -3438,7 +3456,7 @@ font_find_for_lface (f, attrs, spec, c) val = font_select_entity (frame, entities, attrs, pixel_size, c); if (! NILP (val)) - return val; + return val; } } } @@ -3500,7 +3518,7 @@ font_load_for_lface (f, attrs, spec) FRAME_PTR f; Lisp_Object *attrs, spec; { - Lisp_Object entity; + Lisp_Object entity, name; entity = font_find_for_lface (f, attrs, spec, -1); if (NILP (entity)) @@ -3512,7 +3530,13 @@ font_load_for_lface (f, attrs, spec) if (NILP (entity)) return Qnil; } - return font_open_for_lface (f, entity, attrs, spec); + /* Don't loose the original name that was put in initially. We need + it to re-apply the font when font parameters (like hinting or dpi) have + changed. */ + entity = font_open_for_lface (f, entity, attrs, spec); + name = Ffont_get (spec, QCname); + if (STRINGP (name)) font_put_extra (entity, QCname, name); + return entity; } diff --git a/src/font.h b/src/font.h index 5726ffd319..1a09df2558 100644 --- a/src/font.h +++ b/src/font.h @@ -687,6 +687,8 @@ struct font_driver the (N-1)th element of VARIATIONS. */ int (*get_variation_glyphs) P_ ((struct font *font, int c, unsigned variations[256])); + + void (*filter_properties) P_ ((Lisp_Object font, Lisp_Object properties)); }; diff --git a/src/frame.c b/src/frame.c index 467f6bd148..323bfc1c37 100644 --- a/src/frame.c +++ b/src/frame.c @@ -3359,7 +3359,7 @@ x_set_font (f, arg, oldval) struct frame *f; Lisp_Object arg, oldval; { - Lisp_Object frame, font_object, lval; + Lisp_Object frame, font_object; int fontset = -1; /* Set the frame parameter back to the old value because we may @@ -3427,7 +3427,6 @@ x_set_font (f, arg, oldval) if (! NILP (Fequal (font_object, oldval))) return; - x_new_font (f, font_object, fontset); store_frame_param (f, Qfont, arg); /* Recalculate toolbar height. */ diff --git a/src/ftfont.c b/src/ftfont.c index ed9cb6f19d..4ba4712965 100644 --- a/src/ftfont.c +++ b/src/ftfont.c @@ -86,6 +86,8 @@ static Lisp_Object ftfont_resolve_generic_family P_ ((Lisp_Object, static Lisp_Object ftfont_lookup_cache P_ ((Lisp_Object, enum ftfont_cache_for)); +static void ftfont_filter_properties P_ ((Lisp_Object font, Lisp_Object alist)); + Lisp_Object ftfont_font_format P_ ((FcPattern *, Lisp_Object)); #define SYMBOL_FcChar8(SYM) (FcChar8 *) SDATA (SYMBOL_NAME (SYM)) @@ -545,10 +547,12 @@ struct font_driver ftfont_driver = NULL, /* check */ #ifdef HAVE_OTF_GET_VARIATION_GLYPHS - ftfont_variation_glyphs + ftfont_variation_glyphs, #else - NULL + NULL, #endif + + ftfont_filter_properties, /* filter_properties */ }; extern Lisp_Object QCname; @@ -2226,7 +2230,94 @@ ftfont_font_format (FcPattern *pattern, Lisp_Object filename) return intern ("unknown"); } - +static const char *ftfont_booleans [] = { + ":antialias", + ":hinting", + ":verticallayout", + ":autohint", + ":globaladvance", + ":outline", + ":scalable", + ":minspace", + ":embolden", + NULL, +}; + +static const char *ftfont_non_booleans [] = { + ":family", + ":familylang", + ":style", + ":stylelang", + ":fullname", + ":fullnamelang", + ":slant", + ":weight", + ":size", + ":width", + ":aspect", + ":pixelsize", + ":spacing", + ":foundry", + ":hintstyle", + ":file", + ":index", + ":ftface", + ":rasterizer", + ":scale", + ":dpi", + ":rgba", + ":lcdfilter", + ":charset", + ":lang", + ":fontversion", + ":capability", + NULL, +}; + +static void +ftfont_filter_properties (font, alist) + Lisp_Object font; + Lisp_Object alist; +{ + Lisp_Object it; + int i; + + /* Set boolean values to Qt or Qnil */ + for (i = 0; ftfont_booleans[i] != NULL; ++i) + for (it = alist; ! NILP (it); it = XCDR (it)) + { + Lisp_Object key = XCAR (XCAR (it)); + Lisp_Object val = XCDR (XCAR (it)); + char *keystr = SDATA (SYMBOL_NAME (key)); + + if (strcmp (ftfont_booleans[i], keystr) == 0) + { + char *str = SYMBOLP (val) ? SDATA (SYMBOL_NAME (val)) : NULL; + if (INTEGERP (val)) str = XINT (val) != 0 ? "true" : "false"; + if (str == NULL) str = "true"; + + val = Qt; + if (strcmp ("false", str) == 0 || strcmp ("False", str) == 0 + || strcmp ("FALSE", str) == 0 || strcmp ("FcFalse", str) == 0 + || strcmp ("off", str) == 0 || strcmp ("OFF", str) == 0 + || strcmp ("Off", str) == 0) + val = Qnil; + Ffont_put (font, key, val); + } + } + + for (i = 0; ftfont_non_booleans[i] != NULL; ++i) + for (it = alist; ! NILP (it); it = XCDR (it)) + { + Lisp_Object key = XCAR (XCAR (it)); + Lisp_Object val = XCDR (XCAR (it)); + char *keystr = SDATA (SYMBOL_NAME (key)); + if (strcmp (ftfont_non_booleans[i], keystr) == 0) + Ffont_put (font, key, val); + } +} + + void syms_of_ftfont () { diff --git a/src/keyboard.c b/src/keyboard.c index 5ea4f35080..f15b7971c4 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -489,6 +489,8 @@ Lisp_Object Qsave_session; #ifdef HAVE_DBUS Lisp_Object Qdbus_event; #endif +Lisp_Object Qconfig_changed_event; + /* Lisp_Object Qmouse_movement; - also an event header */ /* Properties of event headers. */ @@ -4283,6 +4285,11 @@ kbd_buffer_get_event (kbp, used_mouse_menu, end_time) kbd_fetch_ptr = event + 1; } #endif + else if (event->kind == CONFIG_CHANGED_EVENT) + { + obj = make_lispy_event (event); + kbd_fetch_ptr = event + 1; + } else { /* If this event is on a different frame, return a switch-frame this @@ -6196,6 +6203,10 @@ make_lispy_event (event) } #endif /* HAVE_DBUS */ + case CONFIG_CHANGED_EVENT: + return Fcons (Qconfig_changed_event, + Fcons (event->arg, + Fcons (event->frame_or_window, Qnil))); #ifdef HAVE_GPM case GPM_CLICK_EVENT: { @@ -11805,6 +11816,9 @@ syms_of_keyboard () staticpro (&Qdbus_event); #endif + Qconfig_changed_event = intern_c_string ("config-changed-event"); + staticpro (&Qconfig_changed_event); + Qmenu_enable = intern_c_string ("menu-enable"); staticpro (&Qmenu_enable); QCenable = intern_c_string (":enable"); @@ -12547,6 +12561,9 @@ keys_of_keyboard () initial_define_lispy_key (Vspecial_event_map, "dbus-event", "dbus-handle-event"); #endif + + initial_define_lispy_key (Vspecial_event_map, "config-changed-event", + "ignore"); } /* Mark the pointers in the kboard objects. diff --git a/src/lisp.h b/src/lisp.h index 65c88dcfab..01b2d61247 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -2643,6 +2643,9 @@ extern Lisp_Object safe_eval P_ ((Lisp_Object)); extern int pos_visible_p P_ ((struct window *, int, int *, int *, int *, int *, int *, int *)); +/* Defined in xsettings.c */ +extern void syms_of_xsettings P_ ((void)); + /* Defined in vm-limit.c. */ extern void memory_warnings P_ ((POINTER_TYPE *, void (*warnfun) ())); diff --git a/src/termhooks.h b/src/termhooks.h index bd9b4ec457..33585e832a 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -184,6 +184,8 @@ enum event_kind , DBUS_EVENT #endif + , CONFIG_CHANGED_EVENT + #ifdef WINDOWSNT /* Generated when an APPCOMMAND event is received, in response to Multimedia or Internet buttons on some keyboards. diff --git a/src/w32font.c b/src/w32font.c index 69462512ca..b9ffea7cd3 100644 --- a/src/w32font.c +++ b/src/w32font.c @@ -2472,7 +2472,10 @@ struct font_driver w32font_driver = NULL, /* otf_drive */ NULL, /* start_for_frame */ NULL, /* end_for_frame */ - NULL /* shape */ + NULL, /* shape */ + NULL, /* check */ + NULL, /* get_variation_glyphs */ + NULL, /* filter_properties */ }; diff --git a/src/xfns.c b/src/xfns.c index 0a041a7dc9..3d5a2a127d 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -98,6 +98,8 @@ along with GNU Emacs. If not, see . */ #include #endif +#include "xsettings.h" + #if !defined(NO_EDITRES) #define HACK_EDITRES extern void _XEditResCheckMessages (); @@ -3025,13 +3027,19 @@ x_default_font_parameter (f, parms) { struct x_display_info *dpyinfo = FRAME_X_DISPLAY_INFO (f); Lisp_Object font_param = x_get_arg (dpyinfo, parms, Qfont, NULL, NULL, - RES_TYPE_STRING); + RES_TYPE_STRING); Lisp_Object font; + int got_from_gconf = 0; if (EQ (font_param, Qunbound)) - font_param = Qnil; + { + font_param = Qnil; + font_param = Ffont_get_system_font(); + got_from_gconf = !NILP (font_param); + } + font = !NILP (font_param) ? font_param : x_get_arg (dpyinfo, parms, Qfont, "font", "Font", RES_TYPE_STRING); - + if (! STRINGP (font)) { char *names[] @@ -3068,7 +3076,11 @@ x_default_font_parameter (f, parms) we've applied the `default' face settings. */ x_set_frame_parameters (f, Fcons (Fcons (Qfont_param, font_param), Qnil)); } - x_default_parameter (f, parms, Qfont, font, "font", "Font", RES_TYPE_STRING); + + x_default_parameter (f, parms, Qfont, font, + got_from_gconf ? NULL : "font", + got_from_gconf ? NULL : "Font", + RES_TYPE_STRING); } @@ -5569,10 +5581,10 @@ If FRAME is omitted or nil, it defaults to the selected frame. */) { FRAME_PTR f = check_x_frame (frame); char *name; - Lisp_Object default_font, font = Qnil; + Lisp_Object font; Lisp_Object font_param; char *default_name = NULL; - struct gcpro gcpro1; + struct gcpro gcpro1, gcpro2; int count = SPECPDL_INDEX (); check_x (); @@ -5586,21 +5598,22 @@ If FRAME is omitted or nil, it defaults to the selected frame. */) BLOCK_INPUT; - GCPRO1(font_param); - font_param = Fframe_parameter (frame, Qfont_param); + GCPRO2(font_param, font); - if (x_last_font_name != NULL) - default_name = x_last_font_name; - else if (STRINGP (font_param)) + XSETFONT (font, FRAME_FONT (f)); + font_param = Ffont_get (font, intern_c_string (":name")); + if (STRINGP (font_param)) default_name = SDATA (font_param); - else if (FONTP (default_font)) + else { - XSETFONT (default_font, FRAME_FONT (f)); - default_name = alloca (256); - if (font_unparse_gtkname (default_font, f, default_name, 256) < 0) - default_name = NULL; + font_param = Fframe_parameter (frame, Qfont_param); + if (STRINGP (font_param)) + default_name = SDATA (font_param); } + if (default_name == NULL && x_last_font_name != NULL) + default_name = x_last_font_name; + name = xg_get_font_name (f, default_name); if (name) diff --git a/src/xfont.c b/src/xfont.c index 36ac09fcb1..ab37350ddf 100644 --- a/src/xfont.c +++ b/src/xfont.c @@ -151,7 +151,9 @@ struct font_driver xfont_driver = xfont_text_extents, xfont_draw, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - xfont_check + xfont_check, + NULL, /* get_variation_glyphs */ + NULL, /* filter_properties */ }; extern Lisp_Object QCname; diff --git a/src/xftfont.c b/src/xftfont.c index e66b538fa1..6455c5f2c5 100644 --- a/src/xftfont.c +++ b/src/xftfont.c @@ -184,6 +184,53 @@ extern Lisp_Object QCantialias; static FcChar8 ascii_printable[95]; +static void +xftfont_fix_match (pat, match) + FcPattern *pat, *match; +{ + /* These values are not used for matching (except antialias), but for + rendering, so make sure they are carried over to the match. + We also put antialias here because most fonts are antialiased, so + the match will have antialias true. */ + + FcBool b = FcTrue; + int i; + double dpi; + + FcPatternGetBool (pat, FC_ANTIALIAS, 0, &b); + if (! b) + { + FcPatternDel (match, FC_ANTIALIAS); + FcPatternAddBool (match, FC_ANTIALIAS, FcFalse); + } + FcPatternGetBool (pat, FC_HINTING, 0, &b); + if (! b) + { + FcPatternDel (match, FC_HINTING); + FcPatternAddBool (match, FC_HINTING, FcFalse); + } + if (FcResultMatch == FcPatternGetInteger (pat, FC_HINT_STYLE, 0, &i)) + { + FcPatternDel (match, FC_HINT_STYLE); + FcPatternAddInteger (match, FC_HINT_STYLE, i); + } + if (FcResultMatch == FcPatternGetInteger (pat, FC_LCD_FILTER, 0, &i)) + { + FcPatternDel (match, FC_LCD_FILTER); + FcPatternAddInteger (match, FC_LCD_FILTER, i); + } + if (FcResultMatch == FcPatternGetInteger (pat, FC_RGBA, 0, &i)) + { + FcPatternDel (match, FC_RGBA); + FcPatternAddInteger (match, FC_RGBA, i); + } + if (FcResultMatch == FcPatternGetDouble (pat, FC_DPI, 0, &dpi)) + { + FcPatternDel (match, FC_DPI); + FcPatternAddDouble (match, FC_DPI, dpi); + } +} + static Lisp_Object xftfont_open (f, entity, pixel_size) FRAME_PTR f; @@ -249,7 +296,7 @@ xftfont_open (f, entity, pixel_size) key = XCAR (XCAR (tail)), val = XCDR (XCAR (tail)); if (EQ (key, QCantialias)) - FcPatternAddBool (pat, FC_ANTIALIAS, NILP (val) ? FcFalse : FcTrue); + FcPatternAddBool (pat, FC_ANTIALIAS, NILP (val) ? FcFalse : FcTrue); else if (EQ (key, QChinting)) FcPatternAddBool (pat, FC_HINTING, NILP (val) ? FcFalse : FcTrue); else if (EQ (key, QCautohint)) @@ -285,7 +332,12 @@ xftfont_open (f, entity, pixel_size) int event_base, error_base; XRenderQueryExtension (display, &event_base, &error_base); } + + /* Substitute in values from X resources and XftDefaultSet. */ + XftDefaultSubstitute (display, FRAME_X_SCREEN_NUMBER (f), pat); match = XftFontMatch (display, FRAME_X_SCREEN_NUMBER (f), pat, &result); + xftfont_fix_match (pat, match); + FcPatternDestroy (pat); xftfont = XftFontOpenPattern (display, match); if (!xftfont) diff --git a/src/xsettings.c b/src/xsettings.c new file mode 100644 index 0000000000..84e05a1898 --- /dev/null +++ b/src/xsettings.c @@ -0,0 +1,627 @@ +/* Functions for handle font changes dynamically. + Copyright (C) 2009 + Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see . */ + +#include "config.h" +#include +#include +#include "lisp.h" +#include "xterm.h" +#include "xsettings.h" +#include "frame.h" +#include "blockinput.h" +#include "termhooks.h" +#include "termopts.h" + +#include + +#ifdef HAVE_GCONF +#include +#endif +#ifdef HAVE_XFT +#include +#endif + +static char *current_mono_font; +static struct x_display_info *first_dpyinfo; +static Lisp_Object Qfont_name, Qfont_render; + +#ifdef HAVE_GCONF +static GConfClient *gconf_client; +#endif + + +static void +store_font_changed_event (arg, display_name) + Lisp_Object arg; + Lisp_Object display_name; +{ + struct input_event event; + EVENT_INIT (event); + event.kind = CONFIG_CHANGED_EVENT; + event.frame_or_window = display_name; + event.arg = arg; + kbd_buffer_store_event (&event); +} + +#ifdef HAVE_GCONF + +#define SYSTEM_MONO_FONT "/desktop/gnome/interface/monospace_font_name" + +/* Callback called when something changed in GConf that we care about, + that is SYSTEM_MONO_FONT. */ + +static void +something_changedCB (client, cnxn_id, entry, user_data) + GConfClient *client; + guint cnxn_id; + GConfEntry *entry; + gpointer user_data; +{ + GConfValue *v = gconf_entry_get_value (entry); + + if (!v) return; + if (v->type == GCONF_VALUE_STRING) + { + const char *value = gconf_value_get_string (v); + int i; + if (current_mono_font != NULL && strcmp (value, current_mono_font) == 0) + return; // No change. + + xfree (current_mono_font); + current_mono_font = xstrdup (value); + } + + + if (first_dpyinfo != NULL) + { + /* Check if display still open */ + struct x_display_info *dpyinfo; + int found = 0; + for (dpyinfo = x_display_list; !found && dpyinfo; dpyinfo = dpyinfo->next) + found = dpyinfo == first_dpyinfo; + + if (found) + store_font_changed_event (Qfont_name, + XCAR (first_dpyinfo->name_list_element)); + } +} +#endif /* HAVE_GCONF */ + +#ifdef HAVE_XFT + +/* Find the window that contains the XSETTINGS property values. */ + +static void +get_prop_window (dpyinfo) + struct x_display_info *dpyinfo; +{ + Display *dpy = dpyinfo->display; + + XGrabServer (dpy); + dpyinfo->xsettings_window = XGetSelectionOwner (dpy, + dpyinfo->Xatom_xsettings_sel); + if (dpyinfo->xsettings_window != None) + /* Select events so we can detect if window is deleted or if settings + are changed. */ + XSelectInput (dpy, dpyinfo->xsettings_window, + PropertyChangeMask|StructureNotifyMask); + + XUngrabServer (dpy); +} + +struct xsettings +{ + FcBool aa, hinting; + int rgba, lcdfilter, hintstyle; + double dpi; +}; + +#define SWAP32(nr) (((nr) << 24) | (((nr) << 8) & 0xff0000) \ + | (((nr) >> 8) & 0xff00) | ((nr) >> 24)) +#define SWAP16(nr) (((nr) << 8) | ((nr) >> 8)) +#define PAD(nr) (((nr) + 3) & ~3) + +/* Parse xsettings and extract those that deal with Xft. + See http://freedesktop.org/wiki/Specifications/XSettingsRegistry + and http://standards.freedesktop.org/xsettings-spec/xsettings-spec-0.5.html. + + Layout of prop. First is a header: + + bytes type what + ------------------------------------ + 1 CARD8 byte-order + 3 unused + 4 CARD32 SERIAL + 4 CARD32 N_SETTINGS + + Then N_SETTINGS records, with header: + + bytes type what + ------------------------------------ + 1 SETTING_TYPE type (0 = integer, 1 = string, 2 RGB color). + 1 unused + 2 CARD16 n == name-length + n STRING8 name + p unused, p=pad_to_even_4(n) + 4 CARD32 last-change-serial + + and then the value, For string: + + bytes type what + ------------------------------------ + 4 CARD32 n = value-length + n STRING8 value + p unused, p=pad_to_even_4(n) + + For integer: + + bytes type what + ------------------------------------ + 4 INT32 value + + For RGB color: + + bytes type what + ------------------------------------ + 2 CARD16 red + 2 CARD16 blue + 2 CARD16 green + 2 CARD16 alpha + +*/ + +static int +parse_xft_settings (prop, bytes, settings) + unsigned char *prop; + unsigned long bytes; + struct xsettings *settings; +{ + Lisp_Object byteorder = Fbyteorder (); + int my_bo = XFASTINT (byteorder) == 'B' ? MSBFirst : LSBFirst; + int that_bo = prop[0]; + CARD32 n_settings; + int bytes_parsed = 0; + int settings_seen = 0; + int i = 0; + + /* First 4 bytes is a serial number, skip that. */ + + if (bytes < 12) return BadLength; + memcpy (&n_settings, prop+8, 4); + if (my_bo != that_bo) n_settings = SWAP32 (n_settings); + bytes_parsed = 12; + + memset (settings, 0, sizeof (*settings)); + + while (bytes_parsed+4 < bytes && settings_seen < 6 + && i < n_settings) + { + int type = prop[bytes_parsed++]; + CARD16 nlen; + CARD32 vlen, ival = 0; + char name[128]; /* The names we are looking for are not this long. */ + char sval[128]; /* The values we are looking for are not this long. */ + int is_xft; + int to_cpy; + + sval[0] = '\0'; + ++i; + ++bytes_parsed; /* Padding */ + + memcpy (&nlen, prop+bytes_parsed, 2); + bytes_parsed += 2; + if (my_bo != that_bo) nlen = SWAP16 (nlen); + if (bytes_parsed+nlen > bytes) return BadLength; + to_cpy = nlen > 127 ? 127 : nlen; + memcpy (name, prop+bytes_parsed, to_cpy); + name[to_cpy] = '\0'; + + bytes_parsed += nlen; + bytes_parsed = PAD (bytes_parsed); + + bytes_parsed += 4; /* Skip serial for this value */ + if (bytes_parsed > bytes) return BadLength; + + is_xft = nlen > 6 && strncmp (name, "Xft/", 4) == 0; + + switch (type) + { + case 0: /* Integer */ + if (bytes_parsed+4 > bytes) return BadLength; + if (is_xft) + { + memcpy (&ival, prop+bytes_parsed, 4); + if (my_bo != that_bo) ival = SWAP32 (ival); + } + bytes_parsed += 4; + break; + + case 1: /* String */ + if (bytes_parsed+4 > bytes) return BadLength; + memcpy (&vlen, prop+bytes_parsed, 4); + bytes_parsed += 4; + if (my_bo != that_bo) vlen = SWAP32 (vlen); + if (is_xft) + { + to_cpy = vlen > 127 ? 127 : vlen; + memcpy (sval, prop+bytes_parsed, to_cpy); + sval[to_cpy] = '\0'; + } + bytes_parsed += vlen; + bytes_parsed = PAD (bytes_parsed); + break; + + case 2: /* RGB value */ + /* No need to parse this */ + if (bytes_parsed+8 > bytes) return BadLength; + bytes_parsed += 8; /* 4 values (r, b, g, alpha), 2 bytes each. */ + break; + + default: /* Parse Error */ + return BadValue; + } + + if (is_xft) + { + ++settings_seen; + if (strcmp (name, "Xft/Antialias") == 0) + settings->aa = ival != 0; + else if (strcmp (name, "Xft/Hinting") == 0) + settings->hinting = ival != 0; + else if (strcmp (name, "Xft/HintStyle") == 0) + { + if (strcmp (sval, "hintnone") == 0) + settings->hintstyle = FC_HINT_NONE; + else if (strcmp (sval, "hintslight") == 0) + settings->hintstyle = FC_HINT_SLIGHT; + else if (strcmp (sval, "hintmedium") == 0) + settings->hintstyle = FC_HINT_MEDIUM; + else if (strcmp (sval, "hintfull") == 0) + settings->hintstyle = FC_HINT_FULL; + } + else if (strcmp (name, "Xft/RGBA") == 0) + { + if (strcmp (sval, "none") == 0) + settings->rgba = FC_RGBA_NONE; + else if (strcmp (sval, "rgb") == 0) + settings->rgba = FC_RGBA_RGB; + else if (strcmp (sval, "bgr") == 0) + settings->rgba = FC_RGBA_BGR; + else if (strcmp (sval, "vrgb") == 0) + settings->rgba = FC_RGBA_VRGB; + else if (strcmp (sval, "vbgr") == 0) + settings->rgba = FC_RGBA_VBGR; + } + else if (strcmp (name, "Xft/DPI") == 0) + settings->dpi = (double)ival/1024.0; + else if (strcmp (name, "Xft/lcdfilter") == 0) + { + if (strcmp (sval, "none") == 0) + settings->lcdfilter = FC_LCD_NONE; + else if (strcmp (sval, "lcddefault") == 0) + settings->lcdfilter = FC_LCD_DEFAULT; + } + } + } + + return Success; +} + +static int +read_xft_settings (dpyinfo, settings) + struct x_display_info *dpyinfo; + struct xsettings *settings; +{ + long long_len; + Atom act_type; + int act_form; + unsigned long nitems, bytes_after; + unsigned char *prop = NULL; + Display *dpy = dpyinfo->display; + int rc; + + x_catch_errors (dpy); + rc = XGetWindowProperty (dpy, + dpyinfo->xsettings_window, + dpyinfo->Xatom_xsettings_prop, + 0, LONG_MAX, False, AnyPropertyType, + &act_type, &act_form, &nitems, &bytes_after, + &prop); + + if (rc == Success && prop != NULL && act_form == 8 && nitems > 0 + && act_type == dpyinfo->Xatom_xsettings_prop) + rc = parse_xft_settings (prop, nitems, settings); + + XFree (prop); + + x_uncatch_errors (); + + return rc == Success; +} + +static void +apply_xft_settings (dpyinfo, send_event_p) + struct x_display_info *dpyinfo; + int send_event_p; +{ + FcPattern *pat; + struct xsettings settings, oldsettings; + int changed = 0; + + if (!read_xft_settings (dpyinfo, &settings)) + return; + + memset (&oldsettings, 0, sizeof (oldsettings)); + + pat = FcPatternCreate (); + XftDefaultSubstitute (dpyinfo->display, + XScreenNumberOfScreen (dpyinfo->screen), + pat); + FcPatternGetBool (pat, FC_ANTIALIAS, 0, &oldsettings.aa); + FcPatternGetBool (pat, FC_HINTING, 0, &oldsettings.hinting); + FcPatternGetInteger (pat, FC_HINT_STYLE, 0, &oldsettings.hintstyle); + FcPatternGetInteger (pat, FC_LCD_FILTER, 0, &oldsettings.lcdfilter); + FcPatternGetInteger (pat, FC_RGBA, 0, &oldsettings.rgba); + FcPatternGetDouble (pat, FC_DPI, 0, &oldsettings.dpi); + + if (oldsettings.aa != settings.aa) + { + FcPatternDel (pat, FC_ANTIALIAS); + FcPatternAddBool (pat, FC_ANTIALIAS, settings.aa); + ++changed; + } + if (oldsettings.hinting != settings.hinting) + { + FcPatternDel (pat, FC_HINTING); + FcPatternAddBool (pat, FC_HINTING, settings.hinting); + ++changed; + } + if (oldsettings.rgba != settings.rgba) + { + FcPatternDel (pat, FC_RGBA); + FcPatternAddInteger (pat, FC_RGBA, settings.rgba); + ++changed; + } + if (oldsettings.lcdfilter != settings.lcdfilter) + { + FcPatternDel (pat, FC_LCD_FILTER); + FcPatternAddInteger (pat, FC_LCD_FILTER, settings.lcdfilter); + ++changed; + } + if (oldsettings.hintstyle != settings.hintstyle) + { + FcPatternDel (pat, FC_HINT_STYLE); + FcPatternAddInteger (pat, FC_HINT_STYLE, settings.hintstyle); + ++changed; + } + if (oldsettings.dpi != settings.dpi) + { + Lisp_Object frame, tail; + + FcPatternDel (pat, FC_DPI); + FcPatternAddDouble (pat, FC_DPI, settings.dpi); + ++changed; + + /* Change the DPI on this display and all frames on the display. */ + dpyinfo->resy = dpyinfo->resx = settings.dpi; + FOR_EACH_FRAME (tail, frame) + if (FRAME_X_P (XFRAME (frame)) + && FRAME_X_DISPLAY_INFO (XFRAME (frame)) == dpyinfo) + XFRAME (frame)->resy = XFRAME (frame)->resx = settings.dpi; + } + + if (changed) + { + XftDefaultSet (dpyinfo->display, pat); + if (send_event_p) + store_font_changed_event (Qfont_render, + XCAR (dpyinfo->name_list_element)); + } + else + FcPatternDestroy (pat); +} + +#endif /* HAVE_XFT */ + +void +xft_settings_event (dpyinfo, event) + struct x_display_info *dpyinfo; + XEvent *event; +{ +#ifdef HAVE_XFT + int check_window_p = 0; + + switch (event->type) + { + case DestroyNotify: + if (dpyinfo->xsettings_window == event->xany.window) + check_window_p = 1; + break; + + case ClientMessage: + if (event->xclient.message_type == dpyinfo->Xatom_xsettings_mgr + && event->xclient.data.l[1] == dpyinfo->Xatom_xsettings_sel + && event->xclient.window == dpyinfo->root_window) + check_window_p = 1; + break; + + case PropertyNotify: + if (event->xproperty.window == dpyinfo->xsettings_window + && event->xproperty.state == PropertyNewValue + && event->xproperty.atom == dpyinfo->Xatom_xsettings_prop) + { + apply_xft_settings (dpyinfo, True); + } + break; + } + + if (check_window_p) + { + dpyinfo->xsettings_window = None; + get_prop_window (dpyinfo); + if (dpyinfo->xsettings_window != None) + apply_xft_settings (dpyinfo, True); + } +#endif /* HAVE_XFT */ +} + + +static void +init_gconf () +{ +#ifdef HAVE_GCONF + int i; + char *s; + /* Should be enough, this is called at startup */ +#define N_FDS 1024 + int fd_before[N_FDS], fd_before1[N_FDS]; + int dummy, n_fds; + GPollFD gfds[N_FDS]; + + /* To find out which filedecriptors GConf uses, check before and after. + If we do not do this, GConf changes will only happen when Emacs gets + an X event. */ + memset (fd_before, 0, sizeof (fd_before)); + n_fds = g_main_context_query (g_main_context_default (), + G_PRIORITY_LOW, + &dummy, + gfds, + N_FDS); + for (i = 0; i < n_fds; ++i) + if (gfds[i].fd < N_FDS && gfds[i].fd > 0 && gfds[i].events > 0) + fd_before[gfds[i].fd] = 1; + + g_type_init (); + gconf_client = gconf_client_get_default (); + s = gconf_client_get_string (gconf_client, SYSTEM_MONO_FONT, NULL); + if (s) + { + current_mono_font = xstrdup (s); + g_free (s); + } + gconf_client_set_error_handling (gconf_client, GCONF_CLIENT_HANDLE_NONE); + gconf_client_add_dir (gconf_client, + SYSTEM_MONO_FONT, + GCONF_CLIENT_PRELOAD_ONELEVEL, + NULL); + gconf_client_notify_add (gconf_client, + SYSTEM_MONO_FONT, + something_changedCB, + NULL, NULL, NULL); + n_fds = g_main_context_query (g_main_context_default (), + G_PRIORITY_LOW, + &dummy, + gfds, + N_FDS); + + for (i = 0; i < n_fds; ++i) + if (gfds[i].fd < N_FDS && gfds[i].fd > 0 && gfds[i].events > 0 + && !fd_before[gfds[i].fd]) + { +#ifdef F_SETOWN + fcntl (i, F_SETOWN, getpid ()); +#endif /* ! defined (F_SETOWN) */ + +#ifdef SIGIO + if (interrupt_input) + init_sigio (i); +#endif /* ! defined (SIGIO) */ + } +#endif /* HAVE_GCONF */ +} + +static void +init_xfd_settings (dpyinfo) + struct x_display_info *dpyinfo; +{ +#ifdef HAVE_XFT + char sel[64]; + Display *dpy = dpyinfo->display; + + BLOCK_INPUT; + + sprintf (sel, "_XSETTINGS_S%d", XScreenNumberOfScreen (dpyinfo->screen)); + dpyinfo->Xatom_xsettings_sel = XInternAtom (dpy, sel, False); + dpyinfo->Xatom_xsettings_prop = XInternAtom (dpy, + "_XSETTINGS_SETTINGS", + False); + dpyinfo->Xatom_xsettings_mgr = XInternAtom (dpy, "MANAGER", False); + + /* Select events so we can detect client messages sent when selection + owner changes. */ + XSelectInput (dpy, dpyinfo->root_window, StructureNotifyMask); + + get_prop_window (dpyinfo); + if (dpyinfo->xsettings_window != None) + apply_xft_settings (dpyinfo, False); + + UNBLOCK_INPUT; + +#else /* ! HAVE_XFT */ + + dpyinfo->Xatom_xsettings_sel = None; + dpyinfo->Xatom_xsettings_prop = None; + dpyinfo->Xatom_xsettings_mgr = None; + dpyinfo->xsettings_window = None; + +#endif /* ! HAVE_XFT */ +} + +void +xsettings_initialize (dpyinfo) + struct x_display_info *dpyinfo; +{ + if (first_dpyinfo == NULL) first_dpyinfo = dpyinfo; + init_gconf (); + init_xfd_settings (dpyinfo); +} + + +DEFUN ("font-get-system-font", Ffont_get_system_font, Sfont_get_system_font, + 0, 0, 0, + doc: /* Get the system default monospaced font. */) + () +{ + return current_mono_font + ? make_string (current_mono_font, strlen (current_mono_font)) + : Qnil; +} + +void +syms_of_xsettings () +{ + current_mono_font = NULL; + first_dpyinfo = NULL; +#ifdef HAVE_GCONF + gconf_client = NULL; +#endif + + Qfont_name = intern_c_string ("font-name"); + staticpro (&Qfont_name); + Qfont_render = intern_c_string ("font-render"); + staticpro (&Qfont_render); + defsubr (&Sfont_get_system_font); + +#ifdef HAVE_GCONF + Fprovide (intern_c_string ("system-font-setting"), Qnil); +#endif +#ifdef HAVE_XFT + Fprovide (intern_c_string ("font-render-setting"), Qnil); +#endif +} diff --git a/src/xsettings.h b/src/xsettings.h new file mode 100644 index 0000000000..f8d73db2ee --- /dev/null +++ b/src/xsettings.h @@ -0,0 +1,29 @@ +/* Functions for handle font changes dynamically. + Copyright (C) 2009 + Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see . */ + +#ifndef XSETTINGS_H +#define XSETTINGS_H + +extern Lisp_Object Ffont_get_system_font P_ ((void)); +extern void xsettings_initialize P_ ((struct x_display_info *dpyinfo)); +extern void xft_settings_event P_ ((struct x_display_info *dpyinfo, + XEvent *)); + + +#endif /* XSETTINGS_H */ diff --git a/src/xterm.c b/src/xterm.c index 9936e98b1e..3349dc05b9 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -86,6 +86,7 @@ along with GNU Emacs. If not, see . */ #include "keymap.h" #include "font.h" #include "fontset.h" +#include "xsettings.h" #include "sysselect.h" #ifdef USE_X_TOOLKIT @@ -6026,6 +6027,8 @@ handle_one_xevent (dpyinfo, eventp, finish, hold_quit) goto done; } + xft_settings_event (dpyinfo, &event); + f = x_any_window_to_frame (dpyinfo, event.xclient.window); if (!f) goto OTHER; @@ -6088,6 +6091,7 @@ handle_one_xevent (dpyinfo, eventp, finish, hold_quit) x_handle_net_wm_state (f, &event.xproperty); x_handle_property_notify (&event.xproperty); + xft_settings_event (dpyinfo, &event); goto OTHER; case ReparentNotify: @@ -6991,6 +6995,10 @@ handle_one_xevent (dpyinfo, eventp, finish, hold_quit) } goto OTHER; + case DestroyNotify: + xft_settings_event (dpyinfo, &event); + break; + default: OTHER: #ifdef USE_X_TOOLKIT @@ -10300,17 +10308,33 @@ x_term_init (display_name, xrm_option, resource_name) dpyinfo->cmap = XCreateColormap (dpyinfo->display, dpyinfo->root_window, dpyinfo->visual, AllocNone); +#ifdef HAVE_XFT { - int screen_number = XScreenNumberOfScreen (dpyinfo->screen); - double pixels = DisplayHeight (dpyinfo->display, screen_number); - double mm = DisplayHeightMM (dpyinfo->display, screen_number); - /* Mac OS X 10.3's Xserver sometimes reports 0.0mm. */ - dpyinfo->resy = (mm < 1) ? 100 : pixels * 25.4 / mm; - pixels = DisplayWidth (dpyinfo->display, screen_number); - mm = DisplayWidthMM (dpyinfo->display, screen_number); - /* Mac OS X 10.3's Xserver sometimes reports 0.0mm. */ - dpyinfo->resx = (mm < 1) ? 100 : pixels * 25.4 / mm; + /* If we are using Xft, check dpi value in X resources. + It is better we use it as well, since Xft will use it, as will all + Gnome applications. If our real DPI is smaller or larger than the + one Xft uses, our font will look smaller or larger than other + for other applications, even if it is the same font name (monospace-10 + for example). */ + char *v = XGetDefault (dpyinfo->display, "Xft", "dpi"); + double d; + if (v != NULL && sscanf (v, "%lf", &d) == 1) + dpyinfo->resy = dpyinfo->resx = d; } +#endif + + if (dpyinfo->resy < 1) + { + int screen_number = XScreenNumberOfScreen (dpyinfo->screen); + double pixels = DisplayHeight (dpyinfo->display, screen_number); + double mm = DisplayHeightMM (dpyinfo->display, screen_number); + /* Mac OS X 10.3's Xserver sometimes reports 0.0mm. */ + dpyinfo->resy = (mm < 1) ? 100 : pixels * 25.4 / mm; + pixels = DisplayWidth (dpyinfo->display, screen_number); + mm = DisplayWidthMM (dpyinfo->display, screen_number); + /* Mac OS X 10.3's Xserver sometimes reports 0.0mm. */ + dpyinfo->resx = (mm < 1) ? 100 : pixels * 25.4 / mm; + } dpyinfo->Xatom_wm_protocols = XInternAtom (dpyinfo->display, "WM_PROTOCOLS", False); @@ -10415,6 +10439,8 @@ x_term_init (display_name, xrm_option, resource_name) xim_initialize (dpyinfo, resource_name); #endif + xsettings_initialize (dpyinfo); + #ifdef subprocesses /* This is only needed for distinguishing keyboard and process input. */ if (connection != 0) diff --git a/src/xterm.h b/src/xterm.h index e76634d7d4..1ad2a09701 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -364,6 +364,10 @@ struct x_display_info Atom Xatom_net_wm_state, Xatom_net_wm_state_fullscreen_atom, Xatom_net_wm_state_maximized_horz, Xatom_net_wm_state_maximized_vert, Xatom_net_wm_state_sticky; + + /* XSettings atoms and windows. */ + Atom Xatom_xsettings_sel, Xatom_xsettings_prop, Xatom_xsettings_mgr; + Window xsettings_window; }; #ifdef HAVE_X_I18N -- 2.20.1