unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp>
To: emacs-devel@gnu.org
Subject: Re: x-display-pixel-width/height inconsistency
Date: Sat, 27 Apr 2013 14:13:46 +0900	[thread overview]
Message-ID: <wlip38tv1x.wl%mituharu@math.s.chiba-u.ac.jp> (raw)
In-Reply-To: <wlk3oxifd8.wl%mituharu@math.s.chiba-u.ac.jp>

>>>>> On Sun, 24 Mar 2013 13:36:03 +0900, YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp> said:

>>> Tweeking x-display-pixel-width/height in a platform-specific way
>>> or adding functions such as display-usable-bounds in the NS port
>>> without considering the whole design for operations on monitors
>>> looks rather ad hoc to me.

>> Then please present a design that would make sense, not just an
>> ad-hoc set of changes to align all the platforms to the X11
>> behavior.

> Aligning all the platforms to the X11 is not ad hoc but makes
> several things consistent, not only behaviors among multiple
> platforms but also the argument convention among x-display-*
> functions.

> A possible design for the new function would be:

> DEFUN ("x-display-monitor-attributes-list",

The patch below implements this primitive for GTK+ builds (and also
contains a "degenerated" fallback for non-GTK+ X11 builds) as a
starting point for the discussion about its design.

What would the preferred format for rectangles be?  The patch uses the
format that geometry-related functions (such as x-parse-geometry) are
using, but it might look a bit awkward for this purpose because of
quoting "+" for possibly negative coordinates for positions.

				     YAMAMOTO Mitsuharu
				mituharu@math.s.chiba-u.ac.jp

=== modified file 'src/xfns.c'
*** src/xfns.c	2013-04-07 04:41:19 +0000
--- src/xfns.c	2013-04-26 13:58:27 +0000
***************
*** 126,131 ****
--- 126,132 ----
  static Lisp_Object Qsuppress_icon;
  static Lisp_Object Qundefined_color;
  static Lisp_Object Qcompound_text, Qcancel_timer;
+ static Lisp_Object Qgeometry, Qworkarea, Qmm_size, Qframes;
  Lisp_Object Qfont_param;
  
  #ifdef GLYPH_DEBUG
***************
*** 3791,3796 ****
--- 3792,4067 ----
    else
      return Qnil;
  }
