diff options
Diffstat (limited to 'src/xfns.c')
-rw-r--r-- | src/xfns.c | 4278 |
1 files changed, 3331 insertions, 947 deletions
diff --git a/src/xfns.c b/src/xfns.c index 9022e4a9674..4df5ad890e8 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -1,6 +1,6 @@ -/* Functions for the X window system. +/* Functions for the X Window System. -Copyright (C) 1989, 1992-2017 Free Software Foundation, Inc. +Copyright (C) 1989, 1992-2022 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -24,6 +24,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include <unistd.h> #include "lisp.h" +#include "character.h" #include "xterm.h" #include "frame.h" #include "window.h" @@ -39,6 +40,12 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include <sys/types.h> #include <sys/stat.h> +#ifdef USE_XCB +#include <xcb/xcb.h> +#include <xcb/xproto.h> +#include <xcb/xcb_aux.h> +#endif + #include "bitmaps/gray.xbm" #include "xsettings.h" @@ -57,6 +64,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include <X11/extensions/Xdbe.h> #endif +#ifdef HAVE_XINPUT2 +#include <X11/extensions/XInput2.h> +#endif + #ifdef USE_X_TOOLKIT #include <X11/Shell.h> @@ -215,8 +226,9 @@ x_real_pos_and_offsets (struct frame *f, int win_x = 0, win_y = 0, outer_x = 0, outer_y = 0; int real_x = 0, real_y = 0; bool had_errors = false; - Window win = (FRAME_PARENT_FRAME (f) - ? FRAME_X_WINDOW (FRAME_PARENT_FRAME (f)) + struct frame *parent_frame = FRAME_PARENT_FRAME (f); + Window win = (parent_frame + ? FRAME_X_WINDOW (parent_frame) : f->output_data.x->parent_desc); struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); long max_len = 400; @@ -273,7 +285,7 @@ x_real_pos_and_offsets (struct frame *f, should be the outer WM window. */ for (;;) { - Window wm_window, rootw; + Window wm_window UNINIT, rootw UNINIT; #ifdef USE_XCB xcb_query_tree_cookie_t query_tree_cookie; @@ -355,8 +367,8 @@ x_real_pos_and_offsets (struct frame *f, outer_geom_cookie = xcb_get_geometry (xcb_conn, FRAME_OUTER_WINDOW (f)); - if ((dpyinfo->root_window == f->output_data.x->parent_desc) - && !FRAME_PARENT_FRAME (f)) + if (!parent_frame + && dpyinfo->root_window == f->output_data.x->parent_desc) /* Try _NET_FRAME_EXTENTS if our parent is the root window. */ prop_cookie = xcb_get_property (xcb_conn, 0, win, dpyinfo->Xatom_net_frame_extents, @@ -470,8 +482,7 @@ x_real_pos_and_offsets (struct frame *f, #endif } - if ((dpyinfo->root_window == f->output_data.x->parent_desc) - && !FRAME_PARENT_FRAME (f)) + if (!parent_frame && dpyinfo->root_window == f->output_data.x->parent_desc) { /* Try _NET_FRAME_EXTENTS if our parent is the root window. */ #ifdef USE_XCB @@ -598,24 +609,24 @@ x_relative_mouse_position (struct frame *f, int *x, int *y) block_input (); - XQueryPointer (FRAME_X_DISPLAY (f), - DefaultRootWindow (FRAME_X_DISPLAY (f)), + x_query_pointer (FRAME_X_DISPLAY (f), + FRAME_DISPLAY_INFO (f)->root_window, - /* The root window which contains the pointer. */ - &root, + /* The root window which contains the pointer. */ + &root, - /* Window pointer is on, not used */ - &dummy_window, + /* Window pointer is on, not used */ + &dummy_window, - /* The position on that root window. */ - x, y, + /* The position on that root window. */ + x, y, - /* x/y in dummy_window coordinates, not used. */ - &dummy, &dummy, + /* x/y in dummy_window coordinates, not used. */ + &dummy, &dummy, - /* Modifier keys and pointer buttons, about which - we don't care. */ - (unsigned int *) &dummy); + /* Modifier keys and pointer buttons, about which + we don't care. */ + (unsigned int *) &dummy); XTranslateCoordinates (FRAME_X_DISPLAY (f), @@ -653,7 +664,7 @@ gamma_correct (struct frame *f, XColor *color) bool x_defined_color (struct frame *f, const char *color_name, - XColor *color, bool alloc_p) + Emacs_Color *color, bool alloc_p, bool _makeIndex) { bool success_p = false; Colormap cmap = FRAME_X_COLORMAP (f); @@ -676,7 +687,7 @@ x_defined_color (struct frame *f, const char *color_name, is a monochrome frame, return MONO_COLOR regardless of what ARG says. Signal an error if color can't be allocated. */ -static int +static unsigned long x_decode_color (struct frame *f, Lisp_Object color_name, int mono_color) { XColor cdef; @@ -698,7 +709,7 @@ x_decode_color (struct frame *f, Lisp_Object color_name, int mono_color) /* x_defined_color is responsible for coping with failures by looking for a near-miss. */ - if (x_defined_color (f, SSDATA (color_name), &cdef, true)) + if (x_defined_color (f, SSDATA (color_name), &cdef, true, false)) return cdef.pixel; signal_error ("Undefined color", color_name); @@ -717,6 +728,78 @@ x_set_wait_for_wm (struct frame *f, Lisp_Object new_value, Lisp_Object old_value } static void +x_set_alpha_background (struct frame *f, Lisp_Object arg, Lisp_Object oldval) +{ + unsigned long opaque_region[] = {0, 0, FRAME_PIXEL_WIDTH (f), + FRAME_PIXEL_HEIGHT (f)}; +#ifdef HAVE_GTK3 + GObjectClass *object_class; + GtkWidgetClass *class; +#endif + + gui_set_alpha_background (f, arg, oldval); + +#ifdef HAVE_XRENDER + /* Setting `alpha_background' to something other than opaque on a + display that doesn't support the required features leads to + confusing results. */ + if (f->alpha_background < 1.0 + && !FRAME_DISPLAY_INFO (f)->alpha_bits + && !FRAME_CHECK_XR_VERSION (f, 0, 2)) + f->alpha_background = 1.0; +#else + f->alpha_background = 1.0; +#endif + +#ifdef USE_GTK + /* This prevents GTK from painting the window's background, which + interferes with transparent background in some environments */ + + if (!FRAME_TOOLTIP_P (f)) + gtk_widget_set_app_paintable (FRAME_GTK_OUTER_WIDGET (f), + f->alpha_background != 1.0); +#endif + + if (!FRAME_DISPLAY_INFO (f)->alpha_bits) + return; + + if (f->alpha_background != 1.0) + { + XChangeProperty (FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + FRAME_DISPLAY_INFO (f)->Xatom_net_wm_opaque_region, + XA_CARDINAL, 32, PropModeReplace, + NULL, 0); + } +#ifndef HAVE_GTK3 + else + XChangeProperty (FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + FRAME_DISPLAY_INFO (f)->Xatom_net_wm_opaque_region, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &opaque_region, 4); +#else + else + { + if (FRAME_TOOLTIP_P (f)) + XChangeProperty (FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + FRAME_DISPLAY_INFO (f)->Xatom_net_wm_opaque_region, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &opaque_region, 4); + else + { + object_class = G_OBJECT_GET_CLASS (FRAME_GTK_OUTER_WIDGET (f)); + class = GTK_WIDGET_CLASS (object_class); + + if (class->style_updated) + class->style_updated (FRAME_GTK_OUTER_WIDGET (f)); + } + } +#endif +} + +static void x_set_tool_bar_position (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) @@ -740,22 +823,24 @@ x_set_tool_bar_position (struct frame *f, wrong_choice (choice, new_value); } +#ifdef HAVE_XDBE static void x_set_inhibit_double_buffering (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) { - block_input (); + bool want_double_buffering, was_double_buffered; + if (FRAME_X_WINDOW (f) && !EQ (new_value, old_value)) { - bool want_double_buffering = NILP (new_value); - bool was_double_buffered = FRAME_X_DOUBLE_BUFFERED_P (f); - /* font_drop_xrender_surfaces in xftfont does something only if - we're double-buffered, so call font_drop_xrender_surfaces before - and after any potential change. One of the calls will end up - being a no-op. */ + want_double_buffering = NILP (new_value); + was_double_buffered = FRAME_X_DOUBLE_BUFFERED_P (f); + + block_input (); if (want_double_buffering != was_double_buffered) - font_drop_xrender_surfaces (f); + /* Force XftDraw etc to be recreated with the new double + buffered drawable. */ + font_drop_xrender_surfaces (f); if (FRAME_X_DOUBLE_BUFFERED_P (f) && !want_double_buffering) tear_down_x_back_buffer (f); else if (!FRAME_X_DOUBLE_BUFFERED_P (f) && want_double_buffering) @@ -765,9 +850,10 @@ x_set_inhibit_double_buffering (struct frame *f, SET_FRAME_GARBAGED (f); font_drop_xrender_surfaces (f); } + unblock_input (); } - unblock_input (); } +#endif /** * x_set_undecorated: @@ -792,7 +878,7 @@ x_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value #else Display *dpy = FRAME_X_DISPLAY (f); PropMotifWmHints hints; - Atom prop = XInternAtom (dpy, "_MOTIF_WM_HINTS", False); + Atom prop = FRAME_DISPLAY_INFO (f)->Xatom_MOTIF_WM_HINTS; memset (&hints, 0, sizeof(hints)); hints.flags = MWM_HINTS_DECORATIONS; @@ -844,6 +930,9 @@ static void x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) { struct frame *p = NULL; +#ifdef HAVE_GTK3 + GdkWindow *window; +#endif if (!NILP (new_value) && (!FRAMEP (new_value) @@ -859,8 +948,32 @@ x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_valu block_input (); XReparentWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), - p ? FRAME_X_WINDOW (p) : DefaultRootWindow (FRAME_X_DISPLAY (f)), + p ? FRAME_X_WINDOW (p) : FRAME_DISPLAY_INFO (f)->root_window, f->left_pos, f->top_pos); +#ifdef USE_GTK + if (EQ (x_gtk_resize_child_frames, Qresize_mode)) + gtk_container_set_resize_mode + (GTK_CONTAINER (FRAME_GTK_OUTER_WIDGET (f)), + p ? GTK_RESIZE_IMMEDIATE : GTK_RESIZE_QUEUE); +#endif + +#ifdef HAVE_GTK3 + if (p) + { + window = gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f)); + gdk_x11_window_set_frame_sync_enabled (window, FALSE); + } +#endif + +#if defined HAVE_XSYNC && !defined USE_GTK && defined HAVE_CLOCK_GETTIME + /* Frame synchronization can't be used in child frames since + they are not directly managed by the compositing manager. + Re-enabling vsync in former child frames also leads to + inconsistent display. In addition, they can only be updated + outside of a toplevel frame. */ + FRAME_X_OUTPUT (f)->use_vsync_p = false; + FRAME_X_WAITING_FOR_DRAW (f) = false; +#endif unblock_input (); fset_parent_frame (f, new_value); @@ -887,7 +1000,7 @@ x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, Lisp_Object old_v xg_set_no_focus_on_map (f, new_value); #else /* not USE_GTK */ Display *dpy = FRAME_X_DISPLAY (f); - Atom prop = XInternAtom (dpy, "_NET_WM_USER_TIME", False); + Atom prop = FRAME_DISPLAY_INFO (f)->Xatom_net_wm_user_time; Time timestamp = NILP (new_value) ? CurrentTime : 0; XChangeProperty (dpy, FRAME_OUTER_WINDOW (f), prop, @@ -978,7 +1091,7 @@ xg_set_icon (struct frame *f, Lisp_Object file) bool result = false; Lisp_Object found; - found = x_find_image_file (file); + found = image_find_image_file (file); if (! NILP (found)) { @@ -1021,7 +1134,7 @@ xg_set_icon_from_xpm_data (struct frame *f, const char **data) #endif /* USE_GTK */ -/* Functions called only from `x_set_frame_param' +/* Functions called only from `gui_set_frame_parameters' to set individual parameters. If FRAME_X_WINDOW (f) is 0, @@ -1089,20 +1202,6 @@ x_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) xg_set_background_color (f, bg); #endif -#ifndef USE_TOOLKIT_SCROLL_BARS /* Turns out to be annoying with - toolkit scroll bars. */ - { - Lisp_Object bar; - for (bar = FRAME_SCROLL_BARS (f); - !NILP (bar); - bar = XSCROLL_BAR (bar)->next) - { - Window window = XSCROLL_BAR (bar)->x_window; - XSetWindowBackground (dpy, window, bg); - } - } -#endif /* USE_TOOLKIT_SCROLL_BARS */ - unblock_input (); update_face_from_frame_parameter (f, Qbackground_color, arg); @@ -1146,25 +1245,27 @@ struct mouse_cursor_types { }; /* This array must stay in sync with enum mouse_cursor above! */ -static const struct mouse_cursor_types mouse_cursor_types[] = { - { "text", &Vx_pointer_shape, XC_xterm }, - { "nontext", &Vx_nontext_pointer_shape, XC_left_ptr }, - { "hourglass", &Vx_hourglass_pointer_shape, XC_watch }, - { "modeline", &Vx_mode_pointer_shape, XC_xterm }, - { NULL, &Vx_sensitive_text_pointer_shape, XC_hand2 }, - { NULL, &Vx_window_horizontal_drag_shape, XC_sb_h_double_arrow }, - { NULL, &Vx_window_vertical_drag_shape, XC_sb_v_double_arrow }, - { NULL, &Vx_window_left_edge_shape, XC_left_side }, - { NULL, &Vx_window_top_left_corner_shape, XC_top_left_corner }, - { NULL, &Vx_window_top_edge_shape, XC_top_side }, - { NULL, &Vx_window_top_right_corner_shape, XC_top_right_corner }, - { NULL, &Vx_window_right_edge_shape, XC_right_side }, - { NULL, &Vx_window_bottom_right_corner_shape, XC_bottom_right_corner }, - { NULL, &Vx_window_bottom_edge_shape, XC_bottom_side }, - { NULL, &Vx_window_bottom_left_corner_shape, XC_bottom_left_corner }, -}; - -struct mouse_cursor_data { +static const struct mouse_cursor_types mouse_cursor_types[] = + { + { "text", &Vx_pointer_shape, XC_xterm }, + { "nontext", &Vx_nontext_pointer_shape, XC_left_ptr }, + { "hourglass", &Vx_hourglass_pointer_shape, XC_watch }, + { "modeline", &Vx_mode_pointer_shape, XC_xterm }, + { NULL, &Vx_sensitive_text_pointer_shape, XC_hand2 }, + { NULL, &Vx_window_horizontal_drag_shape, XC_sb_h_double_arrow }, + { NULL, &Vx_window_vertical_drag_shape, XC_sb_v_double_arrow }, + { NULL, &Vx_window_left_edge_shape, XC_left_side }, + { NULL, &Vx_window_top_left_corner_shape, XC_top_left_corner }, + { NULL, &Vx_window_top_edge_shape, XC_top_side }, + { NULL, &Vx_window_top_right_corner_shape, XC_top_right_corner }, + { NULL, &Vx_window_right_edge_shape, XC_right_side }, + { NULL, &Vx_window_bottom_right_corner_shape, XC_bottom_right_corner }, + { NULL, &Vx_window_bottom_edge_shape, XC_bottom_side }, + { NULL, &Vx_window_bottom_left_corner_shape, XC_bottom_left_corner }, + }; + +struct mouse_cursor_data +{ /* Last index for which XCreateFontCursor has been called, and thus the last index for which x_request_serial[] is valid. */ int last_cursor_create_request; @@ -1230,13 +1331,10 @@ x_set_mouse_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) for (i = 0; i < mouse_cursor_max; i++) { Lisp_Object shape_var = *mouse_cursor_types[i].shape_var_ptr; - if (!NILP (shape_var)) - { - CHECK_TYPE_RANGED_INTEGER (unsigned, shape_var); - cursor_data.cursor_num[i] = XINT (shape_var); - } - else - cursor_data.cursor_num[i] = mouse_cursor_types[i].default_shape; + cursor_data.cursor_num[i] + = (!NILP (shape_var) + ? check_uinteger_max (shape_var, UINT_MAX) + : mouse_cursor_types[i].default_shape); } block_input (); @@ -1248,8 +1346,10 @@ x_set_mouse_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) { cursor_data.x_request_serial[i] = XNextRequest (dpy); cursor_data.last_cursor_create_request = i; - cursor_data.cursor[i] = XCreateFontCursor (dpy, - cursor_data.cursor_num[i]); + + cursor_data.cursor[i] + = x_create_font_cursor (FRAME_DISPLAY_INFO (f), + cursor_data.cursor_num[i]); } /* Now sync up and process all received errors from cursor @@ -1388,8 +1488,8 @@ x_set_cursor_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) if (FRAME_VISIBLE_P (f)) { - x_update_cursor (f, false); - x_update_cursor (f, true); + gui_update_cursor (f, false); + gui_update_cursor (f, true); } } @@ -1401,11 +1501,26 @@ x_set_cursor_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) F has an x-window. */ static void -x_set_border_pixel (struct frame *f, int pix) +x_set_border_pixel (struct frame *f, unsigned long pix) { unload_color (f, f->output_data.x->border_pixel); f->output_data.x->border_pixel = pix; +#ifdef USE_X_TOOLKIT + if (f->output_data.x->widget && f->border_width > 0) + { + block_input (); + XtVaSetValues (f->output_data.x->widget, XtNborderColor, + (Pixel) pix, NULL); + unblock_input (); + + if (FRAME_VISIBLE_P (f)) + redraw_frame (f); + + return; + } +#endif + if (FRAME_X_WINDOW (f) != 0 && f->border_width > 0) { block_input (); @@ -1431,7 +1546,7 @@ x_set_border_pixel (struct frame *f, int pix) static void x_set_border_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) { - int pix; + unsigned long pix; CHECK_STRING (arg); pix = x_decode_color (f, arg, BLACK_PIX_DEFAULT (f)); @@ -1453,10 +1568,10 @@ x_set_icon_type (struct frame *f, Lisp_Object arg, Lisp_Object oldval) if (STRINGP (arg)) { - if (STRINGP (oldval) && EQ (Fstring_equal (oldval, arg), Qt)) + if (STRINGP (oldval) && BASE_EQ (Fstring_equal (oldval, arg), Qt)) return; } - else if (!STRINGP (oldval) && EQ (oldval, Qnil) == EQ (arg, Qnil)) + else if (!STRINGP (oldval) && NILP (oldval) == NILP (arg)) return; block_input (); @@ -1466,7 +1581,7 @@ x_set_icon_type (struct frame *f, Lisp_Object arg, Lisp_Object oldval) ? f->icon_name : f->name))); else - result = x_bitmap_icon (f, arg); + result = FRAME_TERMINAL (f)->set_bitmap_icon_hook (f, arg); if (result) { @@ -1485,7 +1600,7 @@ x_set_icon_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval) if (STRINGP (arg)) { - if (STRINGP (oldval) && EQ (Fstring_equal (oldval, arg), Qt)) + if (STRINGP (oldval) && BASE_EQ (Fstring_equal (oldval, arg), Qt)) return; } else if (!NILP (arg) || NILP (oldval)) @@ -1531,8 +1646,8 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) if (FRAME_MINIBUF_ONLY_P (f) || FRAME_PARENT_FRAME (f)) return; - if (TYPE_RANGED_INTEGERP (int, value)) - nlines = XINT (value); + if (TYPE_RANGED_FIXNUMP (int, value)) + nlines = XFIXNUM (value); else nlines = 0; @@ -1560,7 +1675,6 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) #else /* not USE_X_TOOLKIT && not USE_GTK */ FRAME_MENU_BAR_LINES (f) = nlines; FRAME_MENU_BAR_HEIGHT (f) = nlines * FRAME_LINE_HEIGHT (f); - adjust_frame_size (f, -1, -1, 2, true, Qx_set_menu_bar_lines); if (FRAME_X_WINDOW (f)) x_clear_under_internal_border (f); @@ -1574,6 +1688,8 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) int width = FRAME_PIXEL_WIDTH (f); int y; + adjust_frame_size (f, -1, -1, 3, true, Qmenu_bar_lines); + /* height can be zero here. */ if (FRAME_X_WINDOW (f) && height > 0 && width > 0) { @@ -1602,6 +1718,85 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) } +/* Set the number of lines used for the tab bar of frame F to VALUE. + VALUE not an integer, or < 0 means set the lines to zero. OLDVAL + is the old number of tab bar lines. This function may change the + height of all windows on frame F to match the new tab bar height. + The frame's height may change if frame_inhibit_implied_resize was + set accordingly. */ + +static void +x_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) +{ + int olines = FRAME_TAB_BAR_LINES (f); + int nlines; + + /* Treat tab bars like menu bars. */ + if (FRAME_MINIBUF_ONLY_P (f)) + return; + + /* Use VALUE only if an int >= 0. */ + if (RANGED_FIXNUMP (0, value, INT_MAX)) + nlines = XFIXNAT (value); + else + nlines = 0; + + if (nlines != olines && (olines == 0 || nlines == 0)) + x_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f)); +} + + +/* Set the pixel height of the tab bar of frame F to HEIGHT. */ +void +x_change_tab_bar_height (struct frame *f, int height) +{ + int unit = FRAME_LINE_HEIGHT (f); + int old_height = FRAME_TAB_BAR_HEIGHT (f); + int lines = (height + unit - 1) / unit; + Lisp_Object fullscreen = get_frame_param (f, Qfullscreen); + + /* Make sure we redisplay all windows in this frame. */ + fset_redisplay (f); + + /* Recalculate tab bar and frame text sizes. */ + FRAME_TAB_BAR_HEIGHT (f) = height; + FRAME_TAB_BAR_LINES (f) = lines; + store_frame_param (f, Qtab_bar_lines, make_fixnum (lines)); + + if (FRAME_X_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0) + { + clear_frame (f); + clear_current_matrices (f); + } + + if ((height < old_height) && WINDOWP (f->tab_bar_window)) + clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix); + + if (!f->tab_bar_resized) + { + /* As long as tab_bar_resized is false, effectively try to change + F's native height. */ + if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth)) + adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), + 1, false, Qtab_bar_lines); + else + adjust_frame_size (f, -1, -1, 4, false, Qtab_bar_lines); + + f->tab_bar_resized = f->tab_bar_redisplayed; + } + else + /* Any other change may leave the native size of F alone. */ + adjust_frame_size (f, -1, -1, 3, false, Qtab_bar_lines); + + /* adjust_frame_size might not have done anything, garbage frame + here. */ + adjust_frame_glyphs (f); + SET_FRAME_GARBAGED (f); + if (FRAME_X_WINDOW (f)) + x_clear_under_internal_border (f); +} + + /* Set the number of lines used for the tool bar of frame F to VALUE. VALUE not an integer, or < 0 means set the lines to zero. OLDVAL is the old number of tool bar lines. This function changes the @@ -1618,8 +1813,8 @@ x_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) return; /* Use VALUE only if an int >= 0. */ - if (RANGED_INTEGERP (0, value, INT_MAX)) - nlines = XFASTINT (value); + if (RANGED_FIXNUMP (0, value, INT_MAX)) + nlines = XFIXNAT (value); else nlines = 0; @@ -1652,24 +1847,15 @@ x_change_tool_bar_height (struct frame *f, int height) int unit = FRAME_LINE_HEIGHT (f); int old_height = FRAME_TOOL_BAR_HEIGHT (f); int lines = (height + unit - 1) / unit; - Lisp_Object fullscreen; + Lisp_Object fullscreen = get_frame_param (f, Qfullscreen); /* Make sure we redisplay all windows in this frame. */ fset_redisplay (f); - /* Recalculate tool bar and frame text sizes. */ FRAME_TOOL_BAR_HEIGHT (f) = height; FRAME_TOOL_BAR_LINES (f) = lines; - /* Store the `tool-bar-lines' and `height' frame parameters. */ - store_frame_param (f, Qtool_bar_lines, make_number (lines)); - store_frame_param (f, Qheight, make_number (FRAME_LINES (f))); - - /* We also have to make sure that the internal border at the top of - the frame, below the menu bar or tool bar, is redrawn when the - tool bar disappears. This is so because the internal border is - below the tool bar if one is displayed, but is below the menu bar - if there isn't a tool bar. The tool bar draws into the area - below the menu bar. */ + store_frame_param (f, Qtool_bar_lines, make_fixnum (lines)); + if (FRAME_X_WINDOW (f) && FRAME_TOOL_BAR_HEIGHT (f) == 0) { clear_frame (f); @@ -1679,25 +1865,21 @@ x_change_tool_bar_height (struct frame *f, int height) if ((height < old_height) && WINDOWP (f->tool_bar_window)) clear_glyph_matrix (XWINDOW (f->tool_bar_window)->current_matrix); - /* Recalculate toolbar height. */ - f->n_tool_bar_rows = 0; - if (old_height == 0 - && (!f->after_make_frame - || NILP (frame_inhibit_implied_resize) - || (CONSP (frame_inhibit_implied_resize) - && NILP (Fmemq (Qtool_bar_lines, frame_inhibit_implied_resize))))) - f->tool_bar_redisplayed = f->tool_bar_resized = false; - - adjust_frame_size (f, -1, -1, - ((!f->tool_bar_resized - && (NILP (fullscreen = - get_frame_param (f, Qfullscreen)) - || EQ (fullscreen, Qfullwidth))) ? 1 - : (old_height == 0 || height == 0) ? 2 - : 4), - false, Qtool_bar_lines); - - f->tool_bar_resized = f->tool_bar_redisplayed; + if (!f->tool_bar_resized) + { + /* As long as tool_bar_resized is false, effectively try to change + F's native height. */ + if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth)) + adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), + 1, false, Qtool_bar_lines); + else + adjust_frame_size (f, -1, -1, 4, false, Qtool_bar_lines); + + f->tool_bar_resized = f->tool_bar_redisplayed; + } + else + /* Any other change may leave the native size of F alone. */ + adjust_frame_size (f, -1, -1, 3, false, Qtool_bar_lines); /* adjust_frame_size might not have done anything, garbage frame here. */ @@ -1709,14 +1891,40 @@ x_change_tool_bar_height (struct frame *f, int height) #endif /* USE_GTK */ } - static void -x_set_internal_border_width (struct frame *f, Lisp_Object arg, Lisp_Object oldval) +x_set_child_frame_border_width (struct frame *f, Lisp_Object arg, Lisp_Object oldval) { int border; - CHECK_TYPE_RANGED_INTEGER (int, arg); - border = max (XINT (arg), 0); + if (NILP (arg)) + border = -1; + else if (RANGED_FIXNUMP (0, arg, INT_MAX)) + border = XFIXNAT (arg); + else + signal_error ("Invalid child frame border width", arg); + + if (border != FRAME_CHILD_FRAME_BORDER_WIDTH (f)) + { + f->child_frame_border_width = border; + +#ifdef USE_X_TOOLKIT + if (FRAME_X_OUTPUT (f)->edit_widget) + widget_store_internal_border (FRAME_X_OUTPUT (f)->edit_widget); +#endif + + if (FRAME_X_WINDOW (f)) + { + adjust_frame_size (f, -1, -1, 3, false, Qchild_frame_border_width); + x_clear_under_internal_border (f); + } + } + +} + +static void +x_set_internal_border_width (struct frame *f, Lisp_Object arg, Lisp_Object oldval) +{ + int border = check_int_nonnegative (arg); if (border != FRAME_INTERNAL_BORDER_WIDTH (f)) { @@ -1746,6 +1954,10 @@ static void x_set_scroll_bar_foreground (struct frame *f, Lisp_Object value, Lisp_Object oldval) { unsigned long pixel; +#ifdef HAVE_GTK3 + XColor color; + char css[64]; +#endif if (STRINGP (value)) pixel = x_decode_color (f, value, BLACK_PIX_DEFAULT (f)); @@ -1767,6 +1979,28 @@ x_set_scroll_bar_foreground (struct frame *f, Lisp_Object value, Lisp_Object old update_face_from_frame_parameter (f, Qscroll_bar_foreground, value); redraw_frame (f); } + +#ifdef HAVE_GTK3 + if (!FRAME_TOOLTIP_P (f)) + { + if (pixel != -1) + { + color.pixel = pixel; + + XQueryColor (FRAME_X_DISPLAY (f), + FRAME_X_COLORMAP (f), + &color); + + sprintf (css, "scrollbar slider { background-color: #%02x%02x%02x; }", + color.red >> 8, color.green >> 8, color.blue >> 8); + gtk_css_provider_load_from_data (FRAME_X_OUTPUT (f)->scrollbar_foreground_css_provider, + css, -1, NULL); + } + else + gtk_css_provider_load_from_data (FRAME_X_OUTPUT (f)->scrollbar_foreground_css_provider, + "", -1, NULL); + } +#endif } @@ -1779,6 +2013,10 @@ static void x_set_scroll_bar_background (struct frame *f, Lisp_Object value, Lisp_Object oldval) { unsigned long pixel; +#ifdef HAVE_GTK3 + XColor color; + char css[64]; +#endif if (STRINGP (value)) pixel = x_decode_color (f, value, WHITE_PIX_DEFAULT (f)); @@ -1814,11 +2052,33 @@ x_set_scroll_bar_background (struct frame *f, Lisp_Object value, Lisp_Object old update_face_from_frame_parameter (f, Qscroll_bar_background, value); redraw_frame (f); } + +#ifdef HAVE_GTK3 + if (!FRAME_TOOLTIP_P (f)) + { + if (pixel != -1) + { + color.pixel = pixel; + + XQueryColor (FRAME_X_DISPLAY (f), + FRAME_X_COLORMAP (f), + &color); + + sprintf (css, "scrollbar trough { background-color: #%02x%02x%02x; }", + color.red >> 8, color.green >> 8, color.blue >> 8); + gtk_css_provider_load_from_data (FRAME_X_OUTPUT (f)->scrollbar_background_css_provider, + css, -1, NULL); + } + else + gtk_css_provider_load_from_data (FRAME_X_OUTPUT (f)->scrollbar_background_css_provider, + "", -1, NULL); + } +#endif } /* Encode Lisp string STRING as a text in a format appropriate for - XICCC (X Inter Client Communication Conventions). + the ICCCM (Inter Client Communication Conventions Manual). If STRING contains only ASCII characters, do no conversion and return the string data of STRING. Otherwise, encode the text by @@ -2100,6 +2360,104 @@ x_set_scroll_bar_default_height (struct frame *f) #endif } +static void +x_set_alpha (struct frame *f, Lisp_Object arg, Lisp_Object oldval) +{ + double alpha = 1.0; + double newval[2]; + int i; + Lisp_Object item; + bool alpha_identical_p; + + alpha_identical_p = true; + + for (i = 0; i < 2; i++) + { + newval[i] = 1.0; + if (CONSP (arg)) + { + item = CAR (arg); + arg = CDR (arg); + + alpha_identical_p = false; + } + else + item = arg; + + if (NILP (item)) + alpha = - 1.0; + else if (FLOATP (item)) + { + alpha = XFLOAT_DATA (item); + if (! (0 <= alpha && alpha <= 1.0)) + args_out_of_range (make_float (0.0), make_float (1.0)); + } + else if (FIXNUMP (item)) + { + EMACS_INT ialpha = XFIXNUM (item); + if (! (0 <= ialpha && ialpha <= 100)) + args_out_of_range (make_fixnum (0), make_fixnum (100)); + alpha = ialpha / 100.0; + } + else + wrong_type_argument (Qnumberp, item); + newval[i] = alpha; + } + + for (i = 0; i < 2; i++) + f->alpha[i] = newval[i]; + + FRAME_X_OUTPUT (f)->alpha_identical_p = alpha_identical_p; + + if (FRAME_TERMINAL (f)->set_frame_alpha_hook) + { + block_input (); + FRAME_TERMINAL (f)->set_frame_alpha_hook (f); + unblock_input (); + } +} + +static void +x_set_use_frame_synchronization (struct frame *f, Lisp_Object arg, + Lisp_Object oldval) +{ +#if defined HAVE_XSYNC && !defined USE_GTK && defined HAVE_CLOCK_GETTIME + struct x_display_info *dpyinfo; + unsigned long bypass_compositor; + + dpyinfo = FRAME_DISPLAY_INFO (f); + + if (!NILP (arg) && FRAME_X_EXTENDED_COUNTER (f)) + { + FRAME_X_OUTPUT (f)->use_vsync_p + = x_wm_supports (f, dpyinfo->Xatom_net_wm_frame_drawn); + + /* At the same time, write the bypass compositor property to the + outer window. 2 means to never bypass the compositor, as we + need its cooperation for frame synchronization. */ + bypass_compositor = 2; + XChangeProperty (dpyinfo->display, FRAME_OUTER_WINDOW (f), + dpyinfo->Xatom_net_wm_bypass_compositor, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &bypass_compositor, 1); + } + else + { + FRAME_X_OUTPUT (f)->use_vsync_p = false; + + /* Remove the compositor bypass property from the outer + window. */ + XDeleteProperty (dpyinfo->display, FRAME_OUTER_WINDOW (f), + dpyinfo->Xatom_net_wm_bypass_compositor); + } + + store_frame_param (f, Quse_frame_synchronization, + FRAME_X_OUTPUT (f)->use_vsync_p ? Qt : Qnil); +#else + store_frame_param (f, Quse_frame_synchronization, Qnil); +#endif +} + /* Record in frame F the specified or default value according to ALIST of the parameter named PROP (a Lisp symbol). If no value is @@ -2115,8 +2473,9 @@ x_default_scroll_bar_color_parameter (struct frame *f, struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); Lisp_Object tem; - tem = x_get_arg (dpyinfo, alist, prop, xprop, xclass, RES_TYPE_STRING); - if (EQ (tem, Qunbound)) + tem = gui_display_get_arg (dpyinfo, alist, prop, xprop, xclass, + RES_TYPE_STRING); + if (BASE_EQ (tem, Qunbound)) { #ifdef USE_TOOLKIT_SCROLL_BARS @@ -2125,7 +2484,7 @@ x_default_scroll_bar_color_parameter (struct frame *f, AUTO_STRING (foreground, "foreground"); AUTO_STRING (background, "foreground"); AUTO_STRING (verticalScrollBar, "verticalScrollBar"); - tem = (display_x_get_resource + tem = (gui_display_get_resource (dpyinfo, foreground_p ? foreground : background, empty_unibyte_string, verticalScrollBar, @@ -2148,7 +2507,7 @@ x_default_scroll_bar_color_parameter (struct frame *f, } AUTO_FRAME_ARG (arg, prop, tem); - x_set_frame_parameters (f, arg); + gui_set_frame_parameters (f, arg); return tem; } @@ -2223,32 +2582,91 @@ hack_wm_protocols (struct frame *f, Widget widget) } #endif +static void +append_wm_protocols (struct x_display_info *dpyinfo, + struct frame *f) +{ + unsigned char *existing = NULL; + int format = 0; + unsigned long nitems = 0; + Atom type; + Atom *existing_protocols; + Atom protos[10]; + int num_protos = 0; + bool found_wm_ping = false; +#if !defined HAVE_GTK3 && defined HAVE_XSYNC + bool found_wm_sync_request = false; +#endif + unsigned long bytes_after; + + block_input (); + if ((XGetWindowProperty (dpyinfo->display, FRAME_OUTER_WINDOW (f), + dpyinfo->Xatom_wm_protocols, + 0, 100, False, XA_ATOM, &type, &format, &nitems, + &bytes_after, &existing) == Success) + && format == 32 && type == XA_ATOM) + { + existing_protocols = (Atom *) existing; + + while (nitems) + { + nitems--; + + if (existing_protocols[nitems] + == dpyinfo->Xatom_net_wm_ping) + found_wm_ping = true; +#if !defined HAVE_GTK3 && defined HAVE_XSYNC + else if (existing_protocols[nitems] + == dpyinfo->Xatom_net_wm_sync_request) + found_wm_sync_request = true; +#endif + } + } + + if (existing) + XFree (existing); + + if (!found_wm_ping) + protos[num_protos++] = dpyinfo->Xatom_net_wm_ping; +#if !defined HAVE_GTK3 && defined HAVE_XSYNC + if (!found_wm_sync_request && dpyinfo->xsync_supported_p) + protos[num_protos++] = dpyinfo->Xatom_net_wm_sync_request; +#endif + + if (num_protos) + XChangeProperty (dpyinfo->display, + FRAME_OUTER_WINDOW (f), + dpyinfo->Xatom_wm_protocols, + XA_ATOM, 32, PropModeAppend, + (unsigned char *) protos, + num_protos); + unblock_input (); +} + /* Support routines for XIC (X Input Context). */ #ifdef HAVE_X_I18N -static XFontSet xic_create_xfontset (struct frame *); -static XIMStyle best_xim_style (XIMStyles *); +static void xic_preedit_draw_callback (XIC, XPointer, XIMPreeditDrawCallbackStruct *); +static void xic_preedit_caret_callback (XIC, XPointer, XIMPreeditCaretCallbackStruct *); +static void xic_preedit_done_callback (XIC, XPointer, XPointer); +static int xic_preedit_start_callback (XIC, XPointer, XPointer); +#ifndef HAVE_XICCALLBACK_CALLBACK +#define XICCallback XIMCallback +#define XICProc XIMProc +#endif -/* Supported XIM styles, ordered by preference. */ - -static const XIMStyle supported_xim_styles[] = -{ - XIMPreeditPosition | XIMStatusArea, - XIMPreeditPosition | XIMStatusNothing, - XIMPreeditPosition | XIMStatusNone, - XIMPreeditNothing | XIMStatusArea, - XIMPreeditNothing | XIMStatusNothing, - XIMPreeditNothing | XIMStatusNone, - XIMPreeditNone | XIMStatusArea, - XIMPreeditNone | XIMStatusNothing, - XIMPreeditNone | XIMStatusNone, - 0, -}; - +static XIMCallback Xxic_preedit_draw_callback = { NULL, + (XIMProc) xic_preedit_draw_callback }; +static XIMCallback Xxic_preedit_caret_callback = { NULL, + (XIMProc) xic_preedit_caret_callback }; +static XIMCallback Xxic_preedit_done_callback = { NULL, + (XIMProc) xic_preedit_done_callback }; +static XICCallback Xxic_preedit_start_callback = { NULL, + (XICProc) xic_preedit_start_callback }; #if defined HAVE_X_WINDOWS && defined USE_X_TOOLKIT /* Create an X fontset on frame F with base font name BASE_FONTNAME. */ @@ -2525,17 +2943,31 @@ xic_free_xfontset (struct frame *f) FRAME_XIC_FONTSET (f) = NULL; } +/* Create XIC for frame F. */ + +static const XIMStyle supported_xim_styles[] = + { + STYLE_NONE, + STYLE_CALLBACK, + STYLE_OVERTHESPOT, + STYLE_OFFTHESPOT, + STYLE_ROOT + }; /* Value is the best input style, given user preferences USER (already checked to be supported by Emacs), and styles supported by the input method XIM. */ static XIMStyle -best_xim_style (XIMStyles *xim) +best_xim_style (struct x_display_info *dpyinfo, + XIMStyles *xim) { int i, j; int nr_supported = ARRAYELTS (supported_xim_styles); + if (dpyinfo->preferred_xim_style) + return dpyinfo->preferred_xim_style; + for (i = 0; i < nr_supported; ++i) for (j = 0; j < xim->count_styles; ++j) if (supported_xim_styles[i] == xim->supported_styles[j]) @@ -2563,11 +2995,12 @@ create_frame_xic (struct frame *f) goto out; xim = FRAME_X_XIM (f); - if (!xim) + if (!xim || ! FRAME_X_XIM_STYLES(f)) goto out; /* Determine XIC style. */ - xic_style = best_xim_style (FRAME_X_XIM_STYLES (f)); + xic_style = best_xim_style (FRAME_DISPLAY_INFO (f), + FRAME_X_XIM_STYLES (f)); /* Create X fontset. */ if (xic_style & (XIMPreeditPosition | XIMStatusArea)) @@ -2616,6 +3049,22 @@ create_frame_xic (struct frame *f) goto out; } + if (xic_style & XIMPreeditCallbacks) + { + spot.x = 0; + spot.y = 0; + preedit_attr = XVaCreateNestedList (0, + XNSpotLocation, &spot, + XNPreeditStartCallback, &Xxic_preedit_start_callback, + XNPreeditDoneCallback, &Xxic_preedit_done_callback, + XNPreeditDrawCallback, &Xxic_preedit_draw_callback, + XNPreeditCaretCallback, &Xxic_preedit_caret_callback, + NULL); + + if (!preedit_attr) + goto out; + } + if (preedit_attr && status_attr) xic = XCreateIC (xim, XNInputStyle, xic_style, @@ -2686,15 +3135,49 @@ free_frame_xic (struct frame *f) void xic_set_preeditarea (struct window *w, int x, int y) { - struct frame *f = XFRAME (w->frame); + struct frame *f = WINDOW_XFRAME (w); XVaNestedList attr; XPoint spot; - spot.x = WINDOW_TO_FRAME_PIXEL_X (w, x) + WINDOW_LEFT_FRINGE_WIDTH (w); - spot.y = WINDOW_TO_FRAME_PIXEL_Y (w, y) + FONT_BASE (FRAME_FONT (f)); - attr = XVaCreateNestedList (0, XNSpotLocation, &spot, NULL); - XSetICValues (FRAME_XIC (f), XNPreeditAttributes, attr, NULL); - XFree (attr); + if (FRAME_XIC (f)) + { + spot.x = (WINDOW_TO_FRAME_PIXEL_X (w, x) + + WINDOW_LEFT_FRINGE_WIDTH (w) + + WINDOW_LEFT_MARGIN_WIDTH (w)); + spot.y = (WINDOW_TO_FRAME_PIXEL_Y (w, y) + + w->phys_cursor_height); + + if (FRAME_XIC_STYLE (f) & XIMPreeditCallbacks) + attr = XVaCreateNestedList (0, XNSpotLocation, &spot, + XNPreeditStartCallback, &Xxic_preedit_start_callback, + XNPreeditDoneCallback, &Xxic_preedit_done_callback, + XNPreeditDrawCallback, &Xxic_preedit_draw_callback, + XNPreeditCaretCallback, &Xxic_preedit_caret_callback, + NULL); + else + attr = XVaCreateNestedList (0, XNSpotLocation, &spot, NULL); + XSetICValues (FRAME_XIC (f), XNPreeditAttributes, attr, NULL); + XFree (attr); + } +#ifdef USE_GTK + if (f->tooltip) + return; + + GdkRectangle rect; + int scale = xg_get_scale (f); + + rect.x = (WINDOW_TO_FRAME_PIXEL_X (w, x) + + WINDOW_LEFT_FRINGE_WIDTH (w) + + WINDOW_LEFT_MARGIN_WIDTH (w)) / scale; + rect.y = (WINDOW_TO_FRAME_PIXEL_Y (w, y) + + FRAME_TOOLBAR_HEIGHT (f) + + FRAME_MENUBAR_HEIGHT (f)) / scale; + rect.width = w->phys_cursor_width / scale; + rect.height = w->phys_cursor_height / scale; + + gtk_im_context_set_cursor_location (FRAME_X_OUTPUT (f)->im_context, + &rect); +#endif } @@ -2740,9 +3223,391 @@ xic_set_statusarea (struct frame *f) XFree (attr); } +static struct frame * +x_xic_to_frame (XIC xic) +{ + Lisp_Object tail, tem; + struct frame *f; + + FOR_EACH_FRAME (tail, tem) + { + f = XFRAME (tem); + + if (FRAME_X_P (f) && FRAME_XIC (f) == xic) + return f; + } + + return NULL; +} + +static int +xic_preedit_start_callback (XIC xic, XPointer client_data, + XPointer call_data) +{ + struct frame *f = x_xic_to_frame (xic); + struct x_output *output; + + if (f) + { + output = FRAME_X_OUTPUT (f); + + output->preedit_size = 0; + output->preedit_active = true; + output->preedit_caret = 0; + + if (output->preedit_chars) + xfree (output->preedit_chars); + + output->preedit_chars = NULL; + } + + return -1; +} + +static void +xic_preedit_caret_callback (XIC xic, XPointer client_data, + XIMPreeditCaretCallbackStruct *call_data) +{ + struct frame *f = x_xic_to_frame (xic); + struct x_output *output; + struct input_event ie; + EVENT_INIT (ie); + + if (f) + { + output = FRAME_X_OUTPUT (f); + + if (!output->preedit_active) + return; + + switch (call_data->direction) + { + case XIMAbsolutePosition: + output->preedit_caret = call_data->position; + break; + case XIMForwardChar: + case XIMForwardWord: + call_data->position = output->preedit_caret++; + break; + case XIMBackwardChar: + case XIMBackwardWord: + call_data->position = max (0, output->preedit_caret--); + break; + default: + call_data->position = output->preedit_caret; + } + + if (output->preedit_chars) + { + ie.kind = PREEDIT_TEXT_EVENT; + XSETFRAME (ie.frame_or_window, f); + ie.arg = make_string_from_utf8 (output->preedit_chars, + output->preedit_size); + + if (SCHARS (ie.arg)) + Fput_text_property (make_fixnum (min (SCHARS (ie.arg) - 1, + max (0, output->preedit_caret))), + make_fixnum (max (SCHARS (ie.arg), + max (0, output->preedit_caret) + 1)), + Qcursor, Qt, ie.arg); + + XSETINT (ie.x, 0); + XSETINT (ie.y, 0); + + kbd_buffer_store_event (&ie); + } + } +} + + +static void +xic_preedit_done_callback (XIC xic, XPointer client_data, + XPointer call_data) +{ + struct frame *f = x_xic_to_frame (xic); + struct x_output *output; + struct input_event ie; + EVENT_INIT (ie); + + if (f) + { + ie.kind = PREEDIT_TEXT_EVENT; + ie.arg = Qnil; + XSETFRAME (ie.frame_or_window, f); + XSETINT (ie.x, 0); + XSETINT (ie.y, 0); + kbd_buffer_store_event (&ie); + + output = FRAME_X_OUTPUT (f); + + if (output->preedit_chars) + xfree (output->preedit_chars); + + output->preedit_size = 0; + output->preedit_active = false; + output->preedit_chars = NULL; + output->preedit_caret = 0; + } +} + +struct x_xim_text_conversion_data +{ + struct coding_system *coding; + char *source; +}; + +static Lisp_Object +x_xim_text_to_utf8_unix_1 (ptrdiff_t nargs, + Lisp_Object *args) +{ + struct x_xim_text_conversion_data *data; + ptrdiff_t nbytes; + + data = xmint_pointer (args[0]); + nbytes = strlen (data->source); + + data->coding->destination = NULL; + + setup_coding_system (Vlocale_coding_system, + data->coding); + data->coding->mode |= (CODING_MODE_LAST_BLOCK + | CODING_MODE_SAFE_ENCODING); + data->coding->source = (const unsigned char *) data->source; + data->coding->dst_bytes = 2048; + data->coding->destination = xmalloc (2048); + decode_coding_object (data->coding, Qnil, 0, 0, + nbytes, nbytes, Qnil); + + return Qnil; +} + +static Lisp_Object +x_xim_text_to_utf8_unix_2 (Lisp_Object val, + ptrdiff_t nargs, + Lisp_Object *args) +{ + struct x_xim_text_conversion_data *data; + + data = xmint_pointer (args[0]); + + if (data->coding->destination) + xfree (data->coding->destination); + + data->coding->destination = NULL; + + return Qnil; +} + +/* The string returned is not null-terminated. */ +static char * +x_xim_text_to_utf8_unix (XIMText *text, ptrdiff_t *length) +{ + unsigned char *wchar_buf; + ptrdiff_t wchar_actual_length, i; + struct coding_system coding; + struct x_xim_text_conversion_data data; + bool was_waiting_for_input_p; + Lisp_Object arg; + + if (text->encoding_is_wchar) + { + wchar_buf = xmalloc ((text->length + 1) * MAX_MULTIBYTE_LENGTH); + wchar_actual_length = 0; + + for (i = 0; i < text->length; ++i) + wchar_actual_length += CHAR_STRING (text->string.wide_char[i], + wchar_buf + wchar_actual_length); + *length = wchar_actual_length; + + return (char *) wchar_buf; + } + + data.coding = &coding; + data.source = text->string.multi_byte; + + was_waiting_for_input_p = waiting_for_input; + /* Otherwise Fsignal will crash. */ + waiting_for_input = false; + arg = make_mint_ptr (&data); + internal_condition_case_n (x_xim_text_to_utf8_unix_1, 1, &arg, + Qt, x_xim_text_to_utf8_unix_2); + waiting_for_input = was_waiting_for_input_p; + + *length = coding.produced; + return (char *) coding.destination; +} + +static void +xic_preedit_draw_callback (XIC xic, XPointer client_data, + XIMPreeditDrawCallbackStruct *call_data) +{ + struct frame *f = x_xic_to_frame (xic); + struct x_output *output; + ptrdiff_t text_length = 0; + ptrdiff_t charpos; + ptrdiff_t original_size; + char *text; + char *chg_start, *chg_end; + struct input_event ie; + EVENT_INIT (ie); + + if (f) + { + output = FRAME_X_OUTPUT (f); + + if (!output->preedit_active) + return; + + if (call_data->text) + { + text = x_xim_text_to_utf8_unix (call_data->text, &text_length); + + if (!text) + /* Decoding the IM text failed. */ + goto im_abort; + } + else + text = NULL; + + original_size = output->preedit_size; -/* Set X fontset for XIC of frame F, using base font name - BASE_FONTNAME. Called when a new Emacs fontset is chosen. */ + /* This is an ordinary insertion: reallocate the buffer to hold + enough for TEXT. */ + if (!call_data->chg_length) + { + if (!text) + goto im_abort; + + if (output->preedit_chars) + output->preedit_chars = xrealloc (output->preedit_chars, + output->preedit_size += text_length); + else + output->preedit_chars = xmalloc (output->preedit_size += text_length); + } + + chg_start = output->preedit_chars; + + /* The IM sent bad data: the buffer is empty, but the change + position is more than 0. */ + if (!output->preedit_chars && call_data->chg_first) + goto im_abort; + + /* Find the byte position for the character position where the + first change is to be made. */ + if (call_data->chg_first) + { + charpos = 0; + + while (charpos < call_data->chg_first) + { + chg_start += BYTES_BY_CHAR_HEAD (*chg_start); + + if ((chg_start - output->preedit_chars) > output->preedit_size) + /* The IM sent bad data: chg_start is larger than the + current buffer. */ + goto im_abort; + ++charpos; + } + } + + if (!call_data->chg_length) + { + if (!text) + goto im_abort; + + memmove (chg_start + text_length, chg_start, + original_size - (chg_start - output->preedit_chars)); + memcpy (chg_start, text, text_length); + } + else + { + if (call_data->chg_length < 1) + goto im_abort; + + charpos = 0; + chg_end = chg_start; + + while (charpos < call_data->chg_length) + { + chg_end += BYTES_BY_CHAR_HEAD (*chg_end); + + if ((chg_end - output->preedit_chars) > output->preedit_size) + /* The IM sent bad data: chg_end ends someplace outside + the current buffer. */ + goto im_abort; + ++charpos; + } + + memmove (chg_start, chg_end, ((output->preedit_chars + + output->preedit_size) - chg_end)); + output->preedit_size -= (chg_end - chg_start); + + if (text) + { + original_size = output->preedit_size; + output->preedit_chars = xrealloc (output->preedit_chars, + output->preedit_size += text_length); + + /* Find chg_start again, since preedit_chars was reallocated. */ + + chg_start = output->preedit_chars; + charpos = 0; + + while (charpos < call_data->chg_first) + { + chg_start += BYTES_BY_CHAR_HEAD (*chg_start); + + if ((chg_start - output->preedit_chars) > output->preedit_size) + /* The IM sent bad data: chg_start is larger than the + current buffer. */ + goto im_abort; + ++charpos; + } + + memmove (chg_start + text_length, chg_start, + original_size - (chg_start - output->preedit_chars)); + memcpy (chg_start, text, text_length); + } + } + + if (text) + xfree (text); + + output->preedit_caret = call_data->caret; + + /* This is okay because this callback is called from the big XIM + event filter, which runs inside XTread_socket. */ + + ie.kind = PREEDIT_TEXT_EVENT; + XSETFRAME (ie.frame_or_window, f); + ie.arg = make_string_from_utf8 (output->preedit_chars, + output->preedit_size); + + if (SCHARS (ie.arg)) + Fput_text_property (make_fixnum (min (SCHARS (ie.arg) - 1, + max (0, output->preedit_caret))), + make_fixnum (min (SCHARS (ie.arg), + max (0, output->preedit_caret) + 1)), + Qcursor, Qt, ie.arg); + + XSETINT (ie.x, 0); + XSETINT (ie.y, 0); + + kbd_buffer_store_event (&ie); + } + + return; + + im_abort: + if (text) + xfree (text); + if (output->preedit_chars) + xfree (output->preedit_chars); + output->preedit_chars = NULL; + output->preedit_size = 0; + output->preedit_active = false; + output->preedit_caret = 0; +} void xic_set_xfontset (struct frame *f, const char *base_fontname) @@ -2772,17 +3637,34 @@ xic_set_xfontset (struct frame *f, const char *base_fontname) void x_mark_frame_dirty (struct frame *f) { - if (FRAME_X_DOUBLE_BUFFERED_P (f) && !FRAME_X_NEED_BUFFER_FLIP (f)) +#ifdef HAVE_XDBE + if (FRAME_X_DOUBLE_BUFFERED_P (f) + && !FRAME_X_NEED_BUFFER_FLIP (f)) FRAME_X_NEED_BUFFER_FLIP (f) = true; +#endif } static void set_up_x_back_buffer (struct frame *f) { +#ifdef HAVE_XRENDER + block_input (); + if (FRAME_X_PICTURE (f) != None) + { + XRenderFreePicture (FRAME_X_DISPLAY (f), + FRAME_X_PICTURE (f)); + FRAME_X_PICTURE (f) = None; + } + unblock_input (); +#endif + #ifdef HAVE_XDBE block_input (); if (FRAME_X_WINDOW (f) && !FRAME_X_DOUBLE_BUFFERED_P (f)) { +#ifdef USE_CAIRO + x_cr_destroy_frame_context (f); +#endif FRAME_X_RAW_DRAWABLE (f) = FRAME_X_WINDOW (f); if (FRAME_DISPLAY_INFO (f)->supports_xdbe) { @@ -2790,10 +3672,10 @@ set_up_x_back_buffer (struct frame *f) server ran out of memory or we don't have the right kind of visual, just use single-buffered rendering. */ x_catch_errors (FRAME_X_DISPLAY (f)); - FRAME_X_RAW_DRAWABLE (f) = XdbeAllocateBackBufferName ( - FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), - XdbeCopied); + FRAME_X_RAW_DRAWABLE (f) + = XdbeAllocateBackBufferName (FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + XdbeCopied); if (x_had_errors_p (FRAME_X_DISPLAY (f))) FRAME_X_RAW_DRAWABLE (f) = FRAME_X_WINDOW (f); x_uncatch_errors_after_check (); @@ -2806,12 +3688,26 @@ set_up_x_back_buffer (struct frame *f) void tear_down_x_back_buffer (struct frame *f) { +#ifdef HAVE_XRENDER + block_input (); + if (FRAME_X_PICTURE (f) != None) + { + XRenderFreePicture (FRAME_X_DISPLAY (f), + FRAME_X_PICTURE (f)); + FRAME_X_PICTURE (f) = None; + } + unblock_input (); +#endif + #ifdef HAVE_XDBE block_input (); if (FRAME_X_WINDOW (f) && FRAME_X_DOUBLE_BUFFERED_P (f)) { if (FRAME_X_DOUBLE_BUFFERED_P (f)) { +#ifdef USE_CAIRO + x_cr_destroy_frame_context (f); +#endif XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f)); FRAME_X_RAW_DRAWABLE (f) = FRAME_X_WINDOW (f); @@ -2826,13 +3722,107 @@ tear_down_x_back_buffer (struct frame *f) void initial_set_up_x_back_buffer (struct frame *f) { - block_input (); eassert (FRAME_X_WINDOW (f)); FRAME_X_RAW_DRAWABLE (f) = FRAME_X_WINDOW (f); - if (NILP (CDR (Fassq (Qinhibit_double_buffering, f->param_alist)))) + + if (NILP (CDR (Fassq (Qinhibit_double_buffering, + f->param_alist)))) set_up_x_back_buffer (f); +} + +#if defined HAVE_XINPUT2 +static void +setup_xi_event_mask (struct frame *f) +{ + XIEventMask mask; + ptrdiff_t l = XIMaskLen (XI_LASTEVENT); + unsigned char *m; +#ifndef HAVE_XINPUT2_1 + /* Set up fallback values, since XIGetSelectedEvents doesn't work + with this version of libXi. */ + XIEventMask *selected; + + selected = xzalloc (sizeof *selected + l); + selected->mask = ((unsigned char *) selected) + sizeof *selected; + selected->mask_len = l; + selected->deviceid = XIAllMasterDevices; +#endif + + mask.mask = m = alloca (l); + memset (m, 0, l); + mask.mask_len = l; + + block_input (); +#ifndef HAVE_GTK3 + mask.deviceid = XIAllMasterDevices; + + XISetMask (m, XI_ButtonPress); + XISetMask (m, XI_ButtonRelease); + XISetMask (m, XI_Motion); + XISetMask (m, XI_Enter); + XISetMask (m, XI_Leave); +#ifndef USE_GTK + XISetMask (m, XI_FocusIn); + XISetMask (m, XI_FocusOut); + XISetMask (m, XI_KeyPress); + XISetMask (m, XI_KeyRelease); +#endif + XISelectEvents (FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + &mask, 1); + + /* Fortunately `xi_masks' isn't used on GTK 3, where we really have + to get the event mask from the X server. */ +#ifndef HAVE_XINPUT2_1 + memcpy (selected->mask, m, l); +#endif + + memset (m, 0, l); +#endif /* !HAVE_GTK3 */ + +#ifdef USE_X_TOOLKIT + XISetMask (m, XI_KeyPress); + XISetMask (m, XI_KeyRelease); + XISetMask (m, XI_FocusIn); + XISetMask (m, XI_FocusOut); + + XISelectEvents (FRAME_X_DISPLAY (f), + FRAME_OUTER_WINDOW (f), + &mask, 1); + memset (m, 0, l); +#endif + +#ifdef HAVE_XINPUT2_2 + if (FRAME_DISPLAY_INFO (f)->xi2_version >= 2) + { + mask.deviceid = XIAllDevices; + + XISetMask (m, XI_TouchBegin); + XISetMask (m, XI_TouchUpdate); + XISetMask (m, XI_TouchEnd); +#ifdef HAVE_XINPUT2_4 + if (FRAME_DISPLAY_INFO (f)->xi2_version >= 4) + { + XISetMask (m, XI_GesturePinchBegin); + XISetMask (m, XI_GesturePinchUpdate); + XISetMask (m, XI_GesturePinchEnd); + } +#endif + + XISelectEvents (FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + &mask, 1); + } +#endif + +#ifndef HAVE_XINPUT2_1 + FRAME_X_OUTPUT (f)->xi_masks = selected; + FRAME_X_OUTPUT (f)->num_xi_masks = 1; +#endif + unblock_input (); } +#endif #ifdef USE_X_TOOLKIT @@ -2984,10 +3974,6 @@ x_window (struct frame *f, long window_prompting) XtManageChild (pane_widget); XtRealizeWidget (shell_widget); - if (FRAME_X_EMBEDDED_P (f)) - XReparentWindow (FRAME_X_DISPLAY (f), XtWindow (shell_widget), - f->output_data.x->parent_desc, 0, 0); - FRAME_X_WINDOW (f) = XtWindow (frame_widget); initial_set_up_x_back_buffer (f); validate_x_resource_name (); @@ -3008,6 +3994,7 @@ x_window (struct frame *f, long window_prompting) &f->output_data.x->wm_hints); hack_wm_protocols (f, shell_widget); + append_wm_protocols (FRAME_DISPLAY_INFO (f), f); #ifdef X_TOOLKIT_EDITRES XtAddEventHandler (shell_widget, 0, True, _XEditResCheckMessages, 0); @@ -3059,7 +4046,7 @@ x_window (struct frame *f, long window_prompting) { Display *dpy = FRAME_X_DISPLAY (f); PropMotifWmHints hints; - Atom prop = XInternAtom (dpy, "_MOTIF_WM_HINTS", False); + Atom prop = FRAME_DISPLAY_INFO (f)->Xatom_MOTIF_WM_HINTS; memset (&hints, 0, sizeof(hints)); hints.flags = MWM_HINTS_DECORATIONS; @@ -3083,6 +4070,11 @@ x_window (struct frame *f, long window_prompting) /* This is a no-op, except under Motif. Make sure main areas are set to something reasonable, in case we get an error later. */ lw_set_main_areas (pane_widget, 0, frame_widget); + +#ifdef HAVE_XINPUT2 + if (FRAME_DISPLAY_INFO (f)->supports_xi2) + setup_xi_event_mask (f); +#endif } #else /* not USE_X_TOOLKIT */ @@ -3122,6 +4114,13 @@ x_window (struct frame *f) unblock_input (); } #endif + + append_wm_protocols (FRAME_DISPLAY_INFO (f), f); + +#ifdef HAVE_XINPUT2 + if (FRAME_DISPLAY_INFO (f)->supports_xi2) + setup_xi_event_mask (f); +#endif } #else /*! USE_GTK */ @@ -3148,12 +4147,12 @@ x_window (struct frame *f) block_input (); FRAME_X_WINDOW (f) = XCreateWindow (FRAME_X_DISPLAY (f), - f->output_data.x->parent_desc, + FRAME_DISPLAY_INFO (f)->root_window, f->left_pos, f->top_pos, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), f->border_width, - CopyFromParent, /* depth */ + FRAME_DISPLAY_INFO (f)->n_planes, /* depth */ InputOutput, /* class */ FRAME_X_VISUAL (f), attribute_mask, &attributes); @@ -3176,6 +4175,11 @@ x_window (struct frame *f) } #endif /* HAVE_X_I18N */ +#ifdef HAVE_XINPUT2 + if (FRAME_DISPLAY_INFO (f)->supports_xi2) + setup_xi_event_mask (f); +#endif + validate_x_resource_name (); class_hints.res_name = SSDATA (Vx_resource_name); @@ -3201,6 +4205,8 @@ x_window (struct frame *f) XSetWMProtocols (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), protocols, 2); } + append_wm_protocols (FRAME_DISPLAY_INFO (f), f); + /* x_set_name normally ignores requests to set the name if the requested name is the same as the current name. This is the one place where that assumption isn't correct; f->name is set, but @@ -3219,7 +4225,7 @@ x_window (struct frame *f) { Display *dpy = FRAME_X_DISPLAY (f); PropMotifWmHints hints; - Atom prop = XInternAtom (dpy, "_MOTIF_WM_HINTS", False); + Atom prop = FRAME_DISPLAY_INFO (f)->Xatom_MOTIF_WM_HINTS; memset (&hints, 0, sizeof(hints)); hints.flags = MWM_HINTS_DECORATIONS; @@ -3257,14 +4263,14 @@ x_icon_verify (struct frame *f, Lisp_Object parms) /* Set the position of the icon. Note that twm groups all icons in an icon window. */ - icon_x = x_frame_get_and_record_arg (f, parms, Qicon_left, 0, 0, RES_TYPE_NUMBER); - icon_y = x_frame_get_and_record_arg (f, parms, Qicon_top, 0, 0, RES_TYPE_NUMBER); - if (!EQ (icon_x, Qunbound) && !EQ (icon_y, Qunbound)) + icon_x = gui_frame_get_and_record_arg (f, parms, Qicon_left, 0, 0, RES_TYPE_NUMBER); + icon_y = gui_frame_get_and_record_arg (f, parms, Qicon_top, 0, 0, RES_TYPE_NUMBER); + if (!BASE_EQ (icon_x, Qunbound) && !BASE_EQ (icon_y, Qunbound)) { - CHECK_NUMBER (icon_x); - CHECK_NUMBER (icon_y); + CHECK_FIXNUM (icon_x); + CHECK_FIXNUM (icon_y); } - else if (!EQ (icon_x, Qunbound) || !EQ (icon_y, Qunbound)) + else if (!BASE_EQ (icon_x, Qunbound) || !BASE_EQ (icon_y, Qunbound)) error ("Both left and top icon corners of icon must be specified"); } @@ -3278,28 +4284,33 @@ x_icon (struct frame *f, Lisp_Object parms) /* Set the position of the icon. Note that twm groups all icons in an icon window. */ Lisp_Object icon_x - = x_frame_get_and_record_arg (f, parms, Qicon_left, 0, 0, RES_TYPE_NUMBER); + = gui_frame_get_and_record_arg (f, parms, Qicon_left, 0, 0, RES_TYPE_NUMBER); Lisp_Object icon_y - = x_frame_get_and_record_arg (f, parms, Qicon_top, 0, 0, RES_TYPE_NUMBER); - if (!EQ (icon_x, Qunbound) && !EQ (icon_y, Qunbound)) + = gui_frame_get_and_record_arg (f, parms, Qicon_top, 0, 0, RES_TYPE_NUMBER); + int icon_xval, icon_yval; + + bool xgiven = !BASE_EQ (icon_x, Qunbound); + bool ygiven = !BASE_EQ (icon_y, Qunbound); + if (xgiven != ygiven) + error ("Both left and top icon corners of icon must be specified"); + if (xgiven) { - CHECK_TYPE_RANGED_INTEGER (int, icon_x); - CHECK_TYPE_RANGED_INTEGER (int, icon_y); + icon_xval = check_integer_range (icon_x, INT_MIN, INT_MAX); + icon_yval = check_integer_range (icon_y, INT_MIN, INT_MAX); } - else if (!EQ (icon_x, Qunbound) || !EQ (icon_y, Qunbound)) - error ("Both left and top icon corners of icon must be specified"); block_input (); - if (! EQ (icon_x, Qunbound)) - x_wm_set_icon_position (f, XINT (icon_x), XINT (icon_y)); + if (xgiven) + x_wm_set_icon_position (f, icon_xval, icon_yval); -#if false /* x_get_arg removes the visibility parameter as a side effect, - but x_create_frame still needs it. */ +#if false /* gui_display_get_arg removes the visibility parameter as a + side effect, but x_create_frame still needs it. */ /* Start up iconic or window? */ struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); x_wm_set_window_state - (f, (EQ (x_get_arg (dpyinfo, parms, Qvisibility, 0, 0, RES_TYPE_SYMBOL), + (f, (EQ (gui_display_get_arg (dpyinfo, parms, Qvisibility, 0, 0, + RES_TYPE_SYMBOL), Qicon) ? IconicState : NormalState)); @@ -3328,7 +4339,7 @@ x_make_gc (struct frame *f) gc_values.foreground = FRAME_FOREGROUND_PIXEL (f); gc_values.background = FRAME_BACKGROUND_PIXEL (f); - gc_values.line_width = 0; /* Means 1 using fast algorithm. */ + gc_values.line_width = 1; f->output_data.x->normal_gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), @@ -3347,11 +4358,9 @@ x_make_gc (struct frame *f) /* Cursor has cursor-color background, background-color foreground. */ gc_values.foreground = FRAME_BACKGROUND_PIXEL (f); gc_values.background = f->output_data.x->cursor_pixel; - gc_values.fill_style = FillOpaqueStippled; f->output_data.x->cursor_gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), - (GCForeground | GCBackground - | GCFillStyle | GCLineWidth), + (GCForeground | GCBackground | GCLineWidth), &gc_values); /* Create the gray border tile used when the pointer is not in @@ -3407,7 +4416,7 @@ x_free_gcs (struct frame *f) /* Handler for signals raised during x_create_frame and - x_create_tip_frame. FRAME is the frame which is partially + Fx_create_tip_frame. FRAME is the frame which is partially constructed. */ static Lisp_Object @@ -3417,7 +4426,7 @@ unwind_create_frame (Lisp_Object frame) /* If frame is already dead, nothing to do. This can happen if the display is disconnected after the frame has become official, but - before x_create_frame removes the unwind protect. */ + before Fx_create_frame removes the unwind protect. */ if (!FRAME_LIVE_P (f)) return Qnil; @@ -3459,14 +4468,14 @@ do_unwind_create_frame (Lisp_Object frame) unwind_create_frame (frame); } -static void +void x_default_font_parameter (struct frame *f, Lisp_Object parms) { struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); - Lisp_Object font_param = x_get_arg (dpyinfo, parms, Qfont, NULL, NULL, - RES_TYPE_STRING); + Lisp_Object font_param = gui_display_get_arg (dpyinfo, parms, Qfont, NULL, NULL, + RES_TYPE_STRING); Lisp_Object font = Qnil; - if (EQ (font_param, Qunbound)) + if (BASE_EQ (font_param, Qunbound)) font_param = Qnil; if (NILP (font_param)) @@ -3481,13 +4490,14 @@ x_default_font_parameter (struct frame *f, Lisp_Object parms) if (NILP (font)) font = !NILP (font_param) ? font_param - : x_get_arg (dpyinfo, parms, Qfont, "font", "Font", RES_TYPE_STRING); + : gui_display_get_arg (dpyinfo, parms, Qfont, "font", "Font", + RES_TYPE_STRING); if (! FONTP (font) && ! STRINGP (font)) { const char *names[] = { -#ifdef HAVE_XFT +#if defined USE_CAIRO || defined HAVE_XFT /* This will find the normal Xft font. */ "monospace-10", #endif @@ -3518,11 +4528,11 @@ x_default_font_parameter (struct frame *f, Lisp_Object parms) /* Remember the explicit font parameter, so we can re-apply it after we've applied the `default' face settings. */ AUTO_FRAME_ARG (arg, Qfont_parameter, font_param); - x_set_frame_parameters (f, arg); + gui_set_frame_parameters (f, arg); } /* This call will make X resources override any system font setting. */ - x_default_parameter (f, parms, Qfont, font, "font", "Font", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qfont, font, "font", "Font", RES_TYPE_STRING); } @@ -3553,9 +4563,7 @@ set_machine_and_pid_properties (struct frame *f) unsigned long xpid = pid; XChangeProperty (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), - XInternAtom (FRAME_X_DISPLAY (f), - "_NET_WM_PID", - False), + FRAME_DISPLAY_INFO (f)->Xatom_net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &xpid, 1); } @@ -3579,12 +4587,14 @@ This function is an internal primitive--use `make-frame' instead. */) bool minibuffer_only = false; bool undecorated = false, override_redirect = false; long window_prompting = 0; - ptrdiff_t count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); Lisp_Object display; struct x_display_info *dpyinfo = NULL; Lisp_Object parent, parent_frame; struct kboard *kb; - int x_width = 0, x_height = 0; +#ifdef HAVE_GTK3 + GdkWindow *gwin; +#endif parms = Fcopy_alist (parms); @@ -3592,10 +4602,12 @@ This function is an internal primitive--use `make-frame' instead. */) until we know if this frame has a specified name. */ Vx_resource_name = Vinvocation_name; - display = x_get_arg (dpyinfo, parms, Qterminal, 0, 0, RES_TYPE_NUMBER); - if (EQ (display, Qunbound)) - display = x_get_arg (dpyinfo, parms, Qdisplay, 0, 0, RES_TYPE_STRING); - if (EQ (display, Qunbound)) + display = gui_display_get_arg (dpyinfo, parms, Qterminal, 0, 0, + RES_TYPE_NUMBER); + if (BASE_EQ (display, Qunbound)) + display = gui_display_get_arg (dpyinfo, parms, Qdisplay, 0, 0, + RES_TYPE_STRING); + if (BASE_EQ (display, Qunbound)) display = Qnil; dpyinfo = check_x_display_info (display); kb = dpyinfo->terminal->kboard; @@ -3603,9 +4615,10 @@ This function is an internal primitive--use `make-frame' instead. */) if (!dpyinfo->terminal->name) error ("Terminal is not live, can't create new frames on it"); - name = x_get_arg (dpyinfo, parms, Qname, "name", "Name", RES_TYPE_STRING); + name = gui_display_get_arg (dpyinfo, parms, Qname, "name", "Name", + RES_TYPE_STRING); if (!STRINGP (name) - && ! EQ (name, Qunbound) + && ! BASE_EQ (name, Qunbound) && ! NILP (name)) error ("Invalid frame name--not a string or nil"); @@ -3613,15 +4626,17 @@ This function is an internal primitive--use `make-frame' instead. */) Vx_resource_name = name; /* See if parent window is specified. */ - parent = x_get_arg (dpyinfo, parms, Qparent_id, NULL, NULL, RES_TYPE_NUMBER); - if (EQ (parent, Qunbound)) + parent = gui_display_get_arg (dpyinfo, parms, Qparent_id, NULL, NULL, + RES_TYPE_NUMBER); + if (BASE_EQ (parent, Qunbound)) parent = Qnil; if (! NILP (parent)) - CHECK_NUMBER (parent); + CHECK_FIXNUM (parent); frame = Qnil; - tem = x_get_arg (dpyinfo, parms, Qminibuffer, "minibuffer", "Minibuffer", - RES_TYPE_SYMBOL); + tem = gui_display_get_arg (dpyinfo, + parms, Qminibuffer, "minibuffer", "Minibuffer", + RES_TYPE_SYMBOL); if (EQ (tem, Qnone) || NILP (tem)) f = make_frame_without_minibuffer (Qnil, kb, display); else if (EQ (tem, Qonly)) @@ -3634,11 +4649,15 @@ This function is an internal primitive--use `make-frame' instead. */) else f = make_frame (true); - parent_frame = x_get_arg (dpyinfo, parms, Qparent_frame, NULL, NULL, - RES_TYPE_SYMBOL); + parent_frame = gui_display_get_arg (dpyinfo, + parms, + Qparent_frame, + NULL, + NULL, + RES_TYPE_SYMBOL); /* Accept parent-frame iff parent-id was not specified. */ if (!NILP (parent) - || EQ (parent_frame, Qunbound) + || BASE_EQ (parent_frame, Qunbound) || NILP (parent_frame) || !FRAMEP (parent_frame) || !FRAME_LIVE_P (XFRAME (parent_frame)) @@ -3648,17 +4667,25 @@ This function is an internal primitive--use `make-frame' instead. */) fset_parent_frame (f, parent_frame); store_frame_param (f, Qparent_frame, parent_frame); - if (!NILP (tem = (x_get_arg (dpyinfo, parms, Qundecorated, NULL, NULL, - RES_TYPE_BOOLEAN))) - && !(EQ (tem, Qunbound))) + if (!NILP (tem = (gui_display_get_arg (dpyinfo, + parms, + Qundecorated, + NULL, + NULL, + RES_TYPE_BOOLEAN))) + && !(BASE_EQ (tem, Qunbound))) undecorated = true; FRAME_UNDECORATED (f) = undecorated; store_frame_param (f, Qundecorated, undecorated ? Qt : Qnil); - if (!NILP (tem = (x_get_arg (dpyinfo, parms, Qoverride_redirect, NULL, NULL, - RES_TYPE_BOOLEAN))) - && !(EQ (tem, Qunbound))) + if (!NILP (tem = (gui_display_get_arg (dpyinfo, + parms, + Qoverride_redirect, + NULL, + NULL, + RES_TYPE_BOOLEAN))) + && !(BASE_EQ (tem, Qunbound))) override_redirect = true; FRAME_OVERRIDE_REDIRECT (f) = override_redirect; @@ -3681,9 +4708,12 @@ This function is an internal primitive--use `make-frame' instead. */) f->output_data.x->white_relief.pixel = -1; f->output_data.x->black_relief.pixel = -1; - fset_icon_name (f, - x_get_arg (dpyinfo, parms, Qicon_name, "iconName", "Title", - RES_TYPE_STRING)); + fset_icon_name (f, gui_display_get_arg (dpyinfo, + parms, + Qicon_name, + "iconName", + "Title", + RES_TYPE_STRING)); if (! STRINGP (f->icon_name)) fset_icon_name (f, Qnil); @@ -3725,7 +4755,7 @@ This function is an internal primitive--use `make-frame' instead. */) /* Specify the parent under which to make this X window. */ if (!NILP (parent)) { - f->output_data.x->parent_desc = (Window) XFASTINT (parent); + f->output_data.x->parent_desc = (Window) XFIXNAT (parent); f->output_data.x->explicit_parent = true; } else @@ -3736,7 +4766,7 @@ This function is an internal primitive--use `make-frame' instead. */) /* Set the name; the functions to which we pass f expect the name to be set. */ - if (EQ (name, Qunbound) || NILP (name)) + if (BASE_EQ (name, Qunbound) || NILP (name)) { fset_name (f, build_string (dpyinfo->x_id_name)); f->explicit_name = false; @@ -3751,16 +4781,20 @@ This function is an internal primitive--use `make-frame' instead. */) #ifdef USE_CAIRO register_font_driver (&ftcrfont_driver, f); +#ifdef HAVE_HARFBUZZ + register_font_driver (&ftcrhbfont_driver, f); +#endif /* HAVE_HARFBUZZ */ #else #ifdef HAVE_FREETYPE #ifdef HAVE_XFT register_font_driver (&xftfont_driver, f); -#else /* not HAVE_XFT */ - register_font_driver (&ftxfont_driver, f); +#ifdef HAVE_HARFBUZZ + register_font_driver (&xfthbfont_driver, f); +#endif #endif /* not HAVE_XFT */ #endif /* HAVE_FREETYPE */ - register_font_driver (&xfont_driver, f); #endif /* not USE_CAIRO */ + register_font_driver (&xfont_driver, f); image_cache_refcount = FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0; @@ -3768,8 +4802,8 @@ This function is an internal primitive--use `make-frame' instead. */) dpyinfo_refcount = dpyinfo->reference_count; #endif /* GLYPH_DEBUG */ - x_default_parameter (f, parms, Qfont_backend, Qnil, - "fontBackend", "FontBackend", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qfont_backend, Qnil, + "fontBackend", "FontBackend", RES_TYPE_STRING); /* Extract the window parameters from the supplied values that are needed to determine window geometry. */ @@ -3782,8 +4816,8 @@ This function is an internal primitive--use `make-frame' instead. */) /* Frame contents get displaced if an embedded X window has a border. */ if (! FRAME_X_EMBEDDED_P (f)) - x_default_parameter (f, parms, Qborder_width, make_number (0), - "borderWidth", "BorderWidth", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qborder_width, make_fixnum (0), + "borderWidth", "BorderWidth", RES_TYPE_NUMBER); /* This defaults to 1 in order to match xterm. We recognize either internalBorderWidth or internalBorder (which is what xterm calls @@ -3792,54 +4826,80 @@ This function is an internal primitive--use `make-frame' instead. */) { Lisp_Object value; - value = x_get_arg (dpyinfo, parms, Qinternal_border_width, - "internalBorder", "internalBorder", RES_TYPE_NUMBER); - if (! EQ (value, Qunbound)) + value = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width, + "internalBorder", "internalBorder", + RES_TYPE_NUMBER); + if (! BASE_EQ (value, Qunbound)) parms = Fcons (Fcons (Qinternal_border_width, value), parms); } - x_default_parameter (f, parms, Qinternal_border_width, + + gui_default_parameter (f, parms, Qinternal_border_width, #ifdef USE_GTK /* We used to impose 0 in xg_create_frame_widgets. */ - make_number (0), + make_fixnum (0), #else - make_number (1), -#endif - "internalBorderWidth", "internalBorderWidth", - RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qright_divider_width, make_number (0), - NULL, NULL, RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qbottom_divider_width, make_number (0), - NULL, NULL, RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qvertical_scroll_bars, + make_fixnum (1), +#endif + "internalBorderWidth", "internalBorderWidth", + RES_TYPE_NUMBER); + + /* Same for child frames. */ + if (NILP (Fassq (Qchild_frame_border_width, parms))) + { + Lisp_Object value; + + value = gui_display_get_arg (dpyinfo, parms, Qchild_frame_border_width, + "childFrameBorder", "childFrameBorder", + RES_TYPE_NUMBER); + if (! BASE_EQ (value, Qunbound)) + parms = Fcons (Fcons (Qchild_frame_border_width, value), + parms); + } + + gui_default_parameter (f, parms, Qchild_frame_border_width, Qnil, + "childFrameBorderWidth", "childFrameBorderWidth", + RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0), + NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0), + NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qvertical_scroll_bars, #if defined (USE_GTK) && defined (USE_TOOLKIT_SCROLL_BARS) - Qright, + Qright, #else - Qleft, + Qleft, #endif - "verticalScrollBars", "ScrollBars", - RES_TYPE_SYMBOL); - x_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil, - "horizontalScrollBars", "ScrollBars", - RES_TYPE_SYMBOL); + "verticalScrollBars", "ScrollBars", + RES_TYPE_SYMBOL); + gui_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil, + "horizontalScrollBars", "ScrollBars", + RES_TYPE_SYMBOL); /* Also do the stuff which must be set before the window exists. */ - x_default_parameter (f, parms, Qforeground_color, build_string ("black"), - "foreground", "Foreground", RES_TYPE_STRING); - x_default_parameter (f, parms, Qbackground_color, build_string ("white"), - "background", "Background", RES_TYPE_STRING); - x_default_parameter (f, parms, Qmouse_color, build_string ("black"), - "pointerColor", "Foreground", RES_TYPE_STRING); - x_default_parameter (f, parms, Qborder_color, build_string ("black"), - "borderColor", "BorderColor", RES_TYPE_STRING); - x_default_parameter (f, parms, Qscreen_gamma, Qnil, - "screenGamma", "ScreenGamma", RES_TYPE_FLOAT); - x_default_parameter (f, parms, Qline_spacing, Qnil, - "lineSpacing", "LineSpacing", RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qleft_fringe, Qnil, - "leftFringe", "LeftFringe", RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qright_fringe, Qnil, - "rightFringe", "RightFringe", RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qno_special_glyphs, Qnil, - NULL, NULL, RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qforeground_color, build_string ("black"), + "foreground", "Foreground", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qbackground_color, build_string ("white"), + "background", "Background", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qmouse_color, build_string ("black"), + "pointerColor", "Foreground", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qborder_color, build_string ("black"), + "borderColor", "BorderColor", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qscreen_gamma, Qnil, + "screenGamma", "ScreenGamma", RES_TYPE_FLOAT); + gui_default_parameter (f, parms, Qline_spacing, Qnil, + "lineSpacing", "LineSpacing", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qleft_fringe, Qnil, + "leftFringe", "LeftFringe", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qright_fringe, Qnil, + "rightFringe", "RightFringe", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qno_special_glyphs, Qnil, + NULL, NULL, RES_TYPE_BOOLEAN); + +#ifdef HAVE_GTK3 + FRAME_OUTPUT_DATA (f)->scrollbar_background_css_provider + = gtk_css_provider_new (); + FRAME_OUTPUT_DATA (f)->scrollbar_foreground_css_provider + = gtk_css_provider_new (); +#endif x_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_foreground, "scrollBarForeground", @@ -3848,29 +4908,20 @@ This function is an internal primitive--use `make-frame' instead. */) "scrollBarBackground", "ScrollBarBackground", false); - /* Init faces before x_default_parameter is called for the + /* Init faces before gui_default_parameter is called for the scroll-bar-width parameter because otherwise we end up in init_iterator with a null face cache, which should not happen. */ init_frame_faces (f); - /* We have to call adjust_frame_size here since otherwise - x_set_tool_bar_lines will already work with the character sizes - installed by init_frame_faces while the frame's pixel size is still - calculated from a character size of 1 and we subsequently hit the - (height >= 0) assertion in window_box_height. - - The non-pixelwise code apparently worked around this because it - had one frame line vs one toolbar line which left us with a zero - root window height which was obviously wrong as well ... - - Also process `min-width' and `min-height' parameters right here - because `frame-windows-min-size' needs them. */ - tem = x_get_arg (dpyinfo, parms, Qmin_width, NULL, NULL, RES_TYPE_NUMBER); - if (NUMBERP (tem)) + tem = gui_display_get_arg (dpyinfo, parms, Qmin_width, NULL, NULL, + RES_TYPE_NUMBER); + if (FIXNUMP (tem)) store_frame_param (f, Qmin_width, tem); - tem = x_get_arg (dpyinfo, parms, Qmin_height, NULL, NULL, RES_TYPE_NUMBER); - if (NUMBERP (tem)) + tem = gui_display_get_arg (dpyinfo, parms, Qmin_height, NULL, NULL, + RES_TYPE_NUMBER); + if (FIXNUMP (tem)) store_frame_param (f, Qmin_height, tem); + adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f), FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 5, true, Qx_create_frame_1); @@ -3880,32 +4931,37 @@ This function is an internal primitive--use `make-frame' instead. */) here; they are processed specially at startup, and reflected in the values of the mode variables. */ - x_default_parameter (f, parms, Qmenu_bar_lines, - NILP (Vmenu_bar_mode) - ? make_number (0) : make_number (1), - NULL, NULL, RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qtool_bar_lines, - NILP (Vtool_bar_mode) - ? make_number (0) : make_number (1), - NULL, NULL, RES_TYPE_NUMBER); - - x_default_parameter (f, parms, Qbuffer_predicate, Qnil, - "bufferPredicate", "BufferPredicate", - RES_TYPE_SYMBOL); - x_default_parameter (f, parms, Qtitle, Qnil, - "title", "Title", RES_TYPE_STRING); - x_default_parameter (f, parms, Qwait_for_wm, Qt, - "waitForWM", "WaitForWM", RES_TYPE_BOOLEAN); - x_default_parameter (f, parms, Qtool_bar_position, - FRAME_TOOL_BAR_POSITION (f), 0, 0, RES_TYPE_SYMBOL); - x_default_parameter (f, parms, Qinhibit_double_buffering, Qnil, - "inhibitDoubleBuffering", "InhibitDoubleBuffering", - RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qmenu_bar_lines, + NILP (Vmenu_bar_mode) + ? make_fixnum (0) : make_fixnum (1), + NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qtab_bar_lines, + NILP (Vtab_bar_mode) + ? make_fixnum (0) : make_fixnum (1), + NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qtool_bar_lines, + NILP (Vtool_bar_mode) + ? make_fixnum (0) : make_fixnum (1), + NULL, NULL, RES_TYPE_NUMBER); + + gui_default_parameter (f, parms, Qbuffer_predicate, Qnil, + "bufferPredicate", "BufferPredicate", + RES_TYPE_SYMBOL); + gui_default_parameter (f, parms, Qtitle, Qnil, + "title", "Title", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qwait_for_wm, Qt, + "waitForWM", "WaitForWM", RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qtool_bar_position, + FRAME_TOOL_BAR_POSITION (f), 0, 0, RES_TYPE_SYMBOL); + gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil, + "inhibitDoubleBuffering", "InhibitDoubleBuffering", + RES_TYPE_BOOLEAN); /* Compute the size of the X window. */ - window_prompting = x_figure_window_size (f, parms, true, &x_width, &x_height); + window_prompting = gui_figure_window_size (f, parms, true, true); - tem = x_get_arg (dpyinfo, parms, Qunsplittable, 0, 0, RES_TYPE_BOOLEAN); + tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0, + RES_TYPE_BOOLEAN); f->no_split = minibuffer_only || EQ (tem, Qt); x_icon_verify (f, parms); @@ -3917,9 +4973,25 @@ This function is an internal primitive--use `make-frame' instead. */) x_window (f); #endif +#ifndef USE_GTK + if (FRAME_X_EMBEDDED_P (f) + && !x_embed_frame (dpyinfo, f)) + error ("The frame could not be embedded; does the embedder exist?"); +#endif + x_icon (f, parms); x_make_gc (f); + /* While this function is present in versions of libXi that only + support 2.0, it does not release the display lock after + finishing, leading to a deadlock. */ +#if defined HAVE_XINPUT2 && defined HAVE_XINPUT2_1 + if (dpyinfo->supports_xi2) + FRAME_X_OUTPUT (f)->xi_masks + = XIGetSelectedEvents (dpyinfo->display, FRAME_X_WINDOW (f), + &FRAME_X_OUTPUT (f)->num_xi_masks); +#endif + /* Now consider the frame official. */ f->terminal->reference_count++; FRAME_DISPLAY_INFO (f)->reference_count++; @@ -3927,23 +4999,25 @@ This function is an internal primitive--use `make-frame' instead. */) /* We need to do this after creating the X window, so that the icon-creation functions can say whose icon they're describing. */ - x_default_parameter (f, parms, Qicon_type, Qt, - "bitmapIcon", "BitmapIcon", RES_TYPE_BOOLEAN); - - x_default_parameter (f, parms, Qauto_raise, Qnil, - "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN); - x_default_parameter (f, parms, Qauto_lower, Qnil, - "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN); - x_default_parameter (f, parms, Qcursor_type, Qbox, - "cursorType", "CursorType", RES_TYPE_SYMBOL); - x_default_parameter (f, parms, Qscroll_bar_width, Qnil, - "scrollBarWidth", "ScrollBarWidth", - RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qscroll_bar_height, Qnil, - "scrollBarHeight", "ScrollBarHeight", - RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qalpha, Qnil, - "alpha", "Alpha", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qicon_type, Qt, + "bitmapIcon", "BitmapIcon", RES_TYPE_BOOLEAN); + + gui_default_parameter (f, parms, Qauto_raise, Qnil, + "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qauto_lower, Qnil, + "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qcursor_type, Qbox, + "cursorType", "CursorType", RES_TYPE_SYMBOL); + gui_default_parameter (f, parms, Qscroll_bar_width, Qnil, + "scrollBarWidth", "ScrollBarWidth", + RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qscroll_bar_height, Qnil, + "scrollBarHeight", "ScrollBarHeight", + RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qalpha, Qnil, + "alpha", "Alpha", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qalpha_background, Qnil, + "alphaBackground", "AlphaBackground", RES_TYPE_NUMBER); if (!NILP (parent_frame)) { @@ -3952,13 +5026,22 @@ This function is an internal primitive--use `make-frame' instead. */) block_input (); XReparentWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), FRAME_X_WINDOW (p), f->left_pos, f->top_pos); +#ifdef USE_GTK + if (EQ (x_gtk_resize_child_frames, Qresize_mode)) + gtk_container_set_resize_mode + (GTK_CONTAINER (FRAME_GTK_OUTER_WIDGET (f)), GTK_RESIZE_IMMEDIATE); +#endif +#ifdef HAVE_GTK3 + gwin = gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f)); + gdk_x11_window_set_frame_sync_enabled (gwin, FALSE); +#endif unblock_input (); } - x_default_parameter (f, parms, Qno_focus_on_map, Qnil, - NULL, NULL, RES_TYPE_BOOLEAN); - x_default_parameter (f, parms, Qno_accept_focus, Qnil, - NULL, NULL, RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qno_focus_on_map, Qnil, + NULL, NULL, RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qno_accept_focus, Qnil, + NULL, NULL, RES_TYPE_BOOLEAN); #if defined (USE_X_TOOLKIT) || defined (USE_GTK) /* Create the menu bar. */ @@ -3979,12 +5062,7 @@ This function is an internal primitive--use `make-frame' instead. */) #endif /* USE_X_TOOLKIT || USE_GTK */ /* Consider frame official, now. */ - f->can_x_set_window_size = true; - - if (x_width > 0) - SET_FRAME_WIDTH (f, x_width); - if (x_height > 0) - SET_FRAME_HEIGHT (f, x_height); + f->can_set_window_size = true; /* Tell the server what size and position, etc, we want, and how badly we want them. This should be done after we have the menu @@ -3999,28 +5077,47 @@ This function is an internal primitive--use `make-frame' instead. */) /* Process fullscreen parameter here in the hope that normalizing a fullheight/fullwidth frame will produce the size set by the last adjust_frame_size call. */ - x_default_parameter (f, parms, Qfullscreen, Qnil, - "fullscreen", "Fullscreen", RES_TYPE_SYMBOL); + gui_default_parameter (f, parms, Qfullscreen, Qnil, + "fullscreen", "Fullscreen", RES_TYPE_SYMBOL); /* Make the window appear on the frame and enable display, unless the caller says not to. However, with explicit parent, Emacs cannot control visibility, so don't try. */ if (!f->output_data.x->explicit_parent) { + /* When called from `x-create-frame-with-faces' visibility is + always explicitly nil. */ Lisp_Object visibility - = x_get_arg (dpyinfo, parms, Qvisibility, 0, 0, RES_TYPE_SYMBOL); + = gui_display_get_arg (dpyinfo, parms, Qvisibility, 0, 0, + RES_TYPE_SYMBOL); + Lisp_Object height + = gui_display_get_arg (dpyinfo, parms, Qheight, 0, 0, RES_TYPE_NUMBER); + Lisp_Object width + = gui_display_get_arg (dpyinfo, parms, Qwidth, 0, 0, RES_TYPE_NUMBER); if (EQ (visibility, Qicon)) - x_iconify_frame (f); + { + f->was_invisible = true; + x_iconify_frame (f); + } else { - if (EQ (visibility, Qunbound)) + if (BASE_EQ (visibility, Qunbound)) visibility = Qt; if (!NILP (visibility)) x_make_frame_visible (f); + else + f->was_invisible = true; } + /* Leave f->was_invisible true only if height or width were + specified too. This takes effect only when we are not called + from `x-create-frame-with-faces' (see above comment). */ + f->was_invisible + = (f->was_invisible + && (!BASE_EQ (height, Qunbound) || !BASE_EQ (width, Qunbound))); + store_frame_param (f, Qvisibility, visibility); } @@ -4040,14 +5137,66 @@ This function is an internal primitive--use `make-frame' instead. */) (unsigned char *) &dpyinfo->client_leader_window, 1); } +#ifdef HAVE_XSYNC + if (dpyinfo->xsync_supported_p + /* Frame synchronization isn't supported in child frames. */ + && NILP (parent_frame) + && !f->output_data.x->explicit_parent) + { +#ifndef HAVE_GTK3 + XSyncValue initial_value; + XSyncCounter counters[2]; + + AUTO_STRING (synchronizeResize, "synchronizeResize"); + AUTO_STRING (SynchronizeResize, "SynchronizeResize"); + + Lisp_Object value = gui_display_get_resource (dpyinfo, + synchronizeResize, + SynchronizeResize, + Qnil, Qnil); + + XSyncIntToValue (&initial_value, 0); + counters[0] + = FRAME_X_BASIC_COUNTER (f) + = XSyncCreateCounter (FRAME_X_DISPLAY (f), + initial_value); + + if (STRINGP (value) && !strcmp (SSDATA (value), "extended")) + counters[1] + = FRAME_X_EXTENDED_COUNTER (f) + = XSyncCreateCounter (FRAME_X_DISPLAY (f), + initial_value); + + FRAME_X_OUTPUT (f)->current_extended_counter_value + = initial_value; + + XChangeProperty (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), + dpyinfo->Xatom_net_wm_sync_request_counter, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &counters, + ((STRINGP (value) + && !strcmp (SSDATA (value), "extended")) ? 2 : 1)); + +#if defined HAVE_XSYNCTRIGGERFENCE && !defined USE_GTK \ + && defined HAVE_CLOCK_GETTIME + x_sync_init_fences (f); +#endif +#endif + } +#endif + unblock_input (); + /* Set whether or not frame synchronization is enabled. */ + gui_default_parameter (f, parms, Quse_frame_synchronization, Qt, + NULL, NULL, RES_TYPE_BOOLEAN); + /* Works iff frame has been already mapped. */ - x_default_parameter (f, parms, Qskip_taskbar, Qnil, - NULL, NULL, RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qskip_taskbar, Qnil, + NULL, NULL, RES_TYPE_BOOLEAN); /* The `z-group' parameter works only for visible frames. */ - x_default_parameter (f, parms, Qz_group, Qnil, - NULL, NULL, RES_TYPE_SYMBOL); + gui_default_parameter (f, parms, Qz_group, Qnil, + NULL, NULL, RES_TYPE_SYMBOL); /* Initialize `default-minibuffer-frame' in case this is the first frame on this terminal. */ @@ -4056,8 +5205,9 @@ This function is an internal primitive--use `make-frame' instead. */) || !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame))))) kset_default_minibuffer_frame (kb, frame); - /* All remaining specified parameters, which have not been "used" - by x_get_arg and friends, now go in the misc. alist of the frame. */ + /* All remaining specified parameters, which have not been "used" by + gui_display_get_arg and friends, now go in the misc. alist of the + frame. */ for (tem = parms; CONSP (tem); tem = XCDR (tem)) if (CONSP (XCAR (tem)) && !NILP (XCAR (XCAR (tem)))) fset_param_alist (f, Fcons (XCAR (tem), f->param_alist)); @@ -4069,63 +5219,9 @@ This function is an internal primitive--use `make-frame' instead. */) return unbind_to (count, frame); } - -/* FRAME is used only to get a handle on the X display. We don't pass the - display info directly because we're called from frame.c, which doesn't - know about that structure. */ - -Lisp_Object -x_get_focus_frame (struct frame *frame) -{ - struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame); - Lisp_Object xfocus; - if (! dpyinfo->x_focus_frame) - return Qnil; - - XSETFRAME (xfocus, dpyinfo->x_focus_frame); - return xfocus; -} - - -/* In certain situations, when the window manager follows a - click-to-focus policy, there seems to be no way around calling - XSetInputFocus to give another frame the input focus . - - In an ideal world, XSetInputFocus should generally be avoided so - that applications don't interfere with the window manager's focus - policy. But I think it's okay to use when it's clearly done - following a user-command. */ - -void -x_focus_frame (struct frame *f, bool noactivate) -{ - Display *dpy = FRAME_X_DISPLAY (f); - - block_input (); - x_catch_errors (dpy); - - if (FRAME_X_EMBEDDED_P (f)) - { - /* For Xembedded frames, normally the embedder forwards key - events. See XEmbed Protocol Specification at - http://freedesktop.org/wiki/Specifications/xembed-spec */ - xembed_request_focus (f); - } - else - { - XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - RevertToParent, CurrentTime); - if (!noactivate) - x_ewmh_activate_frame (f); - } - - x_uncatch_errors (); - unblock_input (); -} - DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0, - doc: /* Internal function called by `color-defined-p', which see. + doc: /* Internal function called by `color-defined-p'. \(Note that the Nextstep version of this function ignores FRAME.) */) (Lisp_Object color, Lisp_Object frame) { @@ -4134,14 +5230,15 @@ DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0, CHECK_STRING (color); - if (x_defined_color (f, SSDATA (color), &foo, false)) + if (x_defined_color (f, SSDATA (color), &foo, false, false)) return Qt; else return Qnil; } DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, 0, - doc: /* Internal function called by `color-values', which see. */) + doc: /* Internal function called by `color-values'. +\(Note that the Nextstep version of this function ignores FRAME.) */) (Lisp_Object color, Lisp_Object frame) { XColor foo; @@ -4149,14 +5246,14 @@ DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, 0, CHECK_STRING (color); - if (x_defined_color (f, SSDATA (color), &foo, false)) + if (x_defined_color (f, SSDATA (color), &foo, false, false)) return list3i (foo.red, foo.green, foo.blue); else return Qnil; } DEFUN ("xw-display-color-p", Fxw_display_color_p, Sxw_display_color_p, 0, 1, 0, - doc: /* Internal function called by `display-color-p', which see. */) + doc: /* Internal function called by `display-color-p'. */) (Lisp_Object terminal) { struct x_display_info *dpyinfo = check_x_display_info (terminal); @@ -4164,7 +5261,7 @@ DEFUN ("xw-display-color-p", Fxw_display_color_p, Sxw_display_color_p, 0, 1, 0, if (dpyinfo->n_planes <= 2) return Qnil; - switch (dpyinfo->visual->class) + switch (dpyinfo->visual_info.class) { case StaticColor: case PseudoColor: @@ -4191,7 +5288,7 @@ If omitted or nil, that stands for the selected frame's display. */) if (dpyinfo->n_planes <= 1) return Qnil; - switch (dpyinfo->visual->class) + switch (dpyinfo->visual_info.class) { case StaticColor: case PseudoColor: @@ -4212,6 +5309,7 @@ DEFUN ("x-display-pixel-width", Fx_display_pixel_width, Sx_display_pixel_width, 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. +\(On MS Windows, this function does not accept terminal objects.) On \"multi-monitor\" setups this refers to the pixel width for all physical monitors associated with TERMINAL. To get information for @@ -4220,7 +5318,7 @@ each physical monitor, use `display-monitor-attributes-list'. */) { struct x_display_info *dpyinfo = check_x_display_info (terminal); - return make_number (x_display_pixel_width (dpyinfo)); + return make_fixnum (x_display_pixel_width (dpyinfo)); } DEFUN ("x-display-pixel-height", Fx_display_pixel_height, @@ -4229,6 +5327,7 @@ DEFUN ("x-display-pixel-height", Fx_display_pixel_height, 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. +\(On MS Windows, this function does not accept terminal objects.) On \"multi-monitor\" setups this refers to the pixel height for all physical monitors associated with TERMINAL. To get information for @@ -4237,7 +5336,7 @@ each physical monitor, use `display-monitor-attributes-list'. */) { struct x_display_info *dpyinfo = check_x_display_info (terminal); - return make_number (x_display_pixel_height (dpyinfo)); + return make_fixnum (x_display_pixel_height (dpyinfo)); } DEFUN ("x-display-planes", Fx_display_planes, Sx_display_planes, @@ -4245,12 +5344,13 @@ DEFUN ("x-display-planes", Fx_display_planes, Sx_display_planes, doc: /* Return the number of bitplanes of the X display TERMINAL. 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. */) +If omitted or nil, that stands for the selected frame's display. +\(On MS Windows, this function does not accept terminal objects.) */) (Lisp_Object terminal) { struct x_display_info *dpyinfo = check_x_display_info (terminal); - return make_number (dpyinfo->n_planes); + return make_fixnum (dpyinfo->n_planes); } DEFUN ("x-display-color-cells", Fx_display_color_cells, Sx_display_color_cells, @@ -4258,22 +5358,26 @@ DEFUN ("x-display-color-cells", Fx_display_color_cells, Sx_display_color_cells, doc: /* Return the number of color cells of the X display TERMINAL. 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. */) +If omitted or nil, that stands for the selected frame's display. +\(On MS Windows, this function does not accept terminal objects.) */) (Lisp_Object terminal) { struct x_display_info *dpyinfo = check_x_display_info (terminal); - int nr_planes = DisplayPlanes (dpyinfo->display, - XScreenNumberOfScreen (dpyinfo->screen)); + if (dpyinfo->visual_info.class != TrueColor + && dpyinfo->visual_info.class != DirectColor) + return make_fixnum (dpyinfo->visual_info.colormap_size); - /* Truncate nr_planes to 24 to avoid integer overflow. - Some displays says 32, but only 24 bits are actually significant. + int nr_planes = dpyinfo->n_planes; + + /* Truncate nr_planes to 24 to avoid integer overflow. Some + displays says 32, but only 24 bits are actually significant. There are only very few and rare video cards that have more than - 24 significant bits. Also 24 bits is more than 16 million colors, - it "should be enough for everyone". */ + 24 significant bits. Also 24 bits is more than 16 million + colors, it "should be enough for everyone". */ if (nr_planes > 24) nr_planes = 24; - return make_number (1 << nr_planes); + return make_fixnum (1 << nr_planes); } DEFUN ("x-server-max-request-size", Fx_server_max_request_size, @@ -4282,12 +5386,15 @@ DEFUN ("x-server-max-request-size", Fx_server_max_request_size, doc: /* Return the maximum request size of the X server of display TERMINAL. 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. */) +If omitted or nil, that stands for the selected frame's display. + +On MS Windows, this function just returns 1. +On Nextstep, this function just returns nil. */) (Lisp_Object terminal) { struct x_display_info *dpyinfo = check_x_display_info (terminal); - return make_number (MAXREQUEST (dpyinfo->display)); + return make_fixnum (MAXREQUEST (dpyinfo->display)); } DEFUN ("x-server-vendor", Fx_server_vendor, Sx_server_vendor, 0, 1, 0, @@ -4297,8 +5404,8 @@ DEFUN ("x-server-vendor", Fx_server_vendor, Sx_server_vendor, 0, 1, 0, that operating systems cannot be developed and distributed noncommercially.) The optional argument TERMINAL specifies which display to ask about. -For GNU and Unix systems, this queries the X server software; for -MS-Windows, this queries the OS. +For GNU and Unix systems, this queries the X server software. +For MS Windows and Nextstep the result is hard-coded. 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. */) @@ -4318,8 +5425,10 @@ software in use. For GNU and Unix system, the first 2 numbers are the version of the X Protocol used on TERMINAL and the 3rd number is the distributor-specific -release number. For MS-Windows, the 3 numbers report the version and -the build number of the OS. +release number. For MS Windows, the 3 numbers report the OS major and +minor version and build number. For Nextstep, the first 2 numbers are +hard-coded and the 3rd represents the OS version. For Haiku, all 3 +numbers are hard-coded. See also the function `x-server-vendor'. @@ -4335,16 +5444,42 @@ If omitted or nil, that stands for the selected frame's display. */) VendorRelease (dpy)); } +DEFUN ("x-server-input-extension-version", Fx_server_input_extension_version, + Sx_server_input_extension_version, 0, 1, 0, + doc: /* Return the version of the X Input Extension supported by TERMINAL. +The value is nil if TERMINAL's X server doesn't support the X Input +Extension extension, or if Emacs doesn't support the version present +on that server. Otherwise, the return value is a list of the major +and minor versions of the X Input Extension extension running on that +server. */) + (Lisp_Object terminal) +{ +#ifdef HAVE_XINPUT2 + struct x_display_info *dpyinfo = check_x_display_info (terminal); + + return (dpyinfo->supports_xi2 + ? list2i (2, dpyinfo->xi2_version) + : Qnil); +#else + return Qnil; +#endif +} + DEFUN ("x-display-screens", Fx_display_screens, Sx_display_screens, 0, 1, 0, doc: /* Return the number of screens on the X server of display TERMINAL. 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. */) +If omitted or nil, that stands for the selected frame's display. + +On MS Windows, this function just returns 1. +On Nextstep, "screen" is in X terminology, not that of Nextstep. +For the number of physical monitors, use `(length +\(display-monitor-attributes-list TERMINAL))' instead. */) (Lisp_Object terminal) { struct x_display_info *dpyinfo = check_x_display_info (terminal); - return make_number (ScreenCount (dpyinfo->display)); + return make_fixnum (ScreenCount (dpyinfo->display)); } DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, 0, 1, 0, @@ -4352,6 +5487,7 @@ DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, 0, 1, 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. +\(On MS Windows, this function does not accept terminal objects.) On \"multi-monitor\" setups this refers to the height in millimeters for all physical monitors associated with TERMINAL. To get information @@ -4360,7 +5496,10 @@ for each physical monitor, use `display-monitor-attributes-list'. */) { struct x_display_info *dpyinfo = check_x_display_info (terminal); - return make_number (HeightMMOfScreen (dpyinfo->screen)); + if (dpyinfo->screen_mm_height) + return make_fixnum (dpyinfo->screen_mm_height); + + return make_fixnum (HeightMMOfScreen (dpyinfo->screen)); } DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0, @@ -4368,6 +5507,7 @@ DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0, 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. +\(On MS Windows, this function does not accept terminal objects.) On \"multi-monitor\" setups this refers to the width in millimeters for all physical monitors associated with TERMINAL. To get information @@ -4376,16 +5516,22 @@ for each physical monitor, use `display-monitor-attributes-list'. */) { struct x_display_info *dpyinfo = check_x_display_info (terminal); - return make_number (WidthMMOfScreen (dpyinfo->screen)); + if (dpyinfo->screen_mm_width) + return make_fixnum (dpyinfo->screen_mm_width); + + return make_fixnum (WidthMMOfScreen (dpyinfo->screen)); } DEFUN ("x-display-backing-store", Fx_display_backing_store, Sx_display_backing_store, 0, 1, 0, doc: /* Return an indication of whether X display TERMINAL does backing store. -The value may be `always', `when-mapped', or `not-useful'. 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. */) +If omitted or nil, that stands for the selected frame's display. + +The value may be `always', `when-mapped', or `not-useful'. +On Nextstep, the value may be `buffered', `retained', or `non-retained'. +On MS Windows, this returns nothing useful. */) (Lisp_Object terminal) { struct x_display_info *dpyinfo = check_x_display_info (terminal); @@ -4394,15 +5540,15 @@ If omitted or nil, that stands for the selected frame's display. */) switch (DoesBackingStore (dpyinfo->screen)) { case Always: - result = intern ("always"); + result = Qalways; break; case WhenMapped: - result = intern ("when-mapped"); + result = Qwhen_mapped; break; case NotUseful: - result = intern ("not-useful"); + result = Qnot_useful; break; default: @@ -4417,34 +5563,36 @@ DEFUN ("x-display-visual-class", Fx_display_visual_class, doc: /* Return the visual class of the X display TERMINAL. The value is one of the symbols `static-gray', `gray-scale', `static-color', `pseudo-color', `true-color', or `direct-color'. +\(On MS Windows, the second and last result above are not possible.) The optional argument TERMINAL specifies which display to ask about. TERMINAL should a terminal object, a frame or a display name (a string). -If omitted or nil, that stands for the selected frame's display. */) +If omitted or nil, that stands for the selected frame's display. +\(On MS Windows, this function does not accept terminal objects.) */) (Lisp_Object terminal) { struct x_display_info *dpyinfo = check_x_display_info (terminal); Lisp_Object result; - switch (dpyinfo->visual->class) + switch (dpyinfo->visual_info.class) { case StaticGray: - result = intern ("static-gray"); + result = Qstatic_gray; break; case GrayScale: - result = intern ("gray-scale"); + result = Qgray_scale; break; case StaticColor: - result = intern ("static-color"); + result = Qstatic_color; break; case PseudoColor: - result = intern ("pseudo-color"); + result = Qpseudo_color; break; case TrueColor: - result = intern ("true-color"); + result = Qtrue_color; break; case DirectColor: - result = intern ("direct-color"); + result = Qdirect_color; break; default: error ("Display has an unknown visual class"); @@ -4458,7 +5606,9 @@ DEFUN ("x-display-save-under", Fx_display_save_under, doc: /* Return t if the X display TERMINAL supports the save-under feature. 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. */) +If omitted or nil, that stands for the selected frame's display. + +On MS Windows, this just returns nil. */) (Lisp_Object terminal) { struct x_display_info *dpyinfo = check_x_display_info (terminal); @@ -4469,14 +5619,16 @@ If omitted or nil, that stands for the selected frame's display. */) return Qnil; } +#if !(defined USE_GTK && defined HAVE_GTK3) + /* 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. */ -#if ! GTK_CHECK_VERSION (3, 4, 0) static bool x_get_net_workarea (struct x_display_info *dpyinfo, XRectangle *rect) { +#ifndef USE_XCB Display *dpy = dpyinfo->display; long offset, max_len; Atom target_type, actual_type; @@ -4530,8 +5682,71 @@ x_get_net_workarea (struct x_display_info *dpyinfo, XRectangle *rect) x_uncatch_errors (); return result; -} +#else + xcb_get_property_cookie_t current_desktop_cookie; + xcb_get_property_cookie_t workarea_cookie; + xcb_get_property_reply_t *reply; + xcb_generic_error_t *error; + bool rc; + uint32_t current_workspace, *values; + + current_desktop_cookie + = xcb_get_property (dpyinfo->xcb_connection, 0, + (xcb_window_t) dpyinfo->root_window, + (xcb_atom_t) dpyinfo->Xatom_net_current_desktop, + XCB_ATOM_CARDINAL, 0, 1); + + workarea_cookie + = xcb_get_property (dpyinfo->xcb_connection, 0, + (xcb_window_t) dpyinfo->root_window, + (xcb_atom_t) dpyinfo->Xatom_net_workarea, + XCB_ATOM_CARDINAL, 0, UINT32_MAX); + + reply = xcb_get_property_reply (dpyinfo->xcb_connection, + current_desktop_cookie, &error); + rc = true; + + if (!reply) + free (error), rc = false; + else + { + if (xcb_get_property_value_length (reply) != 4 + || reply->type != XCB_ATOM_CARDINAL || reply->format != 32) + rc = false; + else + current_workspace = *(uint32_t *) xcb_get_property_value (reply); + + free (reply); + } + + reply = xcb_get_property_reply (dpyinfo->xcb_connection, + workarea_cookie, &error); + + if (!reply) + free (error), rc = false; + else + { + if (rc && reply->type == XCB_ATOM_CARDINAL && reply->format == 32 + && (xcb_get_property_value_length (reply) / sizeof (uint32_t) + >= current_workspace + 4)) + { + values = xcb_get_property_value (reply); + + rect->x = values[current_workspace]; + rect->y = values[current_workspace + 1]; + rect->width = values[current_workspace + 2]; + rect->height = values[current_workspace + 3]; + } + else + rc = false; + + free (reply); + } + + return rc; #endif +} +#endif /* !(USE_GTK && HAVE_GTK3) */ #ifndef USE_GTK @@ -4560,7 +5775,7 @@ x_get_monitor_for_frame (struct frame *f, if (mi->geom.width == 0) continue; - if (x_intersect_rectangles (&mi->geom, &frect, &res)) + if (gui_intersect_rectangles (&mi->geom, &frect, &res)) { a = res.width * res.height; if (a > area) @@ -4605,15 +5820,16 @@ x_make_monitor_attribute_list (struct MonitorInfo *monitors, struct x_display_info *dpyinfo, const char *source) { - Lisp_Object monitor_frames = Fmake_vector (make_number (n_monitors), Qnil); + Lisp_Object monitor_frames = make_nil_vector (n_monitors); Lisp_Object frame, rest; FOR_EACH_FRAME (rest, frame) { struct frame *f = XFRAME (frame); - if (FRAME_X_P (f) && FRAME_DISPLAY_INFO (f) == dpyinfo - && !EQ (frame, tip_frame)) + if (FRAME_X_P (f) + && FRAME_DISPLAY_INFO (f) == dpyinfo + && !FRAME_TOOLTIP_P (f)) { int i = x_get_monitor_for_frame (f, monitors, n_monitors); ASET (monitor_frames, i, Fcons (frame, AREF (monitor_frames, i))); @@ -4688,7 +5904,7 @@ x_get_monitor_attributes_xinerama (struct x_display_info *dpyinfo) if (i == 0 && x_get_net_workarea (dpyinfo, &workarea_r)) { mi->work = workarea_r; - if (! x_intersect_rectangles (&mi->geom, &mi->work, &mi->work)) + if (! gui_intersect_rectangles (&mi->geom, &mi->work, &mi->work)) mi->work = mi->geom; } else @@ -4717,6 +5933,108 @@ x_get_monitor_attributes_xrandr (struct x_display_info *dpyinfo) int i, n_monitors, primary = -1; RROutput pxid = None; struct MonitorInfo *monitors; + bool randr15_p = false; + +#if RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR >= 5) + XRRMonitorInfo *rr_monitors; +#ifdef USE_XCB + xcb_get_atom_name_cookie_t *atom_name_cookies; + xcb_get_atom_name_reply_t *reply; + xcb_generic_error_t *error; + int length; +#endif + + /* If RandR 1.5 or later is available, use that instead, as some + video drivers don't report correct dimensions via other versions + of RandR. */ + if (dpyinfo->xrandr_major_version > 1 + || (dpyinfo->xrandr_major_version == 1 + && dpyinfo->xrandr_minor_version >= 5)) + { + XRectangle workarea; + char *name; + + rr_monitors = XRRGetMonitors (dpyinfo->display, + dpyinfo->root_window, + True, &n_monitors); + if (!rr_monitors) + goto fallback; + + monitors = xzalloc (n_monitors * sizeof *monitors); +#ifdef USE_XCB + atom_name_cookies = alloca (n_monitors * sizeof *atom_name_cookies); +#endif + + for (int i = 0; i < n_monitors; ++i) + { + monitors[i].geom.x = rr_monitors[i].x; + monitors[i].geom.y = rr_monitors[i].y; + monitors[i].geom.width = rr_monitors[i].width; + monitors[i].geom.height = rr_monitors[i].height; + monitors[i].mm_width = rr_monitors[i].mwidth; + monitors[i].mm_height = rr_monitors[i].mheight; + +#ifndef USE_XCB + name = XGetAtomName (dpyinfo->display, rr_monitors[i].name); + if (name) + { + monitors[i].name = xstrdup (name); + XFree (name); + } + else + monitors[i].name = xstrdup ("Unknown Monitor"); +#else + atom_name_cookies[i] + = xcb_get_atom_name (dpyinfo->xcb_connection, + (xcb_atom_t) rr_monitors[i].name); +#endif + + if (rr_monitors[i].primary) + primary = i; + + if (rr_monitors[i].primary + && x_get_net_workarea (dpyinfo, &workarea)) + { + monitors[i].work = workarea; + if (!gui_intersect_rectangles (&monitors[i].geom, + &monitors[i].work, + &monitors[i].work)) + monitors[i].work = monitors[i].geom; + } + else + monitors[i].work = monitors[i].geom; + } + +#ifdef USE_XCB + for (int i = 0; i < n_monitors; ++i) + { + reply = xcb_get_atom_name_reply (dpyinfo->xcb_connection, + atom_name_cookies[i], &error); + + if (!reply) + { + monitors[i].name = xstrdup ("Unknown monitor"); + free (error); + } + else + { + length = xcb_get_atom_name_name_length (reply); + name = xmalloc (length + 1); + memcpy (name, xcb_get_atom_name_name (reply), length); + name[length] = '\0'; + monitors[i].name = name; + free (reply); + } + } +#endif + + XRRFreeMonitors (rr_monitors); + randr15_p = true; + goto out; + } + + fallback:; +#endif #define RANDR13_LIBRARY \ (RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR >= 3)) @@ -4748,6 +6066,8 @@ x_get_monitor_attributes_xrandr (struct x_display_info *dpyinfo) pxid = XRRGetOutputPrimary (dpy, dpyinfo->root_window); #endif +#undef RANDR13_LIBRARY + for (i = 0; i < n_monitors; ++i) { XRROutputInfo *info = XRRGetOutputInfo (dpy, resources, @@ -4792,7 +6112,7 @@ x_get_monitor_attributes_xrandr (struct x_display_info *dpyinfo) if (i == primary && x_get_net_workarea (dpyinfo, &workarea_r)) { mi->work= workarea_r; - if (! x_intersect_rectangles (&mi->geom, &mi->work, &mi->work)) + if (! gui_intersect_rectangles (&mi->geom, &mi->work, &mi->work)) mi->work = mi->geom; } else @@ -4803,12 +6123,16 @@ x_get_monitor_attributes_xrandr (struct x_display_info *dpyinfo) XRRFreeOutputInfo (info); } XRRFreeScreenResources (resources); - +#if RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR >= 5) + out: +#endif attributes_list = x_make_monitor_attribute_list (monitors, n_monitors, primary, dpyinfo, - "XRandr"); + (randr15_p + ? "XRandR 1.5" + : "XRandr")); free_monitors (monitors, n_monitors); return attributes_list; } @@ -4823,17 +6147,9 @@ x_get_monitor_attributes (struct x_display_info *dpyinfo) (void) dpy; /* Suppress unused variable warning. */ #ifdef HAVE_XRANDR - int xrr_event_base, xrr_error_base; - bool xrr_ok = false; - xrr_ok = XRRQueryExtension (dpy, &xrr_event_base, &xrr_error_base); - if (xrr_ok) - { - XRRQueryVersion (dpy, &dpyinfo->xrandr_major_version, - &dpyinfo->xrandr_minor_version); - xrr_ok = ((dpyinfo->xrandr_major_version == 1 - && dpyinfo->xrandr_minor_version >= 2) - || dpyinfo->xrandr_major_version > 1); - } + bool xrr_ok = ((dpyinfo->xrandr_major_version == 1 + && dpyinfo->xrandr_minor_version >= 2) + || dpyinfo->xrandr_major_version > 1); if (xrr_ok) attributes_list = x_get_monitor_attributes_xrandr (dpyinfo); @@ -4842,10 +6158,7 @@ x_get_monitor_attributes (struct x_display_info *dpyinfo) #ifdef HAVE_XINERAMA if (NILP (attributes_list)) { - int xin_event_base, xin_error_base; - bool xin_ok = false; - xin_ok = XineramaQueryExtension (dpy, &xin_event_base, &xin_error_base); - if (xin_ok && XineramaIsActive (dpy)) + if (dpyinfo->xinerama_supported_p && XineramaIsActive (dpy)) attributes_list = x_get_monitor_attributes_xinerama (dpyinfo); } #endif /* HAVE_XINERAMA */ @@ -4858,6 +6171,65 @@ x_get_monitor_attributes (struct x_display_info *dpyinfo) #endif /* !USE_GTK */ +#ifdef USE_LUCID +/* This is used by the Lucid menu widget, but it's defined here so we + can make use of a great deal of existing code. */ +static void +xlw_monitor_dimensions_at_pos_1 (struct x_display_info *dpyinfo, + Screen *screen, int src_x, int src_y, + int *x, int *y, int *width, int *height) +{ + Lisp_Object attrs, tem, val; + + attrs = x_get_monitor_attributes (dpyinfo); + + for (tem = attrs; CONSP (tem); tem = XCDR (tem)) + { + int sx, sy, swidth, sheight; + val = assq_no_quit (Qworkarea, XCAR (tem)); + if (!NILP (val)) + { + sx = XFIXNUM (XCAR (XCDR (val))); + sy = XFIXNUM (XCAR (XCDR (XCDR (val)))); + swidth = XFIXNUM (XCAR (XCDR (XCDR (XCDR (val))))); + sheight = XFIXNUM (XCAR (XCDR (XCDR (XCDR (XCDR (val)))))); + + if (sx <= src_x && src_x < (sx + swidth) + && sy <= src_y && src_y < (sy + swidth)) + { + *x = sx; + *y = sy; + *width = swidth; + *height = sheight; + return; + } + } + } + + *x = 0; + *y = 0; + *width = WidthOfScreen (screen); + *height = HeightOfScreen (screen); +} + +void +xlw_monitor_dimensions_at_pos (Display *dpy, Screen *screen, int src_x, + int src_y, int *x, int *y, int *width, int *height) +{ + struct x_display_info *dpyinfo = x_display_info_for_display (dpy); + + if (!dpyinfo) + emacs_abort (); + + block_input (); + xlw_monitor_dimensions_at_pos_1 (dpyinfo, screen, src_x, src_y, + x, y, width, height); + + unblock_input (); +} +#endif + + DEFUN ("x-display-monitor-attributes-list", Fx_display_monitor_attributes_list, Sx_display_monitor_attributes_list, 0, 1, 0, @@ -4882,9 +6254,9 @@ Internal use only, use `display-monitor-attributes-list' instead. */) Lisp_Object attributes_list = Qnil; #ifdef USE_GTK - double mm_width_per_pixel, mm_height_per_pixel; GdkDisplay *gdpy; #if ! GTK_CHECK_VERSION (3, 22, 0) + double mm_width_per_pixel, mm_height_per_pixel; GdkScreen *gscreen; #endif gint primary_monitor = 0, n_monitors, i; @@ -4893,29 +6265,29 @@ Internal use only, use `display-monitor-attributes-list' instead. */) struct MonitorInfo *monitors; block_input (); - mm_width_per_pixel = ((double) WidthMMOfScreen (dpyinfo->screen) - / x_display_pixel_width (dpyinfo)); - mm_height_per_pixel = ((double) HeightMMOfScreen (dpyinfo->screen) - / x_display_pixel_height (dpyinfo)); gdpy = gdk_x11_lookup_xdisplay (dpyinfo->display); #if GTK_CHECK_VERSION (3, 22, 0) n_monitors = gdk_display_get_n_monitors (gdpy); #else gscreen = gdk_display_get_default_screen (gdpy); -#if GTK_CHECK_VERSION (2, 20, 0) - primary_monitor = gdk_screen_get_primary_monitor (gscreen); -#endif n_monitors = gdk_screen_get_n_monitors (gscreen); + primary_monitor = gdk_screen_get_primary_monitor (gscreen); + /* Fallback if gdk_screen_get_monitor_{width,height}_mm fail */ + mm_width_per_pixel = ((double) WidthMMOfScreen (dpyinfo->screen) + / x_display_pixel_width (dpyinfo)); + mm_height_per_pixel = ((double) HeightMMOfScreen (dpyinfo->screen) + / x_display_pixel_height (dpyinfo)); #endif - monitor_frames = Fmake_vector (make_number (n_monitors), Qnil); + monitor_frames = make_nil_vector (n_monitors); monitors = xzalloc (n_monitors * sizeof *monitors); FOR_EACH_FRAME (rest, frame) { struct frame *f = XFRAME (frame); - if (FRAME_X_P (f) && FRAME_DISPLAY_INFO (f) == dpyinfo - && !EQ (frame, tip_frame)) + if (FRAME_X_P (f) + && FRAME_DISPLAY_INFO (f) == dpyinfo + && !FRAME_TOOLTIP_P (f)) { GdkWindow *gwin = gtk_widget_get_window (FRAME_GTK_WIDGET (f)); @@ -4933,9 +6305,10 @@ Internal use only, use `display-monitor-attributes-list' instead. */) for (i = 0; i < n_monitors; ++i) { - gint width_mm = -1, height_mm = -1; + gint width_mm, height_mm; GdkRectangle rec, work; struct MonitorInfo *mi = &monitors[i]; + int scale = 1; #if GTK_CHECK_VERSION (3, 22, 0) GdkMonitor *monitor = gdk_display_get_monitor (gdpy, i); @@ -4949,18 +6322,17 @@ Internal use only, use `display-monitor-attributes-list' instead. */) #if GTK_CHECK_VERSION (3, 22, 0) width_mm = gdk_monitor_get_width_mm (monitor); height_mm = gdk_monitor_get_height_mm (monitor); -#elif GTK_CHECK_VERSION (2, 14, 0) +#else 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; - +#endif #if GTK_CHECK_VERSION (3, 22, 0) gdk_monitor_get_workarea (monitor, &work); -#elif GTK_CHECK_VERSION (3, 4, 0) +#elif defined HAVE_GTK3 gdk_screen_get_monitor_workarea (gscreen, i, &work); #else /* Emulate the behavior of GTK+ 3.4. */ @@ -4981,6 +6353,20 @@ Internal use only, use `display-monitor-attributes-list' instead. */) } #endif + /* GTK returns scaled sizes for the workareas. */ +#if GTK_CHECK_VERSION (3, 22, 0) + scale = gdk_monitor_get_scale_factor (monitor); +#elif defined HAVE_GTK3 + scale = gdk_screen_get_monitor_scale_factor (gscreen, i); +#endif + rec.x *= scale; + rec.y *= scale; + rec.width *= scale; + rec.height *= scale; + work.x *= scale; + work.y *= scale; + work.width *= scale; + work.height *= scale; mi->geom.x = rec.x; mi->geom.y = rec.y; @@ -4994,8 +6380,8 @@ Internal use only, use `display-monitor-attributes-list' instead. */) mi->mm_height = height_mm; #if GTK_CHECK_VERSION (3, 22, 0) - mi->name = g_strdup (gdk_monitor_get_model (monitor)); -#elif GTK_CHECK_VERSION (2, 14, 0) + dupstring (&mi->name, (gdk_monitor_get_model (monitor))); +#else mi->name = gdk_screen_get_monitor_plug_name (gscreen, i); #endif } @@ -5005,6 +6391,7 @@ Internal use only, use `display-monitor-attributes-list' instead. */) primary_monitor, monitor_frames, source); + free_monitors (monitors, n_monitors); unblock_input (); #else /* not USE_GTK */ @@ -5037,9 +6424,10 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) int internal_border_width; bool menu_bar_external = false, tool_bar_external = false; int menu_bar_height = 0, menu_bar_width = 0; + int tab_bar_height = 0, tab_bar_width = 0; int tool_bar_height = 0, tool_bar_width = 0; - if (FRAME_INITIAL_P (f) || !FRAME_X_P (f)) + if (FRAME_INITIAL_P (f) || !FRAME_X_P (f) || !FRAME_OUTER_WINDOW (f)) return Qnil; block_input (); @@ -5063,8 +6451,8 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) edges = Fx_frame_edges (parent, Qnative_edges); if (!NILP (edges)) { - x_native += XINT (Fnth (make_number (0), edges)); - y_native += XINT (Fnth (make_number (1), edges)); + x_native += XFIXNUM (Fnth (make_fixnum (0), edges)); + y_native += XFIXNUM (Fnth (make_fixnum (1), edges)); } outer_left = x_native; @@ -5096,7 +6484,7 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) inner_right = native_right - internal_border_width; inner_bottom = native_bottom - internal_border_width; -#if defined (USE_X_TOOLKIT) || defined (USE_GTK) +#ifdef HAVE_EXT_MENU_BAR menu_bar_external = true; menu_bar_height = FRAME_MENUBAR_HEIGHT (f); native_top += menu_bar_height; @@ -5107,7 +6495,13 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) #endif menu_bar_width = menu_bar_height ? native_width : 0; -#if defined (USE_GTK) + tab_bar_height = FRAME_TAB_BAR_HEIGHT (f); + tab_bar_width = (tab_bar_height + ? native_width - 2 * internal_border_width + : 0); + inner_top += tab_bar_height; + +#ifdef HAVE_EXT_TOOL_BAR tool_bar_external = true; if (EQ (FRAME_TOOL_BAR_POSITION (f), Qleft)) { @@ -5149,43 +6543,42 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) /* Construct list. */ if (EQ (attribute, Qouter_edges)) - return list4 (make_number (outer_left), make_number (outer_top), - make_number (outer_right), make_number (outer_bottom)); + return list4i (outer_left, outer_top, outer_right, outer_bottom); else if (EQ (attribute, Qnative_edges)) - return list4 (make_number (native_left), make_number (native_top), - make_number (native_right), make_number (native_bottom)); + return list4i (native_left, native_top, native_right, native_bottom); else if (EQ (attribute, Qinner_edges)) - return list4 (make_number (inner_left), make_number (inner_top), - make_number (inner_right), make_number (inner_bottom)); + return list4i (inner_left, inner_top, inner_right, inner_bottom); else return - listn (CONSTYPE_HEAP, 11, - Fcons (Qouter_position, - Fcons (make_number (outer_left), - make_number (outer_top))), + list (Fcons (Qouter_position, + Fcons (make_fixnum (outer_left), + make_fixnum (outer_top))), Fcons (Qouter_size, - Fcons (make_number (outer_right - outer_left), - make_number (outer_bottom - outer_top))), + Fcons (make_fixnum (outer_right - outer_left), + make_fixnum (outer_bottom - outer_top))), /* Approximate. */ Fcons (Qexternal_border_size, - Fcons (make_number (right_off), - make_number (bottom_off))), - Fcons (Qouter_border_width, make_number (x_border_width)), + Fcons (make_fixnum (right_off), + make_fixnum (bottom_off))), + Fcons (Qouter_border_width, make_fixnum (x_border_width)), /* Approximate. */ Fcons (Qtitle_bar_size, - Fcons (make_number (0), - make_number (top_off - bottom_off))), + Fcons (make_fixnum (0), + make_fixnum (top_off - bottom_off))), Fcons (Qmenu_bar_external, menu_bar_external ? Qt : Qnil), Fcons (Qmenu_bar_size, - Fcons (make_number (menu_bar_width), - make_number (menu_bar_height))), + Fcons (make_fixnum (menu_bar_width), + make_fixnum (menu_bar_height))), + Fcons (Qtab_bar_size, + Fcons (make_fixnum (tab_bar_width), + make_fixnum (tab_bar_height))), Fcons (Qtool_bar_external, tool_bar_external ? Qt : Qnil), Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)), Fcons (Qtool_bar_size, - Fcons (make_number (tool_bar_width), - make_number (tool_bar_height))), + Fcons (make_fixnum (tool_bar_width), + make_fixnum (tool_bar_height))), Fcons (Qinternal_border_width, - make_number (internal_border_width))); + make_fixnum (internal_border_width))); } DEFUN ("x-frame-geometry", Fx_frame_geometry, Sx_frame_geometry, 0, 1, 0, @@ -5230,8 +6623,8 @@ and width values are in pixels. FRAME. `outer-border-width' is the width of the X border of FRAME. The X - border is usually only shown for frames without window manager - decorations like child and tooltip frames. */) + border is usually shown only for frames without window manager + decorations, such as child and tooltip frames. */) (Lisp_Object frame) { return frame_geometry (frame, Qnil); @@ -5269,34 +6662,81 @@ menu bar or tool bar of FRAME. */) * WINDOW to FRAMES and return FRAMES. */ static Lisp_Object -x_frame_list_z_order (Display* dpy, Window window) +x_frame_list_z_order (struct x_display_info *dpyinfo, Window window) { + Display *dpy; Window root, parent, *children; unsigned int nchildren; - int i; - Lisp_Object frames = Qnil; + unsigned long i; + Lisp_Object frames, val; + Atom type; + Window *toplevels; + int format, rc; + unsigned long nitems, bytes_after; + unsigned char *data; + struct frame *f; + + dpy = dpyinfo->display; + data = NULL; + frames = Qnil; + + if (window == dpyinfo->root_window + && x_wm_supports_1 (dpyinfo, + dpyinfo->Xatom_net_client_list_stacking)) + { + rc = XGetWindowProperty (dpyinfo->display, dpyinfo->root_window, + dpyinfo->Xatom_net_client_list_stacking, + 0, LONG_MAX, False, XA_WINDOW, &type, + &format, &nitems, &bytes_after, &data); + + if (rc != Success) + return Qnil; + + if (format != 32 || type != XA_WINDOW) + { + XFree (data); + return Qnil; + } + + toplevels = (Window *) data; + + for (i = 0; i < nitems; ++i) + { + f = x_top_window_to_frame (dpyinfo, toplevels[i]); + + if (f) + { + XSETFRAME (val, f); + frames = Fcons (val, frames); + } + } + + XFree (data); + return frames; + } - block_input (); if (XQueryTree (dpy, window, &root, &parent, &children, &nchildren)) { - unblock_input (); for (i = 0; i < nchildren; i++) { Lisp_Object frame, tail; FOR_EACH_FRAME (tail, frame) - /* With a reparenting window manager the parent_desc field - usually specifies the topmost windows of our frames. - Otherwise FRAME_OUTER_WINDOW should do. */ - if (XFRAME (frame)->output_data.x->parent_desc == children[i] - || FRAME_OUTER_WINDOW (XFRAME (frame)) == children[i]) - frames = Fcons (frame, frames); + { + struct frame *cf = XFRAME (frame); + /* With a reparenting window manager the parent_desc + field usually specifies the topmost windows of our + frames. Otherwise FRAME_OUTER_WINDOW should do. */ + if (FRAME_X_P (cf) + && (cf->output_data.x->parent_desc == children[i] + || FRAME_OUTER_WINDOW (cf) == children[i])) + frames = Fcons (frame, frames); + } } - if (children) XFree ((char *)children); + if (children) + XFree (children); } - else - unblock_input (); return frames; } @@ -5317,7 +6757,6 @@ Frames are listed from topmost (first) to bottommost (last). */) (Lisp_Object terminal) { struct x_display_info *dpyinfo = check_x_display_info (terminal); - Display *dpy = dpyinfo->display; Window window; if (FRAMEP (terminal) && FRAME_LIVE_P (XFRAME (terminal))) @@ -5325,7 +6764,7 @@ Frames are listed from topmost (first) to bottommost (last). */) else window = dpyinfo->root_window; - return x_frame_list_z_order (dpy, window); + return x_frame_list_z_order (dpyinfo, window); } /** @@ -5339,7 +6778,7 @@ Frames are listed from topmost (first) to bottommost (last). */) static void x_frame_restack (struct frame *f1, struct frame *f2, bool above_flag) { -#if defined (USE_GTK) && GTK_CHECK_VERSION (2, 18, 0) +#ifdef USE_GTK block_input (); xg_frame_restack (f1, f2, above_flag); unblock_input (); @@ -5381,16 +6820,10 @@ Some window managers may refuse to restack windows. */) struct frame *f1 = decode_live_frame (frame1); struct frame *f2 = decode_live_frame (frame2); - if (FRAME_OUTER_WINDOW (f1) && FRAME_OUTER_WINDOW (f2)) - { - x_frame_restack (f1, f2, !NILP (above)); - return Qt; - } - else - { - error ("Cannot restack frames"); - return Qnil; - } + if (! (FRAME_OUTER_WINDOW (f1) && FRAME_OUTER_WINDOW (f2))) + error ("Cannot restack frames"); + x_frame_restack (f1, f2, !NILP (above)); + return Qt; } @@ -5410,13 +6843,13 @@ selected frame's display. */) return Qnil; block_input (); - XQueryPointer (FRAME_X_DISPLAY (f), - DefaultRootWindow (FRAME_X_DISPLAY (f)), - &root, &dummy_window, &x, &y, &dummy, &dummy, - (unsigned int *) &dummy); + x_query_pointer (FRAME_X_DISPLAY (f), + FRAME_DISPLAY_INFO (f)->root_window, + &root, &dummy_window, &x, &y, &dummy, &dummy, + (unsigned int *) &dummy); unblock_input (); - return Fcons (make_number (x), make_number (y)); + return Fcons (make_fixnum (x), make_fixnum (y)); } DEFUN ("x-set-mouse-absolute-pixel-position", Fx_set_mouse_absolute_pixel_position, @@ -5425,23 +6858,214 @@ DEFUN ("x-set-mouse-absolute-pixel-position", Fx_set_mouse_absolute_pixel_positi The coordinates X and Y are interpreted in pixels relative to a position \(0, 0) of the selected frame's display. */) (Lisp_Object x, Lisp_Object y) - { +{ struct frame *f = SELECTED_FRAME (); if (FRAME_INITIAL_P (f) || !FRAME_X_P (f)) return Qnil; - CHECK_TYPE_RANGED_INTEGER (int, x); - CHECK_TYPE_RANGED_INTEGER (int, y); + int xval = check_integer_range (x, INT_MIN, INT_MAX); + int yval = check_integer_range (y, INT_MIN, INT_MAX); block_input (); - XWarpPointer (FRAME_X_DISPLAY (f), None, DefaultRootWindow (FRAME_X_DISPLAY (f)), - 0, 0, 0, 0, XINT (x), XINT (y)); +#ifdef HAVE_XINPUT2 + int deviceid; + + deviceid = FRAME_DISPLAY_INFO (f)->client_pointer_device; + + if (FRAME_DISPLAY_INFO (f)->supports_xi2 + && deviceid != -1) + { + x_catch_errors_for_lisp (FRAME_DISPLAY_INFO (f)); + XIWarpPointer (FRAME_X_DISPLAY (f), deviceid, None, + FRAME_DISPLAY_INFO (f)->root_window, + 0, 0, 0, 0, xval, yval); + x_uncatch_errors_for_lisp (FRAME_DISPLAY_INFO (f)); + } + else +#endif + XWarpPointer (FRAME_X_DISPLAY (f), None, + FRAME_DISPLAY_INFO (f)->root_window, + 0, 0, 0, 0, xval, yval); unblock_input (); return Qnil; } +DEFUN ("x-begin-drag", Fx_begin_drag, Sx_begin_drag, 1, 6, 0, + doc: /* Begin dragging contents on FRAME, with targets TARGETS. +TARGETS is a list of strings, which defines the X selection targets +that will be available to the drop target. Block until the mouse +buttons are released, then return the action chosen by the target, or +`nil' if the drop was not accepted by the drop target. Dragging +starts when the mouse is pressed on FRAME, and the contents of the +selection `XdndSelection' will be sent to the X window underneath the +mouse pointer (the drop target) when the mouse button is released. + +ACTION is a symbol which tells the target what it should do, and can +be one of the following: + + - `XdndActionCopy', which means to copy the contents from the drag + source (FRAME) to the drop target. + + - `XdndActionMove', which means to first take the contents of + `XdndSelection', and to delete whatever was saved into that + selection afterwards. + +`XdndActionPrivate' is also a valid return value, and means that the +drop target chose to perform an unspecified or unknown action. + +The source is also expected to cooperate with the target to perform +the action chosen by the target. For example, callers should delete +the buffer text that was dragged if `XdndActionMove' is returned. + +There are also some other valid values of ACTION that depend on +details of both the drop target's implementation details and that of +Emacs. For that reason, they are not mentioned here. Consult +"Drag-and-Drop Protocol for the X Window System" for more details: +https://freedesktop.org/wiki/Specifications/XDND/. + +If RETURN-FRAME is non-nil, this function will return the frame if the +mouse pointer moves onto an Emacs frame, after first moving out of +FRAME. (This is not guaranteed to work on some systems.) If +RETURN-FRAME is the symbol `now', any frame underneath the mouse +pointer will be returned immediately. + +If ACTION is a list and not nil, its elements are assumed to be a cons +of (ITEM . STRING), where ITEM is the name of an action, and STRING is +a string describing ITEM to the user. The drop target is expected to +prompt the user to choose between any of the actions in the list. + +If ACTION is not specified or nil, `XdndActionCopy' is used +instead. + +If ALLOW-CURRENT-FRAME is not specified or nil, then the drop target +is allowed to be FRAME. Otherwise, no action will be taken if the +mouse buttons are released on top of FRAME. + +If FOLLOW-TOOLTIP is non-nil, any tooltip currently being displayed +will be moved to follow the mouse pointer while the drag is in +progress. Note that this does not work with system tooltips (tooltips +created when `use-system-tooltips' is non-nil). + +This function will sometimes return immediately if no mouse buttons +are currently held down. It should only be called when it is known +that mouse buttons are being held down, such as immediately after a +`down-mouse-1' (or similar) event. */) + (Lisp_Object targets, Lisp_Object action, Lisp_Object frame, + Lisp_Object return_frame, Lisp_Object allow_current_frame, + Lisp_Object follow_tooltip) +{ + struct frame *f = decode_window_system_frame (frame); + int ntargets = 0, nnames = 0; + char *target_names[2048]; + Atom *target_atoms; + Lisp_Object lval, original, targets_arg, tem, t1, t2; + Atom xaction; + Atom action_list[2048]; + char *name_list[2048]; + + USE_SAFE_ALLOCA; + + CHECK_LIST (targets); + original = targets; + targets_arg = targets; + + FOR_EACH_TAIL (targets) + { + CHECK_STRING (XCAR (targets)); + + if (ntargets < 2048) + { + SAFE_ALLOCA_STRING (target_names[ntargets], + XCAR (targets)); + ntargets++; + } + else + error ("Too many targets"); + } + + CHECK_LIST_END (targets, original); + + if (NILP (action) || EQ (action, QXdndActionCopy)) + xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionCopy; + else if (EQ (action, QXdndActionMove)) + xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionMove; + else if (EQ (action, QXdndActionLink)) + xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionLink; + else if (EQ (action, QXdndActionPrivate)) + xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionPrivate; + else if (EQ (action, QXdndActionAsk)) + xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionAsk; + else if (SYMBOLP (action)) + /* This is to accommodate non-standard DND protocols such as XDS + that are explicitly implemented by Emacs, and is not documented + for that reason. */ + xaction = symbol_to_x_atom (FRAME_DISPLAY_INFO (f), action); + else if (CONSP (action)) + { + xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionAsk; + original = action; + + CHECK_LIST (action); + FOR_EACH_TAIL (action) + { + tem = XCAR (action); + CHECK_CONS (tem); + t1 = XCAR (tem); + t2 = XCDR (tem); + CHECK_SYMBOL (t1); + CHECK_STRING (t2); + + if (nnames < 2048) + { + if (EQ (t1, QXdndActionCopy)) + action_list[nnames] = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionCopy; + else if (EQ (t1, QXdndActionMove)) + action_list[nnames] = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionMove; + else if (EQ (t1, QXdndActionLink)) + action_list[nnames] = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionLink; + else if (EQ (t1, QXdndActionAsk)) + action_list[nnames] = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionAsk; + else if (EQ (t1, QXdndActionPrivate)) + action_list[nnames] = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionPrivate; + else + signal_error ("Invalid drag-and-drop action", tem); + + SAFE_ALLOCA_STRING (name_list[nnames], + ENCODE_SYSTEM (t2)); + + nnames++; + } + else + error ("Too many actions"); + } + CHECK_LIST_END (action, original); + } + else + signal_error ("Invalid drag-and-drop action", action); + + target_atoms = SAFE_ALLOCA (ntargets * sizeof *target_atoms); + + /* Catch errors since interning lots of targets can potentially + generate a BadAlloc error. */ + x_catch_errors (FRAME_X_DISPLAY (f)); + XInternAtoms (FRAME_X_DISPLAY (f), target_names, + ntargets, False, target_atoms); + x_check_errors (FRAME_X_DISPLAY (f), + "Failed to intern target atoms: %s"); + x_uncatch_errors_after_check (); + + lval = x_dnd_begin_drag_and_drop (f, FRAME_DISPLAY_INFO (f)->last_user_time, + xaction, return_frame, action_list, + (const char **) &name_list, nnames, + !NILP (allow_current_frame), target_atoms, + ntargets, targets_arg, !NILP (follow_tooltip)); + + SAFE_FREE (); + return lval; +} + /************************************************************************ X Displays ************************************************************************/ @@ -5472,8 +7096,7 @@ visual_classes[] = the X function with the same name when that doesn't exist. */ int -XScreenNumberOfScreen (scr) - register Screen *scr; +XScreenNumberOfScreen (Screen *scr) { Display *dpy = scr->display; int i; @@ -5500,8 +7123,8 @@ select_visual (struct x_display_info *dpyinfo) /* See if a visual is specified. */ AUTO_STRING (visualClass, "visualClass"); AUTO_STRING (VisualClass, "VisualClass"); - Lisp_Object value = display_x_get_resource (dpyinfo, visualClass, - VisualClass, Qnil, Qnil); + Lisp_Object value = gui_display_get_resource (dpyinfo, visualClass, + VisualClass, Qnil, Qnil); if (STRINGP (value)) { @@ -5541,21 +7164,62 @@ select_visual (struct x_display_info *dpyinfo) SSDATA (ENCODE_SYSTEM (value))); dpyinfo->visual = vinfo.visual; + dpyinfo->visual_info = vinfo; } else { int n_visuals; XVisualInfo *vinfo, vinfo_template; - dpyinfo->visual = DefaultVisualOfScreen (screen); + vinfo_template.screen = XScreenNumberOfScreen (screen); + +#if !defined USE_X_TOOLKIT && !(defined USE_GTK && !defined HAVE_GTK3) \ + && defined HAVE_XRENDER + int i; + XRenderPictFormat *format; + + /* First attempt to find a visual with an alpha mask if + available. That information is only available when the + render extension is present, and we cannot do much with such + a visual if it isn't. */ + + if (dpyinfo->xrender_supported_p) + { + + vinfo = XGetVisualInfo (dpy, VisualScreenMask, + &vinfo_template, &n_visuals); + + for (i = 0; i < n_visuals; ++i) + { + format = XRenderFindVisualFormat (dpy, vinfo[i].visual); + + if (format && format->type == PictTypeDirect + && format->direct.alphaMask) + { + dpyinfo->n_planes = vinfo[i].depth; + dpyinfo->visual = vinfo[i].visual; + dpyinfo->visual_info = vinfo[i]; + dpyinfo->pict_format = format; + + XFree (vinfo); + return; + } + } + if (vinfo) + XFree (vinfo); + } +#endif /* !USE_X_TOOLKIT */ + + /* Visual with alpha channel (or the Render extension) not + available, fallback to default visual. */ + dpyinfo->visual = DefaultVisualOfScreen (screen); vinfo_template.visualid = XVisualIDFromVisual (dpyinfo->visual); - vinfo_template.screen = XScreenNumberOfScreen (screen); vinfo = XGetVisualInfo (dpy, VisualIDMask | VisualScreenMask, &vinfo_template, &n_visuals); if (n_visuals <= 0) fatal ("Can't get proper X visual info"); - + dpyinfo->visual_info = *vinfo; dpyinfo->n_planes = vinfo->depth; XFree (vinfo); } @@ -5586,8 +7250,6 @@ x_display_info_for_name (Lisp_Object name) if (dpyinfo == 0) error ("Cannot connect to X server %s", SDATA (name)); - XSETFASTINT (Vwindow_system_version, 11); - return dpyinfo; } @@ -5631,7 +7293,6 @@ An insecure way to solve the problem may be to use `xhost'.\n", error ("Cannot connect to X server %s", SDATA (display)); } - XSETFASTINT (Vwindow_system_version, 11); return Qnil; } @@ -5639,8 +7300,8 @@ DEFUN ("x-close-connection", Fx_close_connection, Sx_close_connection, 1, 1, 0, doc: /* Close the connection to TERMINAL's X server. For TERMINAL, specify a terminal object, a frame or a display name (a -string). If TERMINAL is nil, that stands for the selected frame's -terminal. */) +string). If TERMINAL is nil, that stands for the selected frame's terminal. +\(On MS Windows, this function does not accept terminal objects.) */) (Lisp_Object terminal) { struct x_display_info *dpyinfo = check_x_display_info (terminal); @@ -5668,7 +7329,7 @@ DEFUN ("x-display-list", Fx_display_list, Sx_display_list, 0, 0, 0, DEFUN ("x-synchronize", Fx_synchronize, Sx_synchronize, 1, 2, 0, doc: /* If ON is non-nil, report X errors as soon as the erring request is made. -This function only has an effect on X Windows. With MS Windows, it is +This function has an effect only on X Windows. With MS Windows, it is defined but does nothing. If ON is nil, allow buffering of requests. @@ -5682,7 +7343,7 @@ If TERMINAL is omitted or nil, that stands for the selected frame's display. */ { struct x_display_info *dpyinfo = check_x_display_info (terminal); - XSynchronize (dpyinfo->display, !EQ (on, Qnil)); + XSynchronize (dpyinfo->display, !NILP (on)); return Qnil; } @@ -5693,7 +7354,11 @@ void x_sync (struct frame *f) { block_input (); +#ifndef USE_XCB XSync (FRAME_X_DISPLAY (f), False); +#else + xcb_aux_sync (FRAME_DISPLAY_INFO (f)->xcb_connection); +#endif unblock_input (); } @@ -5703,7 +7368,7 @@ x_sync (struct frame *f) ***********************************************************************/ DEFUN ("x-change-window-property", Fx_change_window_property, - Sx_change_window_property, 2, 6, 0, + Sx_change_window_property, 2, 7, 0, doc: /* Change window property PROP to VALUE on the X window of FRAME. PROP must be a string. VALUE may be a string or a list of conses, numbers and/or strings. If an element in the list is a string, it is @@ -5711,35 +7376,67 @@ converted to an atom and the value of the atom is used. If an element is a cons, it is converted to a 32 bit number where the car is the 16 top bits and the cdr is the lower 16 bits. -FRAME nil or omitted means use the selected frame. -If TYPE is given and non-nil, it is the name of the type of VALUE. -If TYPE is not given or nil, the type is STRING. -FORMAT gives the size in bits of each element if VALUE is a list. -It must be one of 8, 16 or 32. -If VALUE is a string or FORMAT is nil or not given, FORMAT defaults to 8. -If OUTER-P is non-nil, the property is changed for the outer X window of -FRAME. Default is to change on the edit X window. */) +FRAME nil or omitted means use the selected frame. If TYPE is given +and non-nil, it is the name of the type of VALUE. If TYPE is not +given or nil, the type is STRING. + +FORMAT gives the size in bits of each element if VALUE is a list. It +must be one of 8, 16 or 32. + +If VALUE is a string or FORMAT is nil or not given, FORMAT defaults to +8. If OUTER-P is non-nil, the property is changed for the outer X +window of FRAME. Default is to change on the edit X window. + +If WINDOW-ID is non-nil, change the property of that window instead of +FRAME's X window; the number 0 denotes the root window. This argument +is separate from FRAME because window IDs are not unique across X +displays or screens on the same display, so FRAME provides context for +the window ID. + +If VALUE is a string and FORMAT is 32, then the format of VALUE is +system-specific. VALUE must contain unsigned integer data in native +endian-ness in multiples of the size of the C type 'long': the low 32 +bits of each such number are used as the value of each element of the +property. + +Wait for the request to complete and signal any error, unless +`x-fast-protocol-requests' is non-nil, in which case errors will be +silently ignored. */) (Lisp_Object prop, Lisp_Object value, Lisp_Object frame, - Lisp_Object type, Lisp_Object format, Lisp_Object outer_p) + Lisp_Object type, Lisp_Object format, Lisp_Object outer_p, + Lisp_Object window_id) { - struct frame *f = decode_window_system_frame (frame); + struct frame *f; Atom prop_atom; Atom target_type = XA_STRING; int element_format = 8; unsigned char *data; int nelements; - Window w; + Window target_window; + struct x_display_info *dpyinfo; +#ifdef USE_XCB + bool intern_prop; + bool intern_target; + xcb_intern_atom_cookie_t prop_atom_cookie; + xcb_intern_atom_cookie_t target_type_cookie; + xcb_intern_atom_reply_t *reply; + xcb_generic_error_t *generic_error; + bool rc; +#endif + + f = decode_window_system_frame (frame); + dpyinfo = FRAME_DISPLAY_INFO (f); CHECK_STRING (prop); if (! NILP (format)) { - CHECK_NUMBER (format); + CHECK_FIXNUM (format); - if (XINT (format) != 8 && XINT (format) != 16 - && XINT (format) != 32) + if (XFIXNUM (format) != 8 && XFIXNUM (format) != 16 + && XFIXNUM (format) != 32) error ("FORMAT must be one of 8, 16 or 32"); - element_format = XINT (format); + element_format = XFIXNUM (format); } if (CONSP (value)) @@ -5758,7 +7455,8 @@ FRAME. Default is to change on the edit X window. */) elsize = element_format == 32 ? sizeof (long) : element_format >> 3; data = xnmalloc (nelements, elsize); - x_fill_property_data (FRAME_X_DISPLAY (f), value, data, element_format); + x_fill_property_data (FRAME_X_DISPLAY (f), value, data, nelements, + element_format); } else { @@ -5776,49 +7474,157 @@ FRAME. Default is to change on the edit X window. */) nelements = SBYTES (value) / elsize; } + if (! NILP (window_id)) + { + CONS_TO_INTEGER (window_id, Window, target_window); + if (! target_window) + target_window = dpyinfo->root_window; + } + else + { + if (! NILP (outer_p)) + target_window = FRAME_OUTER_WINDOW (f); + else + target_window = FRAME_X_WINDOW (f); + } + block_input (); - prop_atom = XInternAtom (FRAME_X_DISPLAY (f), SSDATA (prop), False); +#ifndef USE_XCB + prop_atom = x_intern_cached_atom (dpyinfo, SSDATA (prop), + false); if (! NILP (type)) { CHECK_STRING (type); - target_type = XInternAtom (FRAME_X_DISPLAY (f), SSDATA (type), False); + target_type = x_intern_cached_atom (dpyinfo, SSDATA (type), + false); } +#else + rc = true; + intern_target = true; + intern_prop = true; + + prop_atom = x_intern_cached_atom (dpyinfo, SSDATA (prop), + true); + + if (prop_atom != None) + intern_prop = false; + else + prop_atom_cookie + = xcb_intern_atom (dpyinfo->xcb_connection, + 0, SBYTES (prop), SSDATA (prop)); - if (! NILP (outer_p)) w = FRAME_OUTER_WINDOW (f); - else w = FRAME_X_WINDOW (f); + if (!NILP (type)) + { + CHECK_STRING (type); - XChangeProperty (FRAME_X_DISPLAY (f), w, - prop_atom, target_type, element_format, PropModeReplace, - data, nelements); + target_type = x_intern_cached_atom (dpyinfo, SSDATA (type), + true); - if (CONSP (value)) xfree (data); + if (target_type) + intern_target = false; + else + target_type_cookie + = xcb_intern_atom (dpyinfo->xcb_connection, + 0, SBYTES (type), SSDATA (type)); + } - /* Make sure the property is set when we return. */ - XFlush (FRAME_X_DISPLAY (f)); - unblock_input (); + if (intern_prop) + { + reply = xcb_intern_atom_reply (dpyinfo->xcb_connection, + prop_atom_cookie, &generic_error); + + if (reply) + { + prop_atom = (Atom) reply->atom; + free (reply); + } + else + { + free (generic_error); + rc = false; + } + } + + if (!NILP (type) && intern_target) + { + reply = xcb_intern_atom_reply (dpyinfo->xcb_connection, + target_type_cookie, &generic_error); + if (reply) + { + target_type = (Atom) reply->atom; + free (reply); + } + else + { + free (generic_error); + rc = false; + } + } + + if (!rc) + error ("Failed to intern type or property atom"); +#endif + + x_catch_errors_for_lisp (dpyinfo); + + XChangeProperty (dpyinfo->display, target_window, + prop_atom, target_type, element_format, + PropModeReplace, data, nelements); + + if (CONSP (value)) + xfree (data); + + x_check_errors_for_lisp (dpyinfo, + "Couldn't change window property: %s"); + x_uncatch_errors_for_lisp (dpyinfo); + + unblock_input (); return value; } DEFUN ("x-delete-window-property", Fx_delete_window_property, - Sx_delete_window_property, 1, 2, 0, + Sx_delete_window_property, 1, 3, 0, doc: /* Remove window property PROP from X window of FRAME. -FRAME nil or omitted means use the selected frame. Value is PROP. */) - (Lisp_Object prop, Lisp_Object frame) +FRAME nil or omitted means use the selected frame. +If WINDOW-ID is non-nil, remove property from that window instead + of FRAME's X window; the number 0 denotes the root window. This + argument is separate from FRAME because window IDs are not unique + across X displays or screens on the same display, so FRAME provides + context for the window ID. + +Value is PROP. + +Wait for the request to complete and signal any error, unless +`x-fast-protocol-requests' is non-nil, in which case errors will be +silently ignored. */) + (Lisp_Object prop, Lisp_Object frame, Lisp_Object window_id) { struct frame *f = decode_window_system_frame (frame); + Window target_window = FRAME_X_WINDOW (f); Atom prop_atom; CHECK_STRING (prop); + + if (! NILP (window_id)) + { + CONS_TO_INTEGER (window_id, Window, target_window); + if (! target_window) + target_window = FRAME_DISPLAY_INFO (f)->root_window; + } + block_input (); - prop_atom = XInternAtom (FRAME_X_DISPLAY (f), SSDATA (prop), False); - XDeleteProperty (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), prop_atom); + prop_atom = x_intern_cached_atom (FRAME_DISPLAY_INFO (f), + SSDATA (prop), false); - /* Make sure the property is removed when we return. */ - XFlush (FRAME_X_DISPLAY (f)); - unblock_input (); + x_catch_errors_for_lisp (FRAME_DISPLAY_INFO (f)); + XDeleteProperty (FRAME_X_DISPLAY (f), target_window, prop_atom); + x_check_errors_for_lisp (FRAME_DISPLAY_INFO (f), + "Couldn't delete window property: %s"); + x_uncatch_errors_for_lisp (FRAME_DISPLAY_INFO (f)); + unblock_input (); return prop; } @@ -5907,18 +7713,19 @@ If FRAME is nil or omitted, use the selected frame. On X Windows, the following optional arguments are also accepted: If TYPE is nil or omitted, get the property as a string. -Otherwise TYPE is the name of the atom that denotes the type expected. -If SOURCE is non-nil, get the property on that window instead of from -FRAME. The number 0 denotes the root window. + Otherwise TYPE is the name of the atom that denotes the expected type. +If WINDOW-ID is non-nil, get the property of that window instead of + FRAME's X window; the number 0 denotes the root window. This argument + is separate from FRAME because window IDs are not unique across X + displays or screens on the same display, so FRAME provides context + for the window ID. If DELETE-P is non-nil, delete the property after retrieving it. -If VECTOR-RET-P is non-nil, don't return a string but a vector of values. - -On MS Windows, this function accepts but ignores those optional arguments. +If VECTOR-RET-P is non-nil, return a vector of values instead of a string. -Value is nil if FRAME hasn't a property with name PROP or if PROP has -no value of TYPE (always string in the MS Windows case). */) +Return value is nil if FRAME doesn't have a property with name PROP or +if PROP has no value of TYPE (always a string in the MS Windows case). */) (Lisp_Object prop, Lisp_Object frame, Lisp_Object type, - Lisp_Object source, Lisp_Object delete_p, Lisp_Object vector_ret_p) + Lisp_Object window_id, Lisp_Object delete_p, Lisp_Object vector_ret_p) { struct frame *f = decode_window_system_frame (frame); Atom prop_atom; @@ -5929,23 +7736,27 @@ no value of TYPE (always string in the MS Windows case). */) CHECK_STRING (prop); - if (! NILP (source)) + if (! NILP (window_id)) { - CONS_TO_INTEGER (source, Window, target_window); + CONS_TO_INTEGER (window_id, Window, target_window); if (! target_window) - target_window = FRAME_DISPLAY_INFO (f)->root_window; + target_window = FRAME_DISPLAY_INFO (f)->root_window; } block_input (); + x_catch_errors (FRAME_X_DISPLAY (f)); + if (STRINGP (type)) { if (strcmp ("AnyPropertyType", SSDATA (type)) == 0) target_type = AnyPropertyType; else - target_type = XInternAtom (FRAME_X_DISPLAY (f), SSDATA (type), False); + target_type = x_intern_cached_atom (FRAME_DISPLAY_INFO (f), + SSDATA (type), false); } - prop_atom = XInternAtom (FRAME_X_DISPLAY (f), SSDATA (prop), False); + prop_atom = x_intern_cached_atom (FRAME_DISPLAY_INFO (f), + SSDATA (prop), false); prop_value = x_window_property_intern (f, target_window, prop_atom, @@ -5955,7 +7766,7 @@ no value of TYPE (always string in the MS Windows case). */) &found); if (NILP (prop_value) && ! found - && NILP (source) + && NILP (window_id) && target_window != FRAME_OUTER_WINDOW (f)) { prop_value = x_window_property_intern (f, @@ -5967,6 +7778,9 @@ no value of TYPE (always string in the MS Windows case). */) &found); } + x_check_errors (FRAME_X_DISPLAY (f), + "Can't retrieve window property: %s"); + x_uncatch_errors_after_check (); unblock_input (); return prop_value; @@ -5976,17 +7790,20 @@ DEFUN ("x-window-property-attributes", Fx_window_property_attributes, Sx_window_ 1, 3, 0, doc: /* Retrieve metadata about window property PROP on FRAME. If FRAME is nil or omitted, use the selected frame. -If SOURCE is non-nil, get the property on that window instead of from -FRAME. The number 0 denotes the root window. +If WINDOW-ID is non-nil, get the property of that window instead of + FRAME's X window; the number 0 denotes the root window. This + argument is separate from FRAME because window IDs are not unique + across X displays or screens on the same display, so FRAME provides + context for the window ID. -Return value is nil if FRAME hasn't a property with name PROP. +Return value is nil if FRAME doesn't have a property named PROP. Otherwise, the return value is a vector with the following fields: 0. The property type, as an integer. The symbolic name of the type can be obtained with `x-get-atom-name'. 1. The format of each element; one of 8, 16, or 32. 2. The length of the property, in number of elements. */) - (Lisp_Object prop, Lisp_Object frame, Lisp_Object source) + (Lisp_Object prop, Lisp_Object frame, Lisp_Object window_id) { struct frame *f = decode_window_system_frame (frame); Window target_window = FRAME_X_WINDOW (f); @@ -6000,23 +7817,25 @@ Otherwise, the return value is a vector with the following fields: CHECK_STRING (prop); - if (! NILP (source)) + if (! NILP (window_id)) { - CONS_TO_INTEGER (source, Window, target_window); + CONS_TO_INTEGER (window_id, Window, target_window); if (! target_window) target_window = FRAME_DISPLAY_INFO (f)->root_window; } block_input (); - prop_atom = XInternAtom (FRAME_X_DISPLAY (f), SSDATA (prop), False); + x_catch_errors (FRAME_X_DISPLAY (f)); + prop_atom = x_intern_cached_atom (FRAME_DISPLAY_INFO (f), + SSDATA (prop), false); rc = XGetWindowProperty (FRAME_X_DISPLAY (f), target_window, prop_atom, 0, 0, False, AnyPropertyType, &actual_type, &actual_format, &actual_size, &bytes_remaining, &tmp_data); if (rc == Success /* no invalid params */ && actual_format == 0 /* but prop not found */ - && NILP (source) + && NILP (window_id) && target_window != FRAME_OUTER_WINDOW (f)) { /* analogous behavior to x-window-property: if property isn't found @@ -6033,37 +7852,135 @@ Otherwise, the return value is a vector with the following fields: { XFree (tmp_data); - prop_attr = make_uninit_vector (3); - ASET (prop_attr, 0, make_number (actual_type)); - ASET (prop_attr, 1, make_number (actual_format)); - ASET (prop_attr, 2, make_number (bytes_remaining / (actual_format >> 3))); + prop_attr = CALLN (Fvector, + make_fixnum (actual_type), + make_fixnum (actual_format), + make_fixnum (bytes_remaining / (actual_format >> 3))); } + x_check_errors (FRAME_X_DISPLAY (f), + "Can't retrieve window property: %s"); + x_uncatch_errors_after_check (); + unblock_input (); return prop_attr; } + +/*********************************************************************** + Coordinate management + ***********************************************************************/ + +DEFUN ("x-translate-coordinates", Fx_translate_coordinates, + Sx_translate_coordinates, + 1, 5, 0, doc: /* Translate coordinates from FRAME. +Translate the given coordinates SOURCE-X and SOURCE-Y from +SOURCE-WINDOW's coordinate space to that of DEST-WINDOW, on FRAME. + +If SOURCE-X and SOURCE-Y are nil, use 0 instead. + +FRAME can either be a terminal or a frame. If nil, it defaults to the +selected frame. SOURCE-WINDOW must be an X window ID, 0 (which means +to use the root window), or nil, which means to use FRAME's inner +window. DEST-WINDOW must be another X window ID, or nil (which means +to use the root window). + +Return a list of (X Y CHILD) if the given coordinates are on the same +screen, or nil otherwise, where X and Y are the coordinates in +DEST-WINDOW's coordinate space, and CHILD is the window ID of any +mapped child in DEST-WINDOW at those coordinates, or nil if there is +no such window. */) + (Lisp_Object frame, Lisp_Object source_window, + Lisp_Object dest_window, Lisp_Object source_x, + Lisp_Object source_y) +{ + struct x_display_info *dpyinfo; + struct frame *source_frame; + int dest_x, dest_y; + Window child_return, src, dest; + Bool rc; + + dpyinfo = check_x_display_info (frame); + dest_x = 0; + dest_y = 0; + + if (!NILP (source_x)) + { + CHECK_FIXNUM (source_x); + dest_x = XFIXNUM (source_x); + } + + if (!NILP (source_y)) + { + CHECK_FIXNUM (source_y); + dest_y = XFIXNUM (source_y); + } + + if (!NILP (source_window)) + CONS_TO_INTEGER (source_window, Window, src); + else + { + source_frame = decode_window_system_frame (frame); + src = FRAME_X_WINDOW (source_frame); + } + + if (!src) + src = dpyinfo->root_window; + + if (!NILP (dest_window)) + CONS_TO_INTEGER (dest_window, Window, dest); + else + dest = dpyinfo->root_window; + + block_input (); + x_catch_errors (dpyinfo->display); + rc = XTranslateCoordinates (dpyinfo->display, src, dest, + dest_x, dest_y, &dest_x, &dest_y, + &child_return); + x_check_errors (dpyinfo->display, + "Couldn't translate coordinates: %s"); + x_uncatch_errors_after_check (); + unblock_input (); + + if (!rc) + return Qnil; + + return list3 (make_int (dest_x), + make_int (dest_y), + (child_return != None + ? make_uint (child_return) + : Qnil)); +} + /*********************************************************************** Tool tips ***********************************************************************/ static void compute_tip_xy (struct frame *, Lisp_Object, Lisp_Object, - Lisp_Object, int, int, int *, int *); - -/* The frame of a currently visible tooltip. */ + Lisp_Object, int, int, int *, int *); +/* The frame of the currently visible tooltip, or nil if none. */ Lisp_Object tip_frame; -/* If non-nil, a timer started that hides the last tooltip when it - fires. */ +/* The window-system window corresponding to the frame of the + currently visible tooltip. */ +Window tip_window; + +/* The X and Y deltas of the last call to `x-show-tip'. */ +Lisp_Object tip_dx, tip_dy; +/* A timer that hides or deletes the currently visible tooltip when it + fires. */ static Lisp_Object tip_timer; -Window tip_window; -/* If non-nil, a vector of 3 elements containing the last args - with which x-show-tip was called. See there. */ +/* STRING argument of last `x-show-tip' call. */ +static Lisp_Object tip_last_string; -static Lisp_Object last_show_tip_args; +/* Normalized FRAME argument of last `x-show-tip' call. */ +static Lisp_Object tip_last_frame; + +/* PARMS argument of last `x-show-tip' call. */ +static Lisp_Object tip_last_parms; static void @@ -6084,7 +8001,7 @@ unwind_create_tip_frame (Lisp_Object frame) PARMS is a list of frame parameters. TEXT is the string to display in the tip frame. Value is the frame. - Note that functions called here, esp. x_default_parameter can + Note that functions called here, esp. gui_default_parameter can signal errors, for instance when a specified color name is undefined. We have to make sure that we're in a consistent state when this happens. */ @@ -6095,10 +8012,8 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) struct frame *f; Lisp_Object frame; Lisp_Object name; - int width, height; - ptrdiff_t count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); bool face_change_before = face_change; - int x_width = 0, x_height = 0; if (!dpyinfo->terminal->name) error ("Terminal is not live, can't create new frames on it"); @@ -6106,9 +8021,10 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) parms = Fcopy_alist (parms); /* Get the name of the frame to use for resource lookup. */ - name = x_get_arg (dpyinfo, parms, Qname, "name", "Name", RES_TYPE_STRING); + name = gui_display_get_arg (dpyinfo, parms, Qname, "name", "Name", + RES_TYPE_STRING); if (!STRINGP (name) - && !EQ (name, Qunbound) + && !BASE_EQ (name, Qunbound) && !NILP (name)) error ("Invalid frame name--not a string or nil"); @@ -6137,6 +8053,7 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) f->output_data.x->white_relief.pixel = -1; f->output_data.x->black_relief.pixel = -1; + f->tooltip = true; fset_icon_name (f, Qnil); FRAME_DISPLAY_INFO (f) = dpyinfo; f->output_data.x->parent_desc = FRAME_DISPLAY_INFO (f)->root_window; @@ -6174,7 +8091,7 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) /* Set the name; the functions to which we pass f expect the name to be set. */ - if (EQ (name, Qunbound) || NILP (name)) + if (BASE_EQ (name, Qunbound) || NILP (name)) { fset_name (f, build_string (dpyinfo->x_id_name)); f->explicit_name = false; @@ -6189,16 +8106,20 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) #ifdef USE_CAIRO register_font_driver (&ftcrfont_driver, f); +#ifdef HAVE_HARFBUZZ + register_font_driver (&ftcrhbfont_driver, f); +#endif /* HAVE_HARFBUZZ */ #else - register_font_driver (&xfont_driver, f); #ifdef HAVE_FREETYPE #ifdef HAVE_XFT register_font_driver (&xftfont_driver, f); -#else /* not HAVE_XFT */ - register_font_driver (&ftxfont_driver, f); +#ifdef HAVE_HARFBUZZ + register_font_driver (&xfthbfont_driver, f); +#endif #endif /* not HAVE_XFT */ #endif /* HAVE_FREETYPE */ #endif /* not USE_CAIRO */ + register_font_driver (&xfont_driver, f); image_cache_refcount = FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0; @@ -6206,68 +8127,62 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) dpyinfo_refcount = dpyinfo->reference_count; #endif /* GLYPH_DEBUG */ - x_default_parameter (f, parms, Qfont_backend, Qnil, - "fontBackend", "FontBackend", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qfont_backend, Qnil, + "fontBackend", "FontBackend", RES_TYPE_STRING); /* Extract the window parameters from the supplied values that are needed to determine window geometry. */ x_default_font_parameter (f, parms); - x_default_parameter (f, parms, Qborder_width, make_number (0), - "borderWidth", "BorderWidth", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qborder_width, make_fixnum (0), + "borderWidth", "BorderWidth", RES_TYPE_NUMBER); - /* This defaults to 2 in order to match xterm. We recognize either + /* This defaults to 1 in order to match xterm. We recognize either internalBorderWidth or internalBorder (which is what xterm calls it). */ if (NILP (Fassq (Qinternal_border_width, parms))) { Lisp_Object value; - value = x_get_arg (dpyinfo, parms, Qinternal_border_width, - "internalBorder", "internalBorder", RES_TYPE_NUMBER); - if (! EQ (value, Qunbound)) + value = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width, + "internalBorder", "internalBorder", + RES_TYPE_NUMBER); + if (! BASE_EQ (value, Qunbound)) parms = Fcons (Fcons (Qinternal_border_width, value), parms); } - x_default_parameter (f, parms, Qinternal_border_width, make_number (1), - "internalBorderWidth", "internalBorderWidth", - RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qright_divider_width, make_number (0), - NULL, NULL, RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qbottom_divider_width, make_number (0), - NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qinternal_border_width, make_fixnum (1), + "internalBorderWidth", "internalBorderWidth", + RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0), + NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0), + NULL, NULL, RES_TYPE_NUMBER); /* Also do the stuff which must be set before the window exists. */ - x_default_parameter (f, parms, Qforeground_color, build_string ("black"), - "foreground", "Foreground", RES_TYPE_STRING); - x_default_parameter (f, parms, Qbackground_color, build_string ("white"), - "background", "Background", RES_TYPE_STRING); - x_default_parameter (f, parms, Qmouse_color, build_string ("black"), - "pointerColor", "Foreground", RES_TYPE_STRING); - x_default_parameter (f, parms, Qcursor_color, build_string ("black"), - "cursorColor", "Foreground", RES_TYPE_STRING); - x_default_parameter (f, parms, Qborder_color, build_string ("black"), - "borderColor", "BorderColor", RES_TYPE_STRING); - x_default_parameter (f, parms, Qno_special_glyphs, Qnil, - NULL, NULL, RES_TYPE_BOOLEAN); - - /* Init faces before x_default_parameter is called for the - scroll-bar-width parameter because otherwise we end up in - init_iterator with a null face cache, which should not happen. */ - init_frame_faces (f); - - f->output_data.x->parent_desc = FRAME_DISPLAY_INFO (f)->root_window; - - x_figure_window_size (f, parms, false, &x_width, &x_height); + gui_default_parameter (f, parms, Qforeground_color, build_string ("black"), + "foreground", "Foreground", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qbackground_color, build_string ("white"), + "background", "Background", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qmouse_color, build_string ("black"), + "pointerColor", "Foreground", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qcursor_color, build_string ("black"), + "cursorColor", "Foreground", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qborder_color, build_string ("black"), + "borderColor", "BorderColor", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qno_special_glyphs, Qnil, + NULL, NULL, RES_TYPE_BOOLEAN); { +#ifndef USE_XCB XSetWindowAttributes attrs; unsigned long mask; Atom type = FRAME_DISPLAY_INFO (f)->Xatom_net_window_type_tooltip; block_input (); - mask = CWBackPixel | CWOverrideRedirect | CWEventMask | CWCursor; + mask = (CWBackPixel | CWOverrideRedirect | CWEventMask + | CWCursor | CWColormap | CWBorderPixel); if (DoesSaveUnders (dpyinfo->screen)) mask |= CWSaveUnder; @@ -6277,9 +8192,11 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) attrs.override_redirect = True; attrs.save_under = True; attrs.background_pixel = FRAME_BACKGROUND_PIXEL (f); + attrs.colormap = FRAME_X_COLORMAP (f); attrs.cursor = f->output_data.x->current_cursor = f->output_data.x->text_cursor; + attrs.border_pixel = f->output_data.x->border_pixel; /* Arrange for getting MapNotify and UnmapNotify events. */ attrs.event_mask = StructureNotifyMask; tip_window @@ -6290,7 +8207,8 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) 0, 0, 1, 1, /* Border. */ f->border_width, - CopyFromParent, InputOutput, CopyFromParent, + dpyinfo->n_planes, InputOutput, + FRAME_X_VISUAL (f), mask, &attrs); initial_set_up_x_back_buffer (f); XChangeProperty (FRAME_X_DISPLAY (f), tip_window, @@ -6298,27 +8216,80 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) XA_ATOM, 32, PropModeReplace, (unsigned char *)&type, 1); unblock_input (); +#else + uint32_t value_list[6]; + xcb_atom_t net_wm_window_type_tooltip + = (xcb_atom_t) dpyinfo->Xatom_net_window_type_tooltip; + xcb_visualid_t visual_id + = (xcb_visualid_t) XVisualIDFromVisual (FRAME_X_VISUAL (f)); + + f->output_data.x->current_cursor = f->output_data.x->text_cursor; + /* Values are set in the order of their enumeration in `enum + xcb_cw_t'. */ + value_list[0] = FRAME_BACKGROUND_PIXEL (f); + value_list[1] = f->output_data.x->border_pixel; + value_list[2] = true; + value_list[3] = XCB_EVENT_MASK_STRUCTURE_NOTIFY; + value_list[4] = (xcb_colormap_t) FRAME_X_COLORMAP (f); + value_list[5] = (xcb_cursor_t) f->output_data.x->text_cursor; + + block_input (); + tip_window + = FRAME_X_WINDOW (f) + = (Window) xcb_generate_id (dpyinfo->xcb_connection); + + xcb_create_window (dpyinfo->xcb_connection, + dpyinfo->n_planes, + (xcb_window_t) tip_window, + (xcb_window_t) dpyinfo->root_window, + 0, 0, 1, 1, f->border_width, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + visual_id, + (XCB_CW_BACK_PIXEL + | XCB_CW_BORDER_PIXEL + | XCB_CW_OVERRIDE_REDIRECT + | XCB_CW_EVENT_MASK + | XCB_CW_COLORMAP + | XCB_CW_CURSOR), + &value_list); + + xcb_change_property (dpyinfo->xcb_connection, + XCB_PROP_MODE_REPLACE, + (xcb_window_t) tip_window, + (xcb_atom_t) dpyinfo->Xatom_net_window_type, + (xcb_atom_t) dpyinfo->Xatom_ATOM, + 32, 1, &net_wm_window_type_tooltip); + + initial_set_up_x_back_buffer (f); + unblock_input (); +#endif } + /* Init faces before gui_default_parameter is called for the + scroll-bar-width parameter because otherwise we end up in + init_iterator with a null face cache, which should not happen. */ + init_frame_faces (f); + + gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil, + "inhibitDoubleBuffering", "InhibitDoubleBuffering", + RES_TYPE_BOOLEAN); + + gui_figure_window_size (f, parms, false, false); + + f->output_data.x->parent_desc = FRAME_DISPLAY_INFO (f)->root_window; + x_make_gc (f); - x_default_parameter (f, parms, Qauto_raise, Qnil, - "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN); - x_default_parameter (f, parms, Qauto_lower, Qnil, - "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN); - x_default_parameter (f, parms, Qcursor_type, Qbox, - "cursorType", "CursorType", RES_TYPE_SYMBOL); - x_default_parameter (f, parms, Qalpha, Qnil, - "alpha", "Alpha", RES_TYPE_NUMBER); - - /* Dimensions, especially FRAME_LINES (f), must be done via change_frame_size. - Change will not be effected unless different from the current - FRAME_LINES (f). */ - width = FRAME_COLS (f); - height = FRAME_LINES (f); - SET_FRAME_COLS (f, 0); - SET_FRAME_LINES (f, 0); - change_frame_size (f, width, height, true, false, false, false); + gui_default_parameter (f, parms, Qauto_raise, Qnil, + "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qauto_lower, Qnil, + "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qcursor_type, Qbox, + "cursorType", "CursorType", RES_TYPE_SYMBOL); + gui_default_parameter (f, parms, Qalpha, Qnil, + "alpha", "Alpha", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qalpha_background, Qnil, + "alphaBackground", "AlphaBackground", RES_TYPE_NUMBER); /* Add `tooltip' frame parameter's default value. */ if (NILP (Fframe_parameter (frame, Qtooltip))) @@ -6328,7 +8299,7 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) } /* FIXME - can this be done in a similar way to normal frames? - https://lists.gnu.org/archive/html/emacs-devel/2007-10/msg00641.html */ + https://lists.gnu.org/r/emacs-devel/2007-10/msg00641.html */ /* Set the `display-type' frame parameter before setting up faces. */ { @@ -6336,11 +8307,11 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) if (FRAME_DISPLAY_INFO (f)->n_planes == 1) disptype = Qmono; - else if (FRAME_DISPLAY_INFO (f)->visual->class == GrayScale - || FRAME_DISPLAY_INFO (f)->visual->class == StaticGray) - disptype = intern ("grayscale"); + else if (FRAME_X_VISUAL_INFO (f)->class == GrayScale + || FRAME_X_VISUAL_INFO (f)->class == StaticGray) + disptype = Qgrayscale; else - disptype = intern ("color"); + disptype = Qcolor; if (NILP (Fframe_parameter (frame, Qdisplay_type))) { @@ -6355,7 +8326,7 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) Frame parameters may be changed if .Xdefaults contains specifications for the default font. For example, if there is an `Emacs.default.attributeBackground: pink', the `background-color' - attribute of the frame get's set, which let's the internal border + attribute of the frame gets set, which let's the internal border of the tooltip frame appear in pink. Prevent this. */ { Lisp_Object bg = Fframe_parameter (frame, Qbackground_color); @@ -6380,7 +8351,9 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) below. And the frame needs to be on Vframe_list or making it visible won't work. */ Vframe_list = Fcons (frame, Vframe_list); - f->can_x_set_window_size = true; + f->can_set_window_size = true; + adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), + 0, true, Qtip_frame); /* Setting attributes of faces of the tooltip frame from resources and similar will set face_change, which leads to the clearing of @@ -6401,7 +8374,9 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) the display in *ROOT_X, and *ROOT_Y. */ static void -compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx, Lisp_Object dy, int width, int height, int *root_x, int *root_y) +compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx, + Lisp_Object dy, int width, int height, int *root_x, + int *root_y) { Lisp_Object left, top, right, bottom; int win_x, win_y; @@ -6417,17 +8392,17 @@ compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx, Lisp_Object /* Move the tooltip window where the mouse pointer is. Resize and show it. */ - if ((!INTEGERP (left) && !INTEGERP (right)) - || (!INTEGERP (top) && !INTEGERP (bottom))) + if ((!FIXNUMP (left) && !FIXNUMP (right)) + || (!FIXNUMP (top) && !FIXNUMP (bottom))) { Lisp_Object frame, attributes, monitor, geometry; block_input (); - XQueryPointer (FRAME_X_DISPLAY (f), FRAME_DISPLAY_INFO (f)->root_window, - &root, &child, root_x, root_y, &win_x, &win_y, &pmask); + x_query_pointer (FRAME_X_DISPLAY (f), FRAME_DISPLAY_INFO (f)->root_window, + &root, &child, root_x, root_y, &win_x, &win_y, &pmask); unblock_input (); - XSETFRAME(frame, f); + XSETFRAME (frame, f); attributes = Fx_display_monitor_attributes_list (frame); /* Try to determine the monitor where the mouse pointer is and @@ -6438,15 +8413,17 @@ compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx, Lisp_Object geometry = Fassq (Qgeometry, monitor); if (CONSP (geometry)) { - min_x = XINT (Fnth (make_number (1), geometry)); - min_y = XINT (Fnth (make_number (2), geometry)); - max_x = min_x + XINT (Fnth (make_number (3), geometry)); - max_y = min_y + XINT (Fnth (make_number (4), geometry)); + min_x = XFIXNUM (Fnth (make_fixnum (1), geometry)); + min_y = XFIXNUM (Fnth (make_fixnum (2), geometry)); + max_x = min_x + XFIXNUM (Fnth (make_fixnum (3), geometry)); + max_y = min_y + XFIXNUM (Fnth (make_fixnum (4), geometry)); + if (min_x <= *root_x && *root_x < max_x && min_y <= *root_y && *root_y < max_y) { break; } + max_y = -1; } @@ -6456,7 +8433,7 @@ compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx, Lisp_Object /* It was not possible to determine the monitor's geometry, so we assign some sane defaults here: */ - if ( max_y < 0 ) + if (max_y < 0) { min_x = 0; min_y = 0; @@ -6464,41 +8441,53 @@ compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx, Lisp_Object max_y = x_display_pixel_height (FRAME_DISPLAY_INFO (f)); } - if (INTEGERP (top)) - *root_y = XINT (top); - else if (INTEGERP (bottom)) - *root_y = XINT (bottom) - height; - else if (*root_y + XINT (dy) <= min_y) + if (FIXNUMP (top)) + *root_y = XFIXNUM (top); + else if (FIXNUMP (bottom)) + *root_y = XFIXNUM (bottom) - height; + else if (*root_y + XFIXNUM (dy) <= min_y) *root_y = min_y; /* Can happen for negative dy */ - else if (*root_y + XINT (dy) + height <= max_y) + else if (*root_y + XFIXNUM (dy) + height <= max_y) /* It fits below the pointer */ - *root_y += XINT (dy); - else if (height + XINT (dy) + min_y <= *root_y) + *root_y += XFIXNUM (dy); + else if (height + XFIXNUM (dy) + min_y <= *root_y) /* It fits above the pointer. */ - *root_y -= height + XINT (dy); + *root_y -= height + XFIXNUM (dy); else /* Put it on the top. */ *root_y = min_y; - if (INTEGERP (left)) - *root_x = XINT (left); - else if (INTEGERP (right)) - *root_x = XINT (right) - width; - else if (*root_x + XINT (dx) <= min_x) + if (FIXNUMP (left)) + *root_x = XFIXNUM (left); + else if (FIXNUMP (right)) + *root_x = XFIXNUM (right) - width; + else if (*root_x + XFIXNUM (dx) <= min_x) *root_x = 0; /* Can happen for negative dx */ - else if (*root_x + XINT (dx) + width <= max_x) + else if (*root_x + XFIXNUM (dx) + width <= max_x) /* It fits to the right of the pointer. */ - *root_x += XINT (dx); - else if (width + XINT (dx) + min_x <= *root_x) + *root_x += XFIXNUM (dx); + else if (width + XFIXNUM (dx) + min_x <= *root_x) /* It fits to the left of the pointer. */ - *root_x -= width + XINT (dx); + *root_x -= width + XFIXNUM (dx); else /* Put it left justified on the screen -- it ought to fit that way. */ *root_x = min_x; } -/* Hide tooltip. Delete its frame if DELETE is true. */ +/** + * x_hide_tip: + * + * Hide currently visible tooltip and cancel its timer. + * + * If GTK+ system tooltips are used, this will try to hide the tooltip + * referenced by the x_output structure of tooltip_last_frame. For + * Emacs tooltips this will try to make tooltip_frame invisible (if + * DELETE is false) or delete tooltip_frame (if DELETE is true). + * + * Return Qt if the tooltip was either deleted or made invisible, Qnil + * otherwise. + */ static Lisp_Object x_hide_tip (bool delete) { @@ -6508,75 +8497,119 @@ x_hide_tip (bool delete) tip_timer = Qnil; } - - if (NILP (tip_frame) - || (!delete && FRAMEP (tip_frame) +#ifdef USE_GTK + /* Any GTK+ system tooltip can be found via the x_output structure + of tip_last_frame, provided that frame is still live. Any Emacs + tooltip is found via the tip_frame variable. Note that the + current value of use_system_tooltips might not be the same as + used for the tooltip we have to hide, see Bug#30399. */ + if ((NILP (tip_last_frame) && NILP (tip_frame)) + || (!use_system_tooltips + && !delete + && !NILP (tip_frame) + && FRAME_LIVE_P (XFRAME (tip_frame)) && !FRAME_VISIBLE_P (XFRAME (tip_frame)))) + /* Either there's no tooltip to hide or it's an already invisible + Emacs tooltip and we don't want to change its type. Return + quickly. */ return Qnil; else { - ptrdiff_t count; Lisp_Object was_open = Qnil; - count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); specbind (Qinhibit_redisplay, Qt); specbind (Qinhibit_quit, Qt); -#ifdef USE_GTK - { - /* When using system tooltip, tip_frame is the Emacs frame on - which the tip is shown. */ - struct frame *f = XFRAME (tip_frame); + /* Try to hide the GTK+ system tip first. */ + if (!NILP (tip_last_frame)) + { + struct frame *f = XFRAME (tip_last_frame); - if (FRAME_LIVE_P (f) && xg_hide_tooltip (f)) - { - tip_frame = Qnil; - was_open = Qt; - } - } -#endif + if (FRAME_LIVE_P (f)) + { + if (xg_hide_tooltip (f)) + was_open = Qt; + } + } + + /* When using GTK+ system tooltips (compare Bug#41200) reset + tip_last_frame. It will be reassigned when showing the next + GTK+ system tooltip. */ + if (use_system_tooltips) + tip_last_frame = Qnil; - if (FRAMEP (tip_frame)) + /* Now look whether there's an Emacs tip around. */ + if (!NILP (tip_frame)) { - if (delete) + struct frame *f = XFRAME (tip_frame); + + if (FRAME_LIVE_P (f)) { - delete_frame (tip_frame, Qnil); - tip_frame = Qnil; + if (delete || use_system_tooltips) + { + /* Delete the Emacs tooltip frame when DELETE is true + or we change the tooltip type from an Emacs one to + a GTK+ system one. */ + delete_frame (tip_frame, Qnil); + tip_frame = Qnil; + } + else + x_make_frame_invisible (f); + + was_open = Qt; } else - x_make_frame_invisible (XFRAME (tip_frame)); + tip_frame = Qnil; + } + else + tip_frame = Qnil; - was_open = Qt; + return unbind_to (count, was_open); + } +#else /* not USE_GTK */ + if (NILP (tip_frame) + || (!delete + && !NILP (tip_frame) + && FRAME_LIVE_P (XFRAME (tip_frame)) + && !FRAME_VISIBLE_P (XFRAME (tip_frame)))) + return Qnil; + else + { + Lisp_Object was_open = Qnil; -#ifdef USE_LUCID - /* Bloodcurdling hack alert: The Lucid menu bar widget's - redisplay procedure is not called when a tip frame over - menu items is unmapped. Redisplay the menu manually... */ - { - Widget w; - struct frame *f = SELECTED_FRAME (); - if (FRAME_X_P (f) && FRAME_LIVE_P (f)) - { - w = f->output_data.x->menubar_widget; - - if (!DoesSaveUnders (FRAME_DISPLAY_INFO (f)->screen) - && w != NULL) - { - block_input (); - xlwmenu_redisplay (w); - unblock_input (); - } - } - } -#endif /* USE_LUCID */ + specpdl_ref count = SPECPDL_INDEX (); + specbind (Qinhibit_redisplay, Qt); + specbind (Qinhibit_quit, Qt); + + if (!NILP (tip_frame)) + { + struct frame *f = XFRAME (tip_frame); + + if (FRAME_LIVE_P (f)) + { + if (delete) + { + delete_frame (tip_frame, Qnil); + tip_frame = Qnil; + } + else + x_make_frame_invisible (XFRAME (tip_frame)); + + was_open = Qt; + } + else + tip_frame = Qnil; } else tip_frame = Qnil; return unbind_to (count, was_open); } +#endif /* USE_GTK */ } + DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, doc: /* Show STRING in a "tooltip" window on frame FRAME. A tooltip window is a small X window displaying a string. @@ -6589,7 +8622,8 @@ PARMS is an optional list of frame parameters which can be used to change the tooltip's appearance. Automatically hide the tooltip after TIMEOUT seconds. TIMEOUT nil -means use the default timeout of 5 seconds. +means use the default timeout from the `x-show-tooltip-timeout' +variable. If the list of frame parameters PARMS contains a `left' parameter, display the tooltip at that x-position. If the list of frame parameters @@ -6607,7 +8641,8 @@ with offset DY added (default is -10). A tooltip's maximum size is specified by `x-max-tooltip-size'. Text larger than the specified size is clipped. */) - (Lisp_Object string, Lisp_Object frame, Lisp_Object parms, Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy) + (Lisp_Object string, Lisp_Object frame, Lisp_Object parms, + Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy) { struct frame *f, *tip_f; struct window *w; @@ -6616,9 +8651,15 @@ Text larger than the specified size is clipped. */) struct text_pos pos; int width, height; int old_windows_or_buffers_changed = windows_or_buffers_changed; - ptrdiff_t count = SPECPDL_INDEX (); - ptrdiff_t count_1; - Lisp_Object window, size; + specpdl_ref count = SPECPDL_INDEX (); + Lisp_Object window, size, tip_buf; + Window child; + XWindowAttributes child_attrs; + int dest_x_return, dest_y_return; + bool displayed; +#ifdef ENABLE_CHECKING + struct glyph_row *row, *end; +#endif AUTO_STRING (tip, " *tip*"); specbind (Qinhibit_redisplay, Qt); @@ -6627,24 +8668,29 @@ Text larger than the specified size is clipped. */) if (SCHARS (string) == 0) string = make_unibyte_string (" ", 1); + if (NILP (frame)) + frame = selected_frame; f = decode_window_system_frame (frame); + if (NILP (timeout)) - timeout = make_number (5); - else - CHECK_NATNUM (timeout); + timeout = Vx_show_tooltip_timeout; + CHECK_FIXNAT (timeout); if (NILP (dx)) - dx = make_number (5); + dx = make_fixnum (5); else - CHECK_NUMBER (dx); + CHECK_FIXNUM (dx); if (NILP (dy)) - dy = make_number (-10); + dy = make_fixnum (-10); else - CHECK_NUMBER (dy); + CHECK_FIXNUM (dy); + + tip_dx = dx; + tip_dy = dy; #ifdef USE_GTK - if (x_gtk_use_system_tooltips) + if (use_system_tooltips) { bool ok; @@ -6657,36 +8703,28 @@ Text larger than the specified size is clipped. */) { compute_tip_xy (f, parms, dx, dy, width, height, &root_x, &root_y); xg_show_tooltip (f, root_x, root_y); - /* This is used in Fx_hide_tip. */ - XSETFRAME (tip_frame, f); + tip_last_frame = frame; } + unblock_input (); if (ok) goto start_timer; } #endif /* USE_GTK */ - if (NILP (last_show_tip_args)) - last_show_tip_args = Fmake_vector (make_number (3), Qnil); - - if (FRAMEP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame))) + if (!NILP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame))) { - Lisp_Object last_string = AREF (last_show_tip_args, 0); - Lisp_Object last_frame = AREF (last_show_tip_args, 1); - Lisp_Object last_parms = AREF (last_show_tip_args, 2); - if (FRAME_VISIBLE_P (XFRAME (tip_frame)) - && EQ (frame, last_frame) - && !NILP (Fequal_including_properties (last_string, string)) - && !NILP (Fequal (last_parms, parms))) + && (FRAME_X_DISPLAY (XFRAME (frame)) + == FRAME_X_DISPLAY (XFRAME (tip_last_frame))) + && !NILP (Fequal_including_properties (tip_last_string, string)) + && !NILP (Fequal (tip_last_parms, parms))) { /* Only DX and DY have changed. */ tip_f = XFRAME (tip_frame); if (!NILP (tip_timer)) { - Lisp_Object timer = tip_timer; - + call1 (Qcancel_timer, tip_timer); tip_timer = Qnil; - call1 (Qcancel_timer, timer); } block_input (); @@ -6698,15 +8736,14 @@ Text larger than the specified size is clipped. */) goto start_timer; } - else if (tooltip_reuse_hidden_frame && EQ (frame, last_frame)) + else if (tooltip_reuse_hidden_frame && BASE_EQ (frame, tip_last_frame)) { bool delete = false; Lisp_Object tail, elt, parm, last; /* Check if every parameter in PARMS has the same value in - last_parms unless it should be ignored by means of - Vtooltip_reuse_hidden_frame_parameters. This may destruct - last_parms which, however, will be recreated below. */ + tip_last_parms. This may destruct tip_last_parms which, + however, will be recreated below. */ for (tail = parms; CONSP (tail); tail = XCDR (tail)) { elt = XCAR (tail); @@ -6716,7 +8753,7 @@ Text larger than the specified size is clipped. */) if (!EQ (parm, Qleft) && !EQ (parm, Qtop) && !EQ (parm, Qright) && !EQ (parm, Qbottom)) { - last = Fassq (parm, last_parms); + last = Fassq (parm, tip_last_parms); if (NILP (Fequal (Fcdr (elt), Fcdr (last)))) { /* We lost, delete the old tooltip. */ @@ -6724,17 +8761,18 @@ Text larger than the specified size is clipped. */) break; } else - last_parms = call2 (Qassq_delete_all, parm, last_parms); + tip_last_parms = + call2 (Qassq_delete_all, parm, tip_last_parms); } else - last_parms = call2 (Qassq_delete_all, parm, last_parms); + tip_last_parms = + call2 (Qassq_delete_all, parm, tip_last_parms); } - /* Now check if every parameter in what is left of last_parms - with a non-nil value has an association in PARMS unless it - should be ignored by means of - Vtooltip_reuse_hidden_frame_parameters. */ - for (tail = last_parms; CONSP (tail); tail = XCDR (tail)) + /* Now check if every parameter in what is left of + tip_last_parms with a non-nil value has an association in + PARMS. */ + for (tail = tip_last_parms; CONSP (tail); tail = XCDR (tail)) { elt = XCAR (tail); parm = Fcar (elt); @@ -6755,19 +8793,19 @@ Text larger than the specified size is clipped. */) else x_hide_tip (true); - ASET (last_show_tip_args, 0, string); - ASET (last_show_tip_args, 1, frame); - ASET (last_show_tip_args, 2, parms); + tip_last_frame = frame; + tip_last_string = string; + tip_last_parms = parms; - if (!FRAMEP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame))) + if (NILP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame))) { /* Add default values to frame parameters. */ if (NILP (Fassq (Qname, parms))) parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms); if (NILP (Fassq (Qinternal_border_width, parms))) - parms = Fcons (Fcons (Qinternal_border_width, make_number (3)), parms); + parms = Fcons (Fcons (Qinternal_border_width, make_fixnum (3)), parms); if (NILP (Fassq (Qborder_width, parms))) - parms = Fcons (Fcons (Qborder_width, make_number (1)), parms); + parms = Fcons (Fcons (Qborder_width, make_fixnum (1)), parms); if (NILP (Fassq (Qborder_color, parms))) parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), parms); if (NILP (Fassq (Qbackground_color, parms))) @@ -6783,9 +8821,16 @@ Text larger than the specified size is clipped. */) tip_f = XFRAME (tip_frame); window = FRAME_ROOT_WINDOW (tip_f); - set_window_buffer (window, Fget_buffer_create (tip), false, false); + tip_buf = Fget_buffer_create (tip, Qnil); + /* We will mark the tip window a "pseudo-window" below, and such + windows cannot have display margins. */ + bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0)); + bset_right_margin_cols (XBUFFER (tip_buf), make_fixnum (0)); + set_window_buffer (window, tip_buf, false, false); w = XWINDOW (window); w->pseudo_window_p = true; + /* Try to avoid that `other-window' select us (Bug#47207). */ + Fset_window_parameter (window, Qno_other_window, Qt); /* Set up the frame's root window. Note: The following code does not try to size the window or its frame correctly. Its only purpose is @@ -6797,11 +8842,11 @@ Text larger than the specified size is clipped. */) w->pixel_top = 0; if (CONSP (Vx_max_tooltip_size) - && RANGED_INTEGERP (1, XCAR (Vx_max_tooltip_size), INT_MAX) - && RANGED_INTEGERP (1, XCDR (Vx_max_tooltip_size), INT_MAX)) + && RANGED_FIXNUMP (1, XCAR (Vx_max_tooltip_size), INT_MAX) + && RANGED_FIXNUMP (1, XCDR (Vx_max_tooltip_size), INT_MAX)) { - w->total_cols = XFASTINT (XCAR (Vx_max_tooltip_size)); - w->total_lines = XFASTINT (XCDR (Vx_max_tooltip_size)); + w->total_cols = XFIXNAT (XCAR (Vx_max_tooltip_size)); + w->total_lines = XFIXNAT (XCDR (Vx_max_tooltip_size)); } else { @@ -6816,7 +8861,7 @@ Text larger than the specified size is clipped. */) /* Insert STRING into root window's buffer and fit the frame to the buffer. */ - count_1 = SPECPDL_INDEX (); + specpdl_ref count_1 = SPECPDL_INDEX (); old_buffer = current_buffer; set_buffer_internal_1 (XBUFFER (w->contents)); bset_truncate_lines (current_buffer, Qnil); @@ -6828,34 +8873,108 @@ Text larger than the specified size is clipped. */) clear_glyph_matrix (w->desired_matrix); clear_glyph_matrix (w->current_matrix); SET_TEXT_POS (pos, BEGV, BEGV_BYTE); - try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE); + displayed = try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE); + + if (!displayed && NILP (Vx_max_tooltip_size)) + { +#ifdef ENABLE_CHECKING + row = w->desired_matrix->rows; + end = w->desired_matrix->rows + w->desired_matrix->nrows; + + while (row < end) + { + if (!row->displays_text_p + || row->ends_at_zv_p) + break; + ++row; + } + + eassert (row < end && row->ends_at_zv_p); +#endif + } + /* Calculate size of tooltip window. */ size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil, - make_number (w->pixel_height), Qnil); + make_fixnum (w->pixel_height), Qnil, + Qnil); /* Add the frame's internal border to calculated size. */ - width = XINT (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); - height = XINT (Fcdr (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); + width = XFIXNUM (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); + height = XFIXNUM (Fcdr (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); /* Calculate position of tooltip frame. */ compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y); /* Show tooltip frame. */ block_input (); + /* If the display is composited, then WM_TRANSIENT_FOR must be set + as well, or else the compositing manager won't display + decorations correctly, even though the tooltip window is override + redirect. See + https://specifications.freedesktop.org/wm-spec/1.4/ar01s08.html + + Perhaps WM_TRANSIENT_FOR should be used in place of + override-redirect anyway. The ICCCM only recommends + override-redirect if the pointer will be grabbed. */ + + if (XTranslateCoordinates (FRAME_X_DISPLAY (f), + FRAME_DISPLAY_INFO (f)->root_window, + FRAME_DISPLAY_INFO (f)->root_window, + root_x, root_y, &dest_x_return, + &dest_y_return, &child) + && child != None) + { + /* But only if the child is not override-redirect, which can + happen if the pointer is above a menu. */ + + if (XGetWindowAttributes (FRAME_X_DISPLAY (f), + child, &child_attrs) + || child_attrs.override_redirect) + XDeleteProperty (FRAME_X_DISPLAY (tip_f), + FRAME_X_WINDOW (tip_f), + FRAME_DISPLAY_INFO (tip_f)->Xatom_wm_transient_for); + else + XSetTransientForHint (FRAME_X_DISPLAY (tip_f), + FRAME_X_WINDOW (tip_f), child); + } + else + XDeleteProperty (FRAME_X_DISPLAY (tip_f), + FRAME_X_WINDOW (tip_f), + FRAME_DISPLAY_INFO (tip_f)->Xatom_wm_transient_for); + +#ifndef USE_XCB XMoveResizeWindow (FRAME_X_DISPLAY (tip_f), FRAME_X_WINDOW (tip_f), root_x, root_y, width, height); XMapRaised (FRAME_X_DISPLAY (tip_f), FRAME_X_WINDOW (tip_f)); +#else + uint32_t values[] = { root_x, root_y, width, height, XCB_STACK_MODE_ABOVE }; + + xcb_configure_window (FRAME_DISPLAY_INFO (tip_f)->xcb_connection, + (xcb_window_t) FRAME_X_WINDOW (tip_f), + (XCB_CONFIG_WINDOW_X + | XCB_CONFIG_WINDOW_Y + | XCB_CONFIG_WINDOW_WIDTH + | XCB_CONFIG_WINDOW_HEIGHT + | XCB_CONFIG_WINDOW_STACK_MODE), &values); + xcb_map_window (FRAME_DISPLAY_INFO (tip_f)->xcb_connection, + (xcb_window_t) FRAME_X_WINDOW (tip_f)); +#endif unblock_input (); +#ifdef USE_CAIRO + x_cr_update_surface_desired_size (tip_f, width, height); +#endif /* USE_CAIRO */ + w->must_be_updated_p = true; update_single_window (w); + flush_frame (tip_f); set_buffer_internal_1 (old_buffer); unbind_to (count_1, Qnil); windows_or_buffers_changed = old_windows_or_buffers_changed; start_timer: /* Let the tip disappear after timeout seconds. */ - tip_timer = call3 (intern ("run-at-time"), timeout, Qnil, - intern ("x-hide-tip")); + tip_timer = call3 (Qrun_at_time, timeout, Qnil, + Qx_hide_tip); return unbind_to (count, Qnil); } @@ -6874,8 +8993,12 @@ DEFUN ("x-double-buffered-p", Fx_double_buffered_p, Sx_double_buffered_p, doc: /* Return t if FRAME is being double buffered. */) (Lisp_Object frame) { +#ifdef HAVE_XDBE struct frame *f = decode_live_frame (frame); return FRAME_X_DOUBLE_BUFFERED_P (f) ? Qt : Qnil; +#else + return Qnil; +#endif } @@ -6939,18 +9062,7 @@ clean_up_file_dialog (void *arg) DEFUN ("x-file-dialog", Fx_file_dialog, Sx_file_dialog, 2, 5, 0, - doc: /* Read file name, prompting with PROMPT in directory DIR. -Use a file selection dialog. Select DEFAULT-FILENAME in the dialog's file -selection box, if specified. If MUSTMATCH is non-nil, the returned file -or directory must exist. - -This function is only defined on NS, MS Windows, and X Windows with the -Motif or Gtk toolkits. With the Motif toolkit, ONLY-DIR-P is ignored. -Otherwise, if ONLY-DIR-P is non-nil, the user can only select directories. -On Windows 7 and later, the file selection dialog "remembers" the last -directory where the user selected a file, and will open that directory -instead of DIR on subsequent invocations of this function with the same -value of DIR as in previous invocations; this is standard Windows behavior. */) + doc: /* SKIP: real doc in USE_GTK definition in xfns.c. */) (Lisp_Object prompt, Lisp_Object dir, Lisp_Object default_filename, Lisp_Object mustmatch, Lisp_Object only_dir_p) { @@ -6962,7 +9074,7 @@ value of DIR as in previous invocations; this is standard Windows behavior. */) Arg al[10]; int ac = 0; XmString dir_xmstring, pattern_xmstring; - ptrdiff_t count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); check_window_system (f); @@ -6975,6 +9087,9 @@ value of DIR as in previous invocations; this is standard Windows behavior. */) /* Prevent redisplay. */ specbind (Qinhibit_redisplay, Qt); + /* Defer selection requests. */ + DEFER_SELECTIONS; + block_input (); /* Create the dialog with PROMPT as title, using DIR as initial @@ -7058,20 +9173,70 @@ value of DIR as in previous invocations; this is standard Windows behavior. */) result = 0; while (result == 0) { - XEvent event; + XEvent event, copy; x_menu_wait_for_event (0); - XtAppNextEvent (Xt_app_con, &event); - if (event.type == KeyPress - && FRAME_X_DISPLAY (f) == event.xkey.display) - { - KeySym keysym = XLookupKeysym (&event.xkey, 0); - /* Pop down on C-g. */ - if (keysym == XK_g && (event.xkey.state & ControlMask) != 0) - XtUnmanageChild (dialog); - } + if (XtAppPending (Xt_app_con)) + { + XtAppNextEvent (Xt_app_con, &event); - (void) x_dispatch_event (&event, FRAME_X_DISPLAY (f)); + copy = event; + if (event.type == KeyPress + && FRAME_X_DISPLAY (f) == event.xkey.display) + { + KeySym keysym = XLookupKeysym (&event.xkey, 0); + + /* Pop down on C-g. */ + if (keysym == XK_g && (event.xkey.state & ControlMask) != 0) + XtUnmanageChild (dialog); + } +#ifdef HAVE_XINPUT2 + else if (event.type == GenericEvent + && FRAME_X_DISPLAY (f) == event.xgeneric.display + && FRAME_DISPLAY_INFO (f)->supports_xi2 + && (event.xgeneric.extension + == FRAME_DISPLAY_INFO (f)->xi2_opcode) + && event.xgeneric.evtype == XI_KeyPress) + { + KeySym keysym; + XIDeviceEvent *xev; + + if (event.xcookie.data) + emacs_abort (); + + if (XGetEventData (FRAME_X_DISPLAY (f), &event.xcookie)) + { + xev = (XIDeviceEvent *) event.xcookie.data; + + copy.xkey.type = KeyPress; + copy.xkey.serial = xev->serial; + copy.xkey.send_event = xev->send_event; + copy.xkey.display = FRAME_X_DISPLAY (f); + copy.xkey.window = xev->event; + copy.xkey.root = xev->root; + copy.xkey.subwindow = xev->child; + copy.xkey.time = xev->time; + copy.xkey.x = lrint (xev->event_x); + copy.xkey.y = lrint (xev->event_y); + copy.xkey.x_root = lrint (xev->root_x); + copy.xkey.y_root = lrint (xev->root_y); + copy.xkey.state = xev->mods.effective; + copy.xkey.keycode = xev->detail; + copy.xkey.same_screen = True; + + keysym = XLookupKeysym (©.xkey, 0); + + if (keysym == XK_g + && (copy.xkey.state & ControlMask) != 0) /* Any escape, ignore modifiers. */ + XtUnmanageChild (dialog); + + XFreeEventData (FRAME_X_DISPLAY (f), &event.xcookie); + } + } +#endif + + (void) x_dispatch_event (©, FRAME_X_DISPLAY (f)); + } } /* Get the result. */ @@ -7116,20 +9281,20 @@ Use a file selection dialog. Select DEFAULT-FILENAME in the dialog's file selection box, if specified. If MUSTMATCH is non-nil, the returned file or directory must exist. -This function is only defined on NS, MS Windows, and X Windows with the +This function is defined only on NS, Haiku, MS Windows, and X Windows with the Motif or Gtk toolkits. With the Motif toolkit, ONLY-DIR-P is ignored. -Otherwise, if ONLY-DIR-P is non-nil, the user can only select directories. -On Windows 7 and later, the file selection dialog "remembers" the last +Otherwise, if ONLY-DIR-P is non-nil, the user can select only directories. +On MS Windows 7 and later, the file selection dialog "remembers" the last directory where the user selected a file, and will open that directory instead of DIR on subsequent invocations of this function with the same -value of DIR as in previous invocations; this is standard Windows behavior. */) +value of DIR as in previous invocations; this is standard MS Windows behavior. */) (Lisp_Object prompt, Lisp_Object dir, Lisp_Object default_filename, Lisp_Object mustmatch, Lisp_Object only_dir_p) { struct frame *f = SELECTED_FRAME (); char *fn; Lisp_Object file = Qnil; Lisp_Object decoded_file; - ptrdiff_t count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); char *cdef_file; check_window_system (f); @@ -7190,7 +9355,7 @@ nil, it defaults to the selected frame. */) Lisp_Object font; Lisp_Object font_param; char *default_name = NULL; - ptrdiff_t count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); if (popup_activated ()) error ("Trying to use a menu from within a menu-entry"); @@ -7254,27 +9419,11 @@ present and mapped to the usual X keysyms. */) struct frame *f = decode_window_system_frame (frame); Display *dpy = FRAME_X_DISPLAY (f); Lisp_Object have_keys; - int major, minor, op, event, error_code; - block_input (); + if (!FRAME_DISPLAY_INFO (f)->supports_xkb) + return Qlambda; - /* Check library version in case we're dynamically linked. */ - major = XkbMajorVersion; - minor = XkbMinorVersion; - if (!XkbLibraryVersion (&major, &minor)) - { - unblock_input (); - return Qlambda; - } - - /* Check that the server supports XKB. */ - major = XkbMajorVersion; - minor = XkbMinorVersion; - if (!XkbQueryExtension (dpy, &op, &event, &error_code, &major, &minor)) - { - unblock_input (); - return Qlambda; - } + block_input (); /* In this code we check that the keyboard has physical keys with names that start with BKSP (Backspace) and DELE (Delete), and that they @@ -7334,6 +9483,24 @@ present and mapped to the usual X keysyms. */) #endif } +DEFUN ("x-get-modifier-masks", Fx_get_modifier_masks, Sx_get_modifier_masks, + 0, 1, 0, + doc: /* Return the X modifier masks corresponding to keyboard modifiers. +The optional second argument TERMINAL specifies which display to fetch +modifier masks from. TERMINAL should be a terminal object, a frame or +a display name (a string). If TERMINAL is omitted or nil, that stands +for the selected frame's display. + +Return a list of (HYPER SUPER ALT SHIFT-LOCK META), each element being +a number describing the modifier mask for the corresponding Emacs +modifier. */) + (Lisp_Object terminal) +{ + struct x_display_info *dpyinfo; + + dpyinfo = check_x_display_info (terminal); + return x_get_keyboard_modifiers (dpyinfo); +} /*********************************************************************** @@ -7347,7 +9514,10 @@ FRAMES should be nil (the selected frame), a frame, or a list of frames (each of which corresponds to one page). Each frame should be visible. Optional arg TYPE should be either `pdf' (default), `png', `postscript', or `svg'. Supported types are determined by the -compile-time configuration of cairo. */) +compile-time configuration of cairo. + +Note: Text drawn with the `x' font backend is shown with hollow boxes +unless TYPE is `png'. */) (Lisp_Object frames, Lisp_Object type) { Lisp_Object rest, tmp; @@ -7449,11 +9619,12 @@ DEFUN ("x-print-frames-dialog", Fx_print_frames_dialog, Sx_print_frames_dialog, doc: /* Pop up a print dialog to print the current contents of FRAMES. FRAMES should be nil (the selected frame), a frame, or a list of frames (each of which corresponds to one page). Each frame should be -visible. */) +visible. + +Note: Text drawn with the `x' font backend is shown with hollow boxes. */) (Lisp_Object frames) { Lisp_Object rest, tmp; - int count; if (!CONSP (frames)) frames = list1 (frames); @@ -7472,7 +9643,7 @@ visible. */) frames = Fnreverse (tmp); /* Make sure the current matrices are up-to-date. */ - count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); specbind (Qredisplay_dont_pause, Qt); redisplay_preserve_echo_area (32); unbind_to (count, Qnil); @@ -7486,6 +9657,87 @@ visible. */) #endif /* USE_GTK */ #endif /* USE_CAIRO */ +#ifdef USE_GTK +#ifdef HAVE_GTK3 +#if GTK_CHECK_VERSION (3, 14, 0) +DEFUN ("x-gtk-debug", Fx_gtk_debug, Sx_gtk_debug, 1, 1, 0, + doc: /* Toggle interactive GTK debugging. */) + (Lisp_Object enable) +{ + gboolean enable_debug = !NILP (enable); + + block_input (); + gtk_window_set_interactive_debugging (enable_debug); + unblock_input (); + + return NILP (enable) ? Qnil : Qt; +} +#endif /* GTK_CHECK_VERSION (3, 14, 0) */ +#endif /* HAVE_GTK3 */ +#endif /* USE_GTK */ + +DEFUN ("x-display-set-last-user-time", Fx_display_last_user_time, + Sx_display_set_last_user_time, 1, 2, 0, + doc: /* Set the last user time of TERMINAL to TIME-OBJECT. +TIME-OBJECT is the X server time, in milliseconds, of the last user +interaction. This is the timestamp that `x-get-selection-internal' +will use by default to fetch selection data. +The optional second argument TERMINAL specifies which display to act +on. TERMINAL should be a terminal object, a frame or a display name +(a string). If TERMINAL is omitted or nil, that stands for the +selected frame's display. */) + (Lisp_Object time_object, Lisp_Object terminal) +{ + struct x_display_info *dpyinfo; + Time time; + + dpyinfo = check_x_display_info (terminal); + CONS_TO_INTEGER (time_object, Time, time); + + x_set_last_user_time_from_lisp (dpyinfo, time); + return Qnil; +} + +DEFUN ("x-internal-focus-input-context", Fx_internal_focus_input_context, + Sx_internal_focus_input_context, 1, 1, 0, + doc: /* Focus and set the client window of all focused frames' GTK input context. +If FOCUS is nil, focus out and remove the client window instead. +This should be called from a variable watcher for `x-gtk-use-native-input'. */) + (Lisp_Object focus) +{ +#ifdef USE_GTK + struct x_display_info *dpyinfo; + struct frame *f; + GtkWidget *widget; + + block_input (); + for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next) + { + f = dpyinfo->x_focus_frame; + + if (f) + { + widget = FRAME_GTK_OUTER_WIDGET (f); + + if (!NILP (focus)) + { + gtk_im_context_focus_in (FRAME_X_OUTPUT (f)->im_context); + gtk_im_context_set_client_window (FRAME_X_OUTPUT (f)->im_context, + gtk_widget_get_window (widget)); + } + else + { + gtk_im_context_focus_out (FRAME_X_OUTPUT (f)->im_context); + gtk_im_context_set_client_window (FRAME_X_OUTPUT (f)->im_context, + NULL); + } + } + } + unblock_input (); +#endif + + return Qnil; +} /*********************************************************************** Initialization @@ -7496,44 +9748,50 @@ visible. */) frame_parm_handler x_frame_parm_handlers[] = { - x_set_autoraise, - x_set_autolower, + gui_set_autoraise, + gui_set_autolower, x_set_background_color, x_set_border_color, - x_set_border_width, + gui_set_border_width, x_set_cursor_color, x_set_cursor_type, - x_set_font, + gui_set_font, x_set_foreground_color, x_set_icon_name, x_set_icon_type, + x_set_child_frame_border_width, x_set_internal_border_width, - x_set_right_divider_width, - x_set_bottom_divider_width, + gui_set_right_divider_width, + gui_set_bottom_divider_width, x_set_menu_bar_lines, x_set_mouse_color, x_explicitly_set_name, - x_set_scroll_bar_width, - x_set_scroll_bar_height, + gui_set_scroll_bar_width, + gui_set_scroll_bar_height, x_set_title, - x_set_unsplittable, - x_set_vertical_scroll_bars, - x_set_horizontal_scroll_bars, - x_set_visibility, + gui_set_unsplittable, + gui_set_vertical_scroll_bars, + gui_set_horizontal_scroll_bars, + gui_set_visibility, + x_set_tab_bar_lines, x_set_tool_bar_lines, x_set_scroll_bar_foreground, x_set_scroll_bar_background, - x_set_screen_gamma, - x_set_line_spacing, - x_set_left_fringe, - x_set_right_fringe, + gui_set_screen_gamma, + gui_set_line_spacing, + gui_set_left_fringe, + gui_set_right_fringe, x_set_wait_for_wm, - x_set_fullscreen, - x_set_font_backend, + gui_set_fullscreen, + gui_set_font_backend, x_set_alpha, x_set_sticky, x_set_tool_bar_position, +#ifdef HAVE_XDBE x_set_inhibit_double_buffering, +#else + NULL, +#endif x_set_undecorated, x_set_parent_frame, x_set_skip_taskbar, @@ -7541,9 +9799,68 @@ frame_parm_handler x_frame_parm_handlers[] = x_set_no_accept_focus, x_set_z_group, x_set_override_redirect, - x_set_no_special_glyphs, + gui_set_no_special_glyphs, + x_set_alpha_background, + x_set_use_frame_synchronization, + x_set_shaded, }; +/* Some versions of libX11 don't have symbols for a few functions we + need, so define replacements here. */ + +#ifdef HAVE_XKB +#ifndef HAVE_XKBREFRESHKEYBOARDMAPPING +Status +XkbRefreshKeyboardMapping (XkbMapNotifyEvent *event) +{ + return Success; +} +#endif + +#ifndef HAVE_XKBFREENAMES +void +XkbFreeNames (XkbDescPtr xkb, unsigned int which, Bool free_map) +{ + return; +} +#endif +#endif + +#ifndef HAVE_XDISPLAYCELLS +int +XDisplayCells (Display *dpy, int screen_number) +{ + struct x_display_info *dpyinfo = x_display_info_for_display (dpy); + + if (!dpyinfo) + emacs_abort (); + + /* Not strictly correct, since the display could be using a + non-default visual, but it satisfies the callers we need to care + about. */ + return dpyinfo->visual_info.colormap_size; +} +#endif + +#ifndef HAVE_XDESTROYSUBWINDOWS +int +XDestroySubwindows (Display *dpy, Window w) +{ + Window root, parent, *children; + unsigned int nchildren, i; + + if (XQueryTree (dpy, w, &root, &parent, &children, + &nchildren)) + { + for (i = 0; i < nchildren; ++i) + XDestroyWindow (dpy, children[i]); + XFree (children); + } + + return 0; +} +#endif + void syms_of_xfns (void) { @@ -7553,6 +9870,7 @@ syms_of_xfns (void) DEFSYM (Qfont_parameter, "font-parameter"); DEFSYM (Qmono, "mono"); DEFSYM (Qassq_delete_all, "assq-delete-all"); + DEFSYM (Qresize_mode, "resize-mode"); #ifdef USE_CAIRO DEFSYM (Qpdf, "pdf"); @@ -7566,8 +9884,14 @@ syms_of_xfns (void) DEFSYM (Qreverse_landscape, "reverse-landscape"); #endif + DEFSYM (QXdndActionCopy, "XdndActionCopy"); + DEFSYM (QXdndActionMove, "XdndActionMove"); + DEFSYM (QXdndActionLink, "XdndActionLink"); + DEFSYM (QXdndActionAsk, "XdndActionAsk"); + DEFSYM (QXdndActionPrivate, "XdndActionPrivate"); + Fput (Qundefined_color, Qerror_conditions, - listn (CONSTYPE_PURE, 2, Qundefined_color, Qerror)); + pure_list (Qundefined_color, Qerror)); Fput (Qundefined_color, Qerror_message, build_pure_c_string ("Undefined color")); @@ -7683,7 +10007,7 @@ or when you set the mouse color. */); DEFVAR_LISP ("x-max-tooltip-size", Vx_max_tooltip_size, doc: /* Maximum size for tooltips. Value is a pair (COLUMNS . ROWS). Text larger than this is clipped. */); - Vx_max_tooltip_size = Fcons (make_number (80), make_number (40)); + Vx_max_tooltip_size = Qnil; DEFVAR_LISP ("x-no-window-manager", Vx_no_window_manager, doc: /* Non-nil if no X window manager is in use. @@ -7697,9 +10021,9 @@ unless you set it to something else. */); Vx_pixel_size_width_font_regexp, doc: /* Regexp matching a font name whose width is the same as `PIXEL_SIZE'. -Since Emacs gets width of a font matching with this regexp from -PIXEL_SIZE field of the name, font finding mechanism gets faster for -such a font. This is especially effective for such large fonts as +Since Emacs gets the width of a font matching this regexp from the +PIXEL_SIZE field of the name, the font-finding mechanism gets faster for +such a font. This is especially effective for large fonts such as Chinese, Japanese, and Korean. */); Vx_pixel_size_width_font_regexp = Qnil; @@ -7723,15 +10047,54 @@ If more space for files in the file chooser dialog is wanted, set this to nil to turn the additional text off. */); x_gtk_file_dialog_help_text = true; - DEFVAR_BOOL ("x-gtk-use-system-tooltips", x_gtk_use_system_tooltips, - doc: /* If non-nil with a Gtk+ built Emacs, the Gtk+ tooltip is used. -Otherwise use Emacs own tooltip implementation. -When using Gtk+ tooltips, the tooltip face is not used. */); - x_gtk_use_system_tooltips = true; + DEFVAR_LISP ("x-gtk-resize-child-frames", x_gtk_resize_child_frames, + doc: /* If non-nil, resize child frames specially with GTK builds. +If this is nil, resize child frames like any other frames. This is the +default and usually works with most desktops. Some desktop environments +(GNOME shell in particular when using the mutter window manager), +however, may refuse to resize a child frame when Emacs is built with +GTK3. For those environments, the two settings below are provided. + +If this equals the symbol `hide', Emacs temporarily hides the child +frame during resizing. This approach seems to work reliably, may +however induce some flicker when the frame is made visible again. + +If this equals the symbol `resize-mode', Emacs uses GTK's resize mode to +always trigger an immediate resize of the child frame. This method is +deprecated by GTK and may not work in future versions of that toolkit. +It also may freeze Emacs when used with other desktop environments. It +avoids, however, the unpleasant flicker induced by the hiding approach. + +This variable is considered a temporary workaround and will be hopefully +eliminated in future versions of Emacs. */); + x_gtk_resize_child_frames = Qnil; /* Tell Emacs about this window system. */ Fprovide (Qx, Qnil); + /* Used by Fx_show_tip. */ + DEFSYM (Qrun_at_time, "run-at-time"); + DEFSYM (Qx_hide_tip, "x-hide-tip"); + + /* Used by display class and backing store reporting functions. */ + DEFSYM (Qalways, "always"); + DEFSYM (Qwhen_mapped, "when-mapped"); + DEFSYM (Qnot_useful, "not-useful"); + DEFSYM (Qstatic_gray, "static-gray"); + DEFSYM (Qgray_scale, "gray-scale"); + DEFSYM (Qstatic_color, "static-color"); + DEFSYM (Qpseudo_color, "pseudo-color"); + DEFSYM (Qtrue_color, "true-color"); + DEFSYM (Qdirect_color, "direct-color"); + DEFSYM (Qgrayscale, "grayscale"); + DEFSYM (Qcolor, "color"); + +#ifdef HAVE_XINPUT2 + DEFSYM (Qxinput2, "xinput2"); + + Fprovide (Qxinput2, Qnil); +#endif + #ifdef USE_X_TOOLKIT Fprovide (intern_c_string ("x-toolkit"), Qnil); #ifdef USE_MOTIF @@ -7789,6 +10152,7 @@ When using Gtk+ tooltips, the tooltip face is not used. */); defsubr (&Sx_server_max_request_size); defsubr (&Sx_server_vendor); defsubr (&Sx_server_version); + defsubr (&Sx_server_input_extension_version); defsubr (&Sx_display_pixel_width); defsubr (&Sx_display_pixel_height); defsubr (&Sx_display_mm_width); @@ -7813,17 +10177,28 @@ When using Gtk+ tooltips, the tooltip face is not used. */); defsubr (&Sx_display_list); defsubr (&Sx_synchronize); defsubr (&Sx_backspace_delete_keys_p); - defsubr (&Sx_show_tip); defsubr (&Sx_hide_tip); defsubr (&Sx_double_buffered_p); + defsubr (&Sx_begin_drag); + defsubr (&Sx_display_set_last_user_time); + defsubr (&Sx_translate_coordinates); + defsubr (&Sx_get_modifier_masks); + tip_timer = Qnil; staticpro (&tip_timer); tip_frame = Qnil; staticpro (&tip_frame); - - last_show_tip_args = Qnil; - staticpro (&last_show_tip_args); + tip_last_frame = Qnil; + staticpro (&tip_last_frame); + tip_last_string = Qnil; + staticpro (&tip_last_string); + tip_last_parms = Qnil; + staticpro (&tip_last_parms); + tip_dx = Qnil; + staticpro (&tip_dx); + tip_dy = Qnil; + staticpro (&tip_dy); defsubr (&Sx_uses_old_gtk_dialog); #if defined (USE_MOTIF) || defined (USE_GTK) @@ -7834,6 +10209,8 @@ When using Gtk+ tooltips, the tooltip face is not used. */); defsubr (&Sx_select_font); #endif + defsubr (&Sx_internal_focus_input_context); + #ifdef USE_CAIRO defsubr (&Sx_export_frames); #ifdef USE_GTK @@ -7842,4 +10219,11 @@ When using Gtk+ tooltips, the tooltip face is not used. */); defsubr (&Sx_print_frames_dialog); #endif #endif +#ifdef USE_GTK +#ifdef HAVE_GTK3 +#if GTK_CHECK_VERSION (3, 14, 0) + defsubr (&Sx_gtk_debug); +#endif +#endif +#endif } |