+ 
+ /* Return an alist of the form
+    ((left . (+ XOFFSET)) (top . (+ YOFFSET))
+     (width . WIDTH) (height . HEIGHT))
+    with converting XOFFSET, YOFFSET, WIDTH and HEIGHT to Lisp
+    integers.  */
+ 
+ static Lisp_Object
+ x_create_geometry_alist (EMACS_INT xoffset, EMACS_INT yoffset,
+ 			 EMACS_INT width, EMACS_INT height)
+ {
+   return list4 (Fcons (Qleft, list2 (Qplus, make_number (xoffset))),
+ 		Fcons (Qtop, list2 (Qplus, make_number (yoffset))),
+ 		Fcons (Qwidth, make_number (width)),
+ 		Fcons (Qheight, make_number (height)));
+ }
+ 
+ /* Return an alist of the form ((width . WIDTH) (height . HEIGHT)).
+    with converting WIDTH and HEIGHT to Lisp integers.  */
+ 
+ static Lisp_Object
+ x_create_size_alist (EMACS_INT width, EMACS_INT height)
+ {
+   return list2 (Fcons (Qwidth, make_number (width)),
+ 		Fcons (Qheight, make_number (height)));
+ }
+ 
+ /* Store the geometry of the workarea on display DPYINFO into *RECT.
+    Return false if and only if the workarea information cannot be
+    obtained via the _NET_WORKAREA root window property.  */
+ 
+ static bool
+ x_get_net_workarea (struct x_display_info *dpyinfo, XRectangle *rect)
+ {
+   Display *dpy = dpyinfo->display;
+   long offset, max_len;
+   Atom target_type, actual_type;
+   unsigned long actual_size, bytes_remaining;
+   int rc, actual_format;
+   unsigned char *tmp_data = NULL;
+   bool result = false;
+ 
+   x_catch_errors (dpy);
+   offset = 0;
+   max_len = 1;
+   target_type = XA_CARDINAL;
+   rc = XGetWindowProperty (dpy, dpyinfo->root_window,
+ 			   dpyinfo->Xatom_net_current_desktop,
+ 			   offset, max_len, False, target_type,
+ 			   &actual_type, &actual_format, &actual_size,
+ 			   &bytes_remaining, &tmp_data);
+   if (rc == Success && actual_type == target_type && !x_had_errors_p (dpy)
+       && actual_format == 32 && actual_size == max_len)
+     {
+       long current_desktop = ((long *) tmp_data)[0];
+ 
+       XFree (tmp_data);
+       tmp_data = NULL;
+ 
+       offset = 4 * current_desktop;
+       max_len = 4;
+       rc = XGetWindowProperty (dpy, dpyinfo->root_window,
+ 			       dpyinfo->Xatom_net_workarea,
+ 			       offset, max_len, False, target_type,
+ 			       &actual_type, &actual_format, &actual_size,
+ 			       &bytes_remaining, &tmp_data);
+       if (rc == Success && actual_type == target_type && !x_had_errors_p (dpy)
+ 	  && actual_format == 32 && actual_size == max_len)
+ 	{
+ 	  long *workareas = (long *) tmp_data;
+ 
+ 	  rect->x = workareas[0];
+ 	  rect->y = workareas[1];
+ 	  rect->width = workareas[2];
+ 	  rect->height = workareas[3];
+ 
+ 	  XFree (tmp_data);
+ 	  tmp_data = NULL;
+ 
+ 	  result = true;
+ 	}
+     }
+   if (tmp_data)
+     XFree (tmp_data);
+   x_uncatch_errors ();
+ 
+   return result;
+ }
+ 
+ DEFUN ("x-display-monitor-attributes-list", Fx_display_monitor_attributes_list,
+        Sx_display_monitor_attributes_list,
+        0, 1, 0,
+        doc: /* Return a list of physical monitor attributes on X display.
+ Each element of the list represents the attributes of each physical
+ monitor.  The first element corresponds to the primary monitor.
+ 
+ Attributes for a physical monitor is represented as an alist of
+ attribute names and values as follows:
+ 
+     Name     | Value
+     ---------+--------------------------------------------------
+     geometry | Position and size in the form of
+ 	     | ((left . (+ XOFFSET)) (top . (+ YOFFSET))
+ 	     |  (width . WIDTH) (height . HEIGHT)).
+ 	     |
+     workarea | Position and size of the workarea in the form of
+ 	     | ((left . (+ XOFFSET)) (top . (+ YOFFSET))
+ 	     |  (width . WIDTH) (height . HEIGHT)).
+ 	     |
+     mm-size  | Width and height in millimeters in the form of
+ 	     | ((width . WIDTH) (height . HEIGHT)).
+ 	     |
+     frames   | List of frames belonging to the physical monitor.
+ 
+ where XOFFSET, YOFFSET, WIDTH, and HEIGHT are integers.  A frame
+ belongs to a monitor when either the largest area of the frame resides
+ in the monitor, or the monitor is the closest to the frame if the
+ frame does not intersect any monitors.  Every non-tip frame (including
+ invisible one) in a graphical display belongs to exactly one monitor.
+ 
+ The optional argument TERMINAL specifies which display to ask about.
+ TERMINAL should be a terminal object, a frame or a display name (a string).
+ If omitted or nil, that stands for the selected frame's display.  */)
+   (Lisp_Object terminal)
+ {
+   struct x_display_info *dpyinfo = check_x_display_info (terminal);
+   Lisp_Object attributes_list = Qnil, rest, frame;
+ 
+ #ifdef USE_GTK
+   float mm_width_per_pixel, mm_height_per_pixel;
+   GdkDisplay *gdpy;
+   GdkScreen *gscreen;
+   gint primary_monitor = 0, n_monitors, i;
+   Lisp_Object primary_monitor_attributes = Qnil;
+   Lisp_Object monitor_frames;
+ 
+   block_input ();
+   mm_width_per_pixel = ((float) WidthMMOfScreen (dpyinfo->screen)
+ 			/ x_display_pixel_width (dpyinfo));
+   mm_height_per_pixel = ((float) HeightMMOfScreen (dpyinfo->screen)
+ 			 / x_display_pixel_height (dpyinfo));
+   gdpy = gdk_x11_lookup_xdisplay (dpyinfo->display);
+   gscreen = gdk_display_get_default_screen (gdpy);
+ #if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION >= 20
+   primary_monitor = gdk_screen_get_primary_monitor (gscreen);
+ #endif
+   n_monitors = gdk_screen_get_n_monitors (gscreen);
+   monitor_frames = Fmake_vector (make_number (n_monitors), Qnil);
+   FOR_EACH_FRAME (rest, frame)
+     {
+       struct frame *f = XFRAME (frame);
+ 
+       if (FRAME_X_P (f) && FRAME_X_DISPLAY_INFO (f) == dpyinfo
+ 	  && !EQ (frame, tip_frame))
+ 	{
+ 	  GdkWindow *gwin = gtk_widget_get_window (FRAME_GTK_WIDGET (f));
+ 
+ 	  i = gdk_screen_get_monitor_at_window (gscreen, gwin);
+ 	  ASET (monitor_frames, i, Fcons (frame, AREF (monitor_frames, i)));
+ 	}
+     }
+ 
+   i = n_monitors;
+   while (i-- > 0)
+     {
+       Lisp_Object geometry, workarea, attributes = Qnil;
+       gint width_mm = -1, height_mm = -1;
+       GdkRectangle rec;
+ 
+       attributes = Fcons (Fcons (Qframes, AREF (monitor_frames, i)),
+ 			  attributes);
+ 
+       gdk_screen_get_monitor_geometry (gscreen, i, &rec);
+       geometry = x_create_geometry_alist (rec.x, rec.y, rec.width, rec.height);
+ 
+ #if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION >= 14
+       width_mm = gdk_screen_get_monitor_width_mm (gscreen, i);
+       height_mm = gdk_screen_get_monitor_height_mm (gscreen, i);
+ #endif
+       if (width_mm < 0)
+ 	width_mm = rec.width * mm_width_per_pixel + 0.5;
+       if (height_mm < 0)
+ 	height_mm = rec.height * mm_height_per_pixel + 0.5;
+       attributes = Fcons (Fcons (Qmm_size,
+ 				 x_create_size_alist (width_mm, height_mm)),
+ 			  attributes);
+ 
+ #if GTK_MAJOR_VERSION > 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 4)
+       gdk_screen_get_monitor_workarea (gscreen, i, &rec);
+       workarea = x_create_geometry_alist (rec.x, rec.y, rec.width, rec.height);
+ #else
+       /* Emulate the behavior of GTK+ 3.4.  */
+       {
+ 	XRectangle workarea_r;
+ 
+ 	workarea = Qnil;
+ 	if (i == primary_monitor && x_get_net_workarea (dpyinfo, &workarea_r))
+ 	  {
+ 	    GdkRectangle work;
+ 
+ 	    work.x = workarea_r.x;
+ 	    work.y = workarea_r.y;
+ 	    work.width = workarea_r.width;
+ 	    work.height = workarea_r.height;
+ 	    if (gdk_rectangle_intersect (&rec, &work, &work))
+ 	      workarea = x_create_geometry_alist (work.x, work.y,
+ 						  work.width, work.height);
+ 	  }
+ 	if (NILP (workarea))
+ 	  workarea = geometry;
+       }
+ #endif
+       attributes = Fcons (Fcons (Qworkarea, workarea), attributes);
+ 
+       attributes = Fcons (Fcons (Qgeometry, geometry), attributes);
+ 
+       if (i == primary_monitor)
+ 	primary_monitor_attributes = attributes;
+       else
+ 	attributes_list = Fcons (attributes, attributes_list);
+     }
+ 
+   if (!NILP (primary_monitor_attributes))
+     attributes_list = Fcons (primary_monitor_attributes, attributes_list);
+   unblock_input ();
+ #else  /* not USE_GTK */
+   /* Fallback: treat (possibly) multiple physical monitors as if they
+      formed a single monitor as a whole.  This should provide a
+      consistent result at least on single monitor environments.  */
+   Lisp_Object geometry, workarea, frames, attributes = Qnil;
+   int width_mm, height_mm;
+   XRectangle workarea_r;
+ 
+   block_input ();
+   frames = Qnil;
+   FOR_EACH_FRAME (rest, frame)
+     {
+       struct frame *f = XFRAME (frame);
+ 
+       if (FRAME_X_P (f) && FRAME_X_DISPLAY_INFO (f) == dpyinfo
+ 	  && !EQ (frame, tip_frame))
+ 	frames = Fcons (frame, frames);
+     }
+   attributes = Fcons (Fcons (Qframes, frames), attributes);
+ 
+   width_mm = WidthMMOfScreen (dpyinfo->screen);
+   height_mm = HeightMMOfScreen (dpyinfo->screen);
+   attributes = Fcons (Fcons (Qmm_size,
+ 			     x_create_size_alist (width_mm, height_mm)),
+ 		      attributes);
+ 
+   geometry = x_create_geometry_alist (0, 0, x_display_pixel_width (dpyinfo),
+ 				      x_display_pixel_height (dpyinfo));
+ 
+   if (x_get_net_workarea (dpyinfo, &workarea_r))
+     workarea = x_create_geometry_alist (workarea_r.x, workarea_r.y,
+ 					workarea_r.width, workarea_r.height);
+   else
+     workarea = geometry;
+   attributes = Fcons (Fcons (Qworkarea, workarea), attributes);
+ 
+   attributes = Fcons (Fcons (Qgeometry, geometry), attributes);
+ 
+   attributes_list = list1 (attributes);
+   unblock_input ();
+ #endif	/* not USE_GTK */
+ 
+   return attributes_list;
+ }
+ 
  \f
  int
  x_pixel_width (register struct frame *f)
***************
*** 5701,5706 ****
--- 5972,5981 ----
    DEFSYM (Qundefined_color, "undefined-color");
    DEFSYM (Qcompound_text, "compound-text");
    DEFSYM (Qcancel_timer, "cancel-timer");
+   DEFSYM (Qgeometry, "geometry");
+   DEFSYM (Qworkarea, "workarea");
+   DEFSYM (Qmm_size, "mm-size");
+   DEFSYM (Qframes, "frames");
    DEFSYM (Qfont_param, "font-parameter");
    /* This is the end of symbol initialization.  */
  
***************
*** 5864,5869 ****
--- 6139,6145 ----
    defsubr (&Sx_display_visual_class);
    defsubr (&Sx_display_backing_store);
    defsubr (&Sx_display_save_under);
+   defsubr (&Sx_display_monitor_attributes_list);
    defsubr (&Sx_wm_set_size_hint);
    defsubr (&Sx_create_frame);
    defsubr (&Sx_open_connection);

=== modified file 'src/xterm.c'
*** src/xterm.c	2013-03-25 17:58:35 +0000
--- src/xterm.c	2013-04-26 00:15:04 +0000
***************
*** 10251,10256 ****
--- 10251,10258 ----
        { "_NET_WM_WINDOW_OPACITY", &dpyinfo->Xatom_net_wm_window_opacity },
        { "_NET_ACTIVE_WINDOW", &dpyinfo->Xatom_net_active_window },
        { "_NET_FRAME_EXTENTS", &dpyinfo->Xatom_net_frame_extents },
+       { "_NET_CURRENT_DESKTOP", &dpyinfo->Xatom_net_current_desktop },
+       { "_NET_WORKAREA", &dpyinfo->Xatom_net_workarea },
        /* Session management */
        { "SM_CLIENT_ID", &dpyinfo->Xatom_SM_CLIENT_ID },
        { "_XSETTINGS_SETTINGS", &dpyinfo->Xatom_xsettings_prop },

=== modified file 'src/xterm.h'
*** src/xterm.h	2013-04-07 04:41:19 +0000
--- src/xterm.h	2013-04-26 00:15:24 +0000
***************
*** 346,352 ****
    Atom Xatom_net_wm_state, Xatom_net_wm_state_fullscreen,
      Xatom_net_wm_state_maximized_horz, Xatom_net_wm_state_maximized_vert,
      Xatom_net_wm_state_sticky, Xatom_net_wm_state_hidden,
!     Xatom_net_frame_extents;
  
    /* XSettings atoms and windows.  */
    Atom Xatom_xsettings_sel, Xatom_xsettings_prop, Xatom_xsettings_mgr;
--- 346,353 ----
    Atom Xatom_net_wm_state, Xatom_net_wm_state_fullscreen,
      Xatom_net_wm_state_maximized_horz, Xatom_net_wm_state_maximized_vert,
      Xatom_net_wm_state_sticky, Xatom_net_wm_state_hidden,
!     Xatom_net_frame_extents,
!     Xatom_net_current_desktop, Xatom_net_workarea;
  
    /* XSettings atoms and windows.  */
    Atom Xatom_xsettings_sel, Xatom_xsettings_prop, Xatom_xsettings_mgr;




  parent reply	other threads:[~2013-04-27  5:13 UTC|newest]

Thread overview: 97+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-03-21  0:58 x-display-pixel-width/height inconsistency grischka
2013-03-21  1:05 ` YAMAMOTO Mitsuharu
2013-03-21  1:09   ` grischka
2013-03-21  1:44     ` YAMAMOTO Mitsuharu
2013-03-21 23:29       ` YAMAMOTO Mitsuharu
2013-03-22 10:33         ` Eli Zaretskii
2013-03-23  0:32           ` YAMAMOTO Mitsuharu
2013-03-23  6:15             ` Eli Zaretskii
2013-03-23 13:35               ` Jan Djärv
2013-03-23 23:58               ` YAMAMOTO Mitsuharu
2013-03-24  3:53                 ` Eli Zaretskii
2013-03-24  4:36                   ` YAMAMOTO Mitsuharu
2013-03-24 16:19                     ` Eli Zaretskii
2013-04-27  5:13                     ` YAMAMOTO Mitsuharu [this message]
2013-04-27  8:04                       ` Jan Djärv
2013-04-28  1:40                         ` YAMAMOTO Mitsuharu
2013-04-28 17:16                           ` Jan D.
2013-04-29  2:27                             ` YAMAMOTO Mitsuharu
2013-04-29  2:42                               ` YAMAMOTO Mitsuharu
2013-05-01  9:58                               ` Jan Djärv
2013-05-02  4:09                                 ` YAMAMOTO Mitsuharu
2013-05-06  1:04                                   ` YAMAMOTO Mitsuharu
2013-05-06  1:55                                     ` Stefan Monnier
2013-05-06  6:15                                       ` YAMAMOTO Mitsuharu
2013-05-06 13:37                                         ` Stefan Monnier
2013-05-08 10:46                                         ` YAMAMOTO Mitsuharu
2013-05-08 11:24                                           ` YAMAMOTO Mitsuharu
2013-05-08 17:41                                           ` Eli Zaretskii
2013-05-09  0:09                                             ` YAMAMOTO Mitsuharu
2013-05-09  1:52                                               ` Glenn Morris
2013-05-09  3:19                                                 ` YAMAMOTO Mitsuharu
2013-05-09  6:27                                                   ` Glenn Morris
2013-05-09  2:53                                               ` Eli Zaretskii
2013-05-09  8:14                                               ` Jan Djärv
2013-05-09  8:43                                                 ` YAMAMOTO Mitsuharu
2013-05-09 15:18                                                   ` Jan Djärv
2013-05-09 20:03                                               ` Eli Zaretskii
2013-05-09 21:28                                                 ` Stefan Monnier
2013-05-10  6:00                                                 ` YAMAMOTO Mitsuharu
2013-05-10  6:05                                                   ` YAMAMOTO Mitsuharu
2013-05-10  7:06                                                   ` Eli Zaretskii
2013-05-10  7:47                                                     ` YAMAMOTO Mitsuharu
2013-05-10  8:41                                                       ` Eli Zaretskii
2013-05-10  8:55                                                         ` YAMAMOTO Mitsuharu
2013-05-10  9:15                                                           ` Eli Zaretskii
2013-05-10  9:27                                                             ` YAMAMOTO Mitsuharu
2013-05-14 10:39                                                               ` YAMAMOTO Mitsuharu
2013-07-01  6:49                                                                 ` martin rudalics
2013-07-02  1:30                                                                   ` YAMAMOTO Mitsuharu
2013-07-02 10:38                                                                     ` martin rudalics
2013-07-02 10:53                                                                       ` Juanma Barranquero
2013-07-02 13:11                                                                         ` martin rudalics
2013-07-02 14:05                                                                           ` Juanma Barranquero
2013-07-03  9:27                                                                             ` martin rudalics
2013-07-03 10:49                                                                               ` Juanma Barranquero
2013-07-03 12:44                                                                                 ` martin rudalics
2013-07-03 13:43                                                                                   ` Juanma Barranquero
2013-07-04  9:34                                                                                     ` martin rudalics
     [not found]                                                                                       ` <5987E3>
2013-07-04 22:32                                                                                       ` Juanma Barranquero
2013-07-05  7:44                                                                                         ` martin rudalics
2013-07-05  9:32                                                                                           ` Juanma Barranquero
2013-07-05  9:34                                                                                         ` Jan Djärv
2013-07-05  9:41                                                                                           ` Juanma Barranquero
2013-07-05 11:25                                                                                             ` Jan Djärv
2013-07-05 11:56                                                                                               ` Juanma Barranquero
2013-07-05 12:12                                                                                                 ` Jan Djärv
2013-07-05 12:16                                                                                                   ` Juanma Barranquero
2013-07-05 15:30                                                                                                     ` Drew Adams
2013-07-05 15:53                                                                                                       ` Juanma Barranquero
2013-07-05 16:58                                                                                                         ` Drew Adams
2013-07-06 14:48                                                                                                           ` Juanma Barranquero
2013-07-06 19:25                                                                                                             ` Drew Adams
2013-07-05 15:27                                                                                                   ` Drew Adams
2013-07-04 10:28                                                                                     ` YAMAMOTO Mitsuharu
2013-05-10  7:44                                                   ` Jan Djärv
2013-04-28  1:48                       ` YAMAMOTO Mitsuharu
  -- strict thread matches above, loose matches on Subject: below --
2013-03-19  0:39 YAMAMOTO Mitsuharu
2013-03-19  1:34 ` Leo Liu
2013-03-19  4:54   ` Xue Fuqiao
2013-03-19 15:41     ` Drew Adams
2013-03-19 15:51       ` Leo Liu
2013-03-19 15:58         ` Drew Adams
2013-03-20  0:55           ` Leo Liu
2013-03-19 22:25 ` YAMAMOTO Mitsuharu
2013-03-19 23:15   ` Dmitry Gutov
2013-03-19 23:52     ` YAMAMOTO Mitsuharu
2013-03-20  0:12       ` Dmitry Gutov
2013-03-20  0:20         ` YAMAMOTO Mitsuharu
2013-03-20  1:41           ` Dmitry Gutov
2013-03-20  3:58             ` YAMAMOTO Mitsuharu
2013-03-20 14:05               ` Dmitry Gutov
2013-03-20 23:28                 ` YAMAMOTO Mitsuharu
2013-03-21  1:27                   ` Dmitry Gutov
2013-03-21  1:51                     ` YAMAMOTO Mitsuharu
2013-03-21  2:43                       ` Dmitry Gutov
2013-03-21  3:47                         ` YAMAMOTO Mitsuharu
2013-03-21  4:22                           ` YAMAMOTO Mitsuharu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=wlip38tv1x.wl%mituharu@math.s.chiba-u.ac.jp \
    --to=mituharu@math.s.chiba-u.ac.jp \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).