commit 88e012511ac6bfd7eb31b14d792ab0005e3693a8 (HEAD, refs/remotes/origin/master) Author: Eli Zaretskii Date: Thu Apr 13 10:03:09 2017 +0300 Avoid unnecessary regeneration of the entire loaddefs.el * lisp/Makefile.in (autoloads .PHONY): Add commentary explaining why $(lisp)/loaddefs.el is a dependency of '.PHONY'. ($(lisp)/loaddefs.el): Copy an existing loaddefs.el to loaddefs.tmp before running 'batch-update-autoloads' on it, to avoid slow regeneration of the full contents. (Bug#26459) Use 'move-if-change' instead of 'mv', to avoid producing a new Emacs binary when not necessary. diff --git a/lisp/Makefile.in b/lisp/Makefile.in index 334f2a44bb..ec9ea16021 100644 --- a/lisp/Makefile.in +++ b/lisp/Makefile.in @@ -182,15 +182,25 @@ $(lisp)/finder-inf.el: # # Write to a temporary file in case we're doing a parallel build and a # CANNOT_DUMP-mode Emacs needs to read loaddefs at startup. +# +# We make $(lisp)/loaddefs.el a dependency of .PHONY to cause Make to +# ignore its time stamp. That's because the real dependencies of +# loaddefs.el aren't known to Make, they are implemented in +# batch-update-autoloads, which only updates the autoloads whose +# sources have changed. We start by copying an existing loaddefs.el +# to loaddefs.tmp to avoid regenerating the entire file anew, which is +# slow; starting from an almost-correct content will enable the "only +# update where necessary" feature of batch-update-autoloads. autoloads .PHONY: $(lisp)/loaddefs.el $(lisp)/loaddefs.el: $(LOADDEFS) @echo Directories for loaddefs: ${SUBDIRS_ALMOST} + @if test -f $@ ; then cp $@ $(lisp)/loaddefs.tmp ; fi $(AM_V_GEN)$(emacs) -l autoload \ --eval '(setq autoload-ensure-writable t)' \ --eval '(setq autoload-builtin-package-versions t)' \ --eval '(setq generated-autoload-file (expand-file-name (unmsys--file-name "$(lisp)/loaddefs.tmp")))' \ -f batch-update-autoloads ${SUBDIRS_ALMOST} - mv -f $(lisp)/loaddefs.tmp $@ + $(top_srcdir)/build-aux/move-if-change $(lisp)/loaddefs.tmp $@ # autoloads only runs when loaddefs.el is nonexistent, although it # generates a number of different files. Provide a force option to enable commit 6354e3c3368d99fad755d4dbd7c872ce1280f005 Author: Dmitry Gutov Date: Thu Apr 13 03:50:41 2017 +0300 Handle indentation of nested ternary operators in JS * lisp/progmodes/js.el (js--looking-at-operator-p): Handle nested ternary operators. diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index c220353e9b..bae9e52bf0 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -1788,6 +1788,8 @@ This performs fontification according to `js--class-styles'." (and (looking-at js--indent-operator-re) (or (not (eq (char-after) ?:)) (save-excursion + (js--backward-syntactic-ws) + (when (= (char-before) ?\)) (backward-list)) (and (js--re-search-backward "[?:{]\\|\\_" nil t) (eq (char-after) ??)))) (not (and diff --git a/test/manual/indent/js.js b/test/manual/indent/js.js index 846c3a1a5c..1ad76a83e1 100644 --- a/test/manual/indent/js.js +++ b/test/manual/indent/js.js @@ -128,6 +128,13 @@ if (x > 72 && let x = svg.mumble() .zzz; +// https://github.com/mooz/js2-mode/issues/405 +if (1) { + isSet + ? (isEmpty ? 2 : 3) + : 4 +} + // Local Variables: // indent-tabs-mode: nil // js-indent-level: 2 commit 2e4f4c9d48c563ff8bec102b66da0225587786c6 Author: Eli Zaretskii Date: Wed Apr 12 23:14:20 2017 +0300 Don't call 'kill-this-buffer' outside of menus * lisp/simple.el (kill-current-buffer): New function. (completion-list-mode-map): Use it instead of kill-this-buffer. * lisp/type-break.el (type-break-mode): * lisp/term/ns-win.el (global-map): * lisp/progmodes/gdb-mi.el (gdb-memory-mode-map) (gdb-disassembly-mode-map, gdb-frames-mode-map) (gdb-locals-mode-map, gdb-registers-mode-map): * lisp/org/org-mhe.el (org-mhe-follow-link): * lisp/net/secrets.el (secrets-mode-map): * lisp/net/eudc.el (eudc-mode-map): * lisp/net/eudc-hotlist.el (eudc-hotlist-mode-map): Use kill-current-buffer instead of kill-this-buffer. (Bug#26466) diff --git a/lisp/net/eudc-hotlist.el b/lisp/net/eudc-hotlist.el index 5c170d0aea..083fd7fe7e 100644 --- a/lisp/net/eudc-hotlist.el +++ b/lisp/net/eudc-hotlist.el @@ -42,7 +42,7 @@ (define-key map "s" 'eudc-hotlist-select-server) (define-key map "t" 'eudc-hotlist-transpose-servers) (define-key map "q" 'eudc-hotlist-quit-edit) - (define-key map "x" 'kill-this-buffer) + (define-key map "x" 'kill-current-buffer) map)) (define-derived-mode eudc-hotlist-mode fundamental-mode "EUDC-Servers" diff --git a/lisp/net/eudc.el b/lisp/net/eudc.el index bdd69bf53e..644df7ab78 100644 --- a/lisp/net/eudc.el +++ b/lisp/net/eudc.el @@ -68,8 +68,8 @@ (defvar eudc-mode-map (let ((map (make-sparse-keymap))) - (define-key map "q" 'kill-this-buffer) - (define-key map "x" 'kill-this-buffer) + (define-key map "q" 'kill-current-buffer) + (define-key map "x" 'kill-current-buffer) (define-key map "f" 'eudc-query-form) (define-key map "b" 'eudc-try-bbdb-insert) (define-key map "n" 'eudc-move-to-next-record) diff --git a/lisp/net/secrets.el b/lisp/net/secrets.el index 4d6e48ba2e..9bcfc378f4 100644 --- a/lisp/net/secrets.el +++ b/lisp/net/secrets.el @@ -737,7 +737,7 @@ If there is no such item, or the item doesn't own this attribute, return nil." (set-keymap-parent map (make-composed-keymap special-mode-map widget-keymap)) (define-key map "n" 'next-line) (define-key map "p" 'previous-line) - (define-key map "z" 'kill-this-buffer) + (define-key map "z" 'kill-current-buffer) map) "Keymap used in `secrets-mode' buffers.") diff --git a/lisp/org/org-mhe.el b/lisp/org/org-mhe.el index 72c2eeec22..d1067cd57e 100644 --- a/lisp/org/org-mhe.el +++ b/lisp/org/org-mhe.el @@ -212,7 +212,7 @@ folders." (mh-search folder (list "--message-id" article)) (when (and org-mhe-search-all-folders (not (org-mhe-get-message-real-folder))) - (kill-this-buffer) + (kill-current-buffer) (mh-search "+" (list "--message-id" article)))) (if mh-search-regexp-builder (mh-search "+" (funcall mh-search-regexp-builder @@ -220,7 +220,7 @@ folders." (mh-search "+" article))) (if (org-mhe-get-message-real-folder) (mh-show-msg 1) - (kill-this-buffer) + (kill-current-buffer) (error "Message not found")))) (provide 'org-mhe) diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el index a30669d6bd..1af520dbc3 100644 --- a/lisp/progmodes/gdb-mi.el +++ b/lisp/progmodes/gdb-mi.el @@ -3484,7 +3484,7 @@ in `gdb-memory-format'." (defvar gdb-memory-mode-map (let ((map (make-sparse-keymap))) (suppress-keymap map t) - (define-key map "q" 'kill-this-buffer) + (define-key map "q" 'kill-current-buffer) (define-key map "n" 'gdb-memory-show-next-page) (define-key map "p" 'gdb-memory-show-previous-page) (define-key map "a" 'gdb-memory-set-address) @@ -3838,7 +3838,7 @@ DOC is an optional documentation string." ;; TODO (let ((map (make-sparse-keymap))) (suppress-keymap map) - (define-key map "q" 'kill-this-buffer) + (define-key map "q" 'kill-current-buffer) map)) (define-derived-mode gdb-disassembly-mode gdb-parent-mode "Disassembly" @@ -4042,7 +4042,7 @@ member." (defvar gdb-frames-mode-map (let ((map (make-sparse-keymap))) (suppress-keymap map) - (define-key map "q" 'kill-this-buffer) + (define-key map "q" 'kill-current-buffer) (define-key map "\r" 'gdb-select-frame) (define-key map [mouse-2] 'gdb-select-frame) (define-key map [follow-link] 'mouse-face) @@ -4168,7 +4168,7 @@ member." (defvar gdb-locals-mode-map (let ((map (make-sparse-keymap))) (suppress-keymap map) - (define-key map "q" 'kill-this-buffer) + (define-key map "q" 'kill-current-buffer) (define-key map "\t" (lambda () (interactive) (gdb-set-window-buffer @@ -4259,7 +4259,7 @@ member." (suppress-keymap map) (define-key map "\r" 'gdb-edit-register-value) (define-key map [mouse-2] 'gdb-edit-register-value) - (define-key map "q" 'kill-this-buffer) + (define-key map "q" 'kill-current-buffer) (define-key map "\t" (lambda () (interactive) (gdb-set-window-buffer diff --git a/lisp/simple.el b/lisp/simple.el index 48c1a9b15d..a58164a112 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -5147,6 +5147,21 @@ If ARG is zero, move to the beginning of the current line." (point-max))) (goto-char (next-overlay-change (point)))) (end-of-line))) + +(defun kill-current-buffer () + "Kill the current buffer. +When called in the minibuffer, get out of the minibuffer +using `abort-recursive-edit'. + +This is like `kill-this-buffer', but it doesn't have to be invoked +via the menu bar, and pays no attention to the menu-bar's frame." + (interactive) + (let ((frame (selected-frame))) + (if (and (frame-live-p frame) + (not (window-minibuffer-p (frame-selected-window frame)))) + (kill-buffer (current-buffer)) + (abort-recursive-edit)))) + (defun insert-buffer (buffer) "Insert after point the contents of BUFFER. @@ -7892,7 +7907,7 @@ With a prefix argument, set VARIABLE to VALUE buffer-locally." (define-key map [?\t] 'next-completion) (define-key map [backtab] 'previous-completion) (define-key map "q" 'quit-window) - (define-key map "z" 'kill-this-buffer) + (define-key map "z" 'kill-current-buffer) map) "Local map for completion list buffers.") diff --git a/lisp/term/ns-win.el b/lisp/term/ns-win.el index caad5155fb..70bd817d93 100644 --- a/lisp/term/ns-win.el +++ b/lisp/term/ns-win.el @@ -109,7 +109,7 @@ The properties returned may include `top', `left', `height', and `width'." (define-key global-map [?\s-:] 'ispell) (define-key global-map [?\s-?] 'info) (define-key global-map [?\s-^] 'kill-some-buffers) -(define-key global-map [?\s-&] 'kill-this-buffer) +(define-key global-map [?\s-&] 'kill-current-buffer) (define-key global-map [?\s-C] 'ns-popup-color-panel) (define-key global-map [?\s-D] 'dired) (define-key global-map [?\s-E] 'edit-abbrevs) @@ -125,7 +125,7 @@ The properties returned may include `top', `left', `height', and `width'." (define-key global-map [?\s-h] 'ns-do-hide-emacs) (define-key global-map [?\s-H] 'ns-do-hide-others) (define-key global-map [?\s-j] 'exchange-point-and-mark) -(define-key global-map [?\s-k] 'kill-this-buffer) +(define-key global-map [?\s-k] 'kill-current-buffer) (define-key global-map [?\s-l] 'goto-line) (define-key global-map [?\s-m] 'iconify-frame) (define-key global-map [?\s-n] 'make-frame) diff --git a/lisp/type-break.el b/lisp/type-break.el index fb131d6ebf..8cb81d496e 100644 --- a/lisp/type-break.el +++ b/lisp/type-break.el @@ -402,7 +402,7 @@ problems." 'nowarn) (set-buffer-modified-p nil) (unlock-buffer) - (kill-this-buffer)))))) + (kill-current-buffer)))))) (define-minor-mode type-break-mode-line-message-mode "Toggle warnings about typing breaks in the mode line. commit c25005eda1c5ad8dabb3ce815658bd3c637ae686 Author: Martin Rudalics Date: Wed Apr 12 18:22:44 2017 +0200 New internal-border face and args for select-window and x-focus-frame Add `internal-border' face and handle it whenever clearing the internal border. If NORECORD equals the symbol 'mark-for-redisplay', `select-window' will not record the window but still mark it for redisplay. The new argument NOACTIVATE for `x-focus-frame' tries to not activate FRAME when set. * lisp/faces.el (internal-border): New face. * lisp/mwheel.el (mwheel-scroll): Select window to scroll with `mark-for-redisplay'. * lisp/scroll-bar.el (scroll-bar-drag) (scroll-bar-horizontal-drag, scroll-bar-scroll-down) (scroll-bar-scroll-up, scroll-bar-toolkit-scroll) (scroll-bar-toolkit-horizontal-scroll): Select window to scroll with `mark-for-redisplay'. * lisp/window.el (handle-select-window): When `focus-follows-mouse' is not 'auto-raise' try to not activate FRAME. * src/dispextern.h (face_id): Add INTERNAL_BORDER_FACE_ID. * src/frame.c (Fx_focus_frame): New argument NOACTIVATE. * src/frame.h (x_focus_frame): Update extern declaration. * src/gtkutil.c (xg_clear_under_internal_border): Remove function. (xg_frame_resized, xg_frame_set_char_size): Call x_clear_under_internal_border. (xg_tool_bar_callback): Adapt x_focus_frame call. * src/gtkutil.h (xg_clear_under_internal_border): Remove declaration. * src/nsfns.m (x_focus_frame): Add argument NOACTIVATE. * src/w32fns.c (x_clear_under_internal_border): Fill border with internal-border background if specified. * src/w32term.h (x_clear_under_internal_border): Add extern declaration. * src/w32term.c (x_after_update_window_line): Fill border with internal-border background if specified. (w32_set_vertical_scroll_bar, w32_set_horizontal_scroll_bar) (x_scroll_bar_clear, w32_read_socket): Call x_clear_under_internal_border. (x_focus_frame): New argument NOACTIVATE. * src/window.c (select_window): Mark WINDOW for redisplay when NORECORD equals 'mark-for-redisplay'. (Fselect_window): Update doc-string. (syms_of_window): Define Qmark_for_redisplay. * src/xdisp.c (clear_garbaged_frames, echo_area_display) (redisplay_internal): Call x_clear_under_internal_border. * src/xfaces.c (lookup_basic_face): Handle `window-divider' and `internal-border' faces. (realize_basic_faces): Realize `internal-border' face. (syms_of_xfaces): Define Qinternal_border. * src/xfns.c (x_set_internal_border_width): Remove call for xg_clear_under_internal_border. (x_focus_frame): New argument NOACTIVATE. When non-nil try to not activate frame. * src/xterm.c (x_fill_rectangle): No more static. (x_clear_under_internal_border, x_after_update_window_line): Fill border with internal-border background if specified. (xt_horizontal_action_hook): Rewrite. (handle_one_xevent): Call x_clear_under_internal_border. * src/xterm.h (x_fill_rectangle): Add extern declaration. diff --git a/lisp/faces.el b/lisp/faces.el index e62561a63a..a6ffd1ecd3 100644 --- a/lisp/faces.el +++ b/lisp/faces.el @@ -2629,6 +2629,13 @@ the same as `window-divider' face." :group 'window-divider :group 'basic-faces) +(defface internal-border + '((t nil)) + "Basic face for the internal border." + :version "26.1" + :group 'frames + :group 'basic-faces) + (defface minibuffer-prompt '((((background dark)) :foreground "cyan") ;; Don't use blue because many users of the MS-DOS port customize diff --git a/lisp/mwheel.el b/lisp/mwheel.el index 73fd2b7e11..1428e5f4d0 100644 --- a/lisp/mwheel.el +++ b/lisp/mwheel.el @@ -220,6 +220,9 @@ non-Windows systems." (mods (delq 'click (delq 'double (delq 'triple (event-modifiers event))))) (amt (assoc mods mouse-wheel-scroll-amount))) + (unless (eq scroll-window selected-window) + ;; Mark window to be scrolled for redisplay. + (select-window scroll-window 'mark-for-redisplay)) ;; Extract the actual amount or find the element that has no modifiers. (if amt (setq amt (cdr amt)) (let ((list-elt mouse-wheel-scroll-amount)) diff --git a/lisp/scroll-bar.el b/lisp/scroll-bar.el index 5290a7b3be..5835274044 100644 --- a/lisp/scroll-bar.el +++ b/lisp/scroll-bar.el @@ -281,7 +281,7 @@ If you click outside the slider, the window scrolls to bring the slider there." (with-current-buffer (window-buffer window) (setq before-scroll point-before-scroll)) (save-selected-window - (select-window window) + (select-window window 'mark-for-redisplay) (setq before-scroll (or before-scroll (point)))) (scroll-bar-drag-1 event) @@ -326,7 +326,7 @@ If you click outside the slider, the window scrolls to bring the slider there." (with-current-buffer (window-buffer window) (setq before-scroll point-before-scroll)) (save-selected-window - (select-window window) + (select-window window 'mark-for-redisplay) (setq before-scroll (or before-scroll (point)))) (scroll-bar-horizontal-drag-1 event) @@ -356,7 +356,7 @@ EVENT should be a scroll bar click." (unwind-protect (save-selected-window (let ((portion-whole (nth 2 end-position))) - (select-window window) + (select-window window 'mark-for-redisplay) (setq before-scroll (or before-scroll (point))) (scroll-down @@ -377,7 +377,7 @@ EVENT should be a scroll bar click." (unwind-protect (save-selected-window (let ((portion-whole (nth 2 end-position))) - (select-window window) + (select-window window 'mark-for-redisplay) (setq before-scroll (or before-scroll (point))) (scroll-up @@ -402,7 +402,7 @@ EVENT should be a scroll bar click." (with-current-buffer (window-buffer window) (setq before-scroll point-before-scroll)) (save-selected-window - (select-window window) + (select-window window 'mark-for-redisplay) (setq before-scroll (or before-scroll (point))) (cond ((eq part 'above-handle) @@ -449,7 +449,7 @@ EVENT should be a scroll bar click." (with-current-buffer (window-buffer window) (setq before-scroll point-before-scroll)) (save-selected-window - (select-window window) + (select-window window 'mark-for-redisplay) (setq before-scroll (or before-scroll (point))) (cond ((eq part 'before-handle) diff --git a/lisp/window.el b/lisp/window.el index bea8383fcd..f4a834c0d8 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -8855,7 +8855,7 @@ is active. This function is run by `mouse-autoselect-window-timer'." (raise-frame frame)) (t ;; Just focus frame. - (x-focus-frame frame)))))) + (x-focus-frame frame t)))))) (defun truncated-partial-width-window-p (&optional window) "Return non-nil if lines in WINDOW are specifically truncated due to its width. diff --git a/src/dispextern.h b/src/dispextern.h index 679820d506..d1e4715c32 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -1784,6 +1784,7 @@ enum face_id WINDOW_DIVIDER_FACE_ID, WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID, + INTERNAL_BORDER_FACE_ID, BASIC_FACE_ID_SENTINEL }; diff --git a/src/frame.c b/src/frame.c index e5d80fa825..fc7982d0d5 100644 --- a/src/frame.c +++ b/src/frame.c @@ -2450,14 +2450,16 @@ See `redirect-frame-focus'. */) return FRAME_FOCUS_FRAME (decode_live_frame (frame)); } -DEFUN ("x-focus-frame", Fx_focus_frame, Sx_focus_frame, 1, 1, 0, +DEFUN ("x-focus-frame", Fx_focus_frame, Sx_focus_frame, 1, 2, 0, doc: /* Set the input focus to FRAME. -FRAME nil means use the selected frame. +FRAME nil means use the selected frame. Optional argument NOACTIVATE +means do not activate FRAME. + If there is no window system support, this function does nothing. */) - (Lisp_Object frame) + (Lisp_Object frame, Lisp_Object noactivate) { #ifdef HAVE_WINDOW_SYSTEM - x_focus_frame (decode_window_system_frame (frame)); + x_focus_frame (decode_window_system_frame (frame), !NILP (noactivate)); #endif return Qnil; } diff --git a/src/frame.h b/src/frame.h index 84f9a05d77..36af6e6780 100644 --- a/src/frame.h +++ b/src/frame.h @@ -1531,7 +1531,7 @@ extern void x_sync (struct frame *); #endif /* HAVE_X_WINDOWS */ extern void x_query_colors (struct frame *f, XColor *, int); -extern void x_focus_frame (struct frame *); +extern void x_focus_frame (struct frame *, bool); #ifndef HAVE_NS diff --git a/src/gtkutil.c b/src/gtkutil.c index 227a062bff..ad3590dfa6 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -835,30 +835,6 @@ xg_set_geometry (struct frame *f) } } -/* Clear under internal border if any. As we use a mix of Gtk+ and X calls - and use a GtkFixed widget, this doesn't happen automatically. */ - -void -xg_clear_under_internal_border (struct frame *f) -{ - if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0) - { - x_clear_area (f, 0, 0, - FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f)); - - x_clear_area (f, 0, 0, - FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f)); - - x_clear_area (f, 0, - FRAME_PIXEL_HEIGHT (f) - FRAME_INTERNAL_BORDER_WIDTH (f), - FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f)); - - x_clear_area (f, - FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f), - 0, FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f)); - } -} - static int xg_get_gdk_scale (void) { @@ -905,7 +881,7 @@ xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight) || pixelwidth != FRAME_PIXEL_WIDTH (f) || pixelheight != FRAME_PIXEL_HEIGHT (f)) { - xg_clear_under_internal_border (f); + x_clear_under_internal_border (f); change_frame_size (f, width, height, 0, 1, 0, 1); SET_FRAME_GARBAGED (f); cancel_mouse_face (f); @@ -933,7 +909,7 @@ xg_frame_set_char_size (struct frame *f, int width, int height) &gwidth, &gheight); /* Do this before resize, as we don't know yet if we will be resized. */ - xg_clear_under_internal_border (f); + x_clear_under_internal_border (f); if (FRAME_VISIBLE_P (f)) { @@ -4361,7 +4337,7 @@ xg_tool_bar_callback (GtkWidget *w, gpointer client_data) /* Return focus to the frame after we have clicked on a detached tool bar button. */ - x_focus_frame (f); + x_focus_frame (f, false); } static GtkWidget * diff --git a/src/gtkutil.h b/src/gtkutil.h index 244549fc54..0abcb06bc7 100644 --- a/src/gtkutil.h +++ b/src/gtkutil.h @@ -150,7 +150,6 @@ extern void update_frame_tool_bar (struct frame *f); extern void free_frame_tool_bar (struct frame *f); extern void xg_change_toolbar_position (struct frame *f, Lisp_Object pos); -extern void xg_clear_under_internal_border (struct frame *f); extern void xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight); diff --git a/src/nsfns.m b/src/nsfns.m index e8f035f0e5..8a923dd393 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -1391,7 +1391,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side } void -x_focus_frame (struct frame *f) +x_focus_frame (struct frame *f, bool noactivate) { struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); diff --git a/src/w32fns.c b/src/w32fns.c index f7d3b722ab..62798f269e 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -1634,7 +1634,13 @@ x_set_icon_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval) #endif } -static void +/** + * x_clear_under_internal_border: + * + * Clear area of frame F's internal border. If the internal border face + * of F has been specified (is not null), fill the area with that face. + */ +void x_clear_under_internal_border (struct frame *f) { int border = FRAME_INTERNAL_BORDER_WIDTH (f); @@ -1645,12 +1651,26 @@ x_clear_under_internal_border (struct frame *f) HDC hdc = get_frame_dc (f); int width = FRAME_PIXEL_WIDTH (f); int height = FRAME_PIXEL_HEIGHT (f); + struct face *face = FACE_FROM_ID_OR_NULL (f, INTERNAL_BORDER_FACE_ID); block_input (); - w32_clear_area (f, hdc, 0, FRAME_TOP_MARGIN_HEIGHT (f), width, border); - w32_clear_area (f, hdc, 0, 0, border, height); - w32_clear_area (f, hdc, width - border, 0, border, height); - w32_clear_area (f, hdc, 0, height - border, width, border); + if (face) + { + /* Fill border with internal border face. */ + unsigned long color = face->background; + + w32_fill_area (f, hdc, color, 0, FRAME_TOP_MARGIN_HEIGHT (f), width, border); + w32_fill_area (f, hdc, color, 0, 0, border, height); + w32_fill_area (f, hdc, color, width - border, 0, border, height); + w32_fill_area (f, hdc, color, 0, height - border, width, border); + } + else + { + w32_clear_area (f, hdc, 0, FRAME_TOP_MARGIN_HEIGHT (f), width, border); + w32_clear_area (f, hdc, 0, 0, border, height); + w32_clear_area (f, hdc, width - border, 0, border, height); + w32_clear_area (f, hdc, 0, height - border, width, border); + } release_frame_dc (f, hdc); unblock_input (); } diff --git a/src/w32term.c b/src/w32term.c index b50f0d39a4..1c3d243b62 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -782,9 +782,23 @@ x_after_update_window_line (struct window *w, struct glyph_row *desired_row) block_input (); { HDC hdc = get_frame_dc (f); - w32_clear_area (f, hdc, 0, y, width, height); - w32_clear_area (f, hdc, FRAME_PIXEL_WIDTH (f) - width, - y, width, height); + struct face *face = FACE_FROM_ID_OR_NULL (f, INTERNAL_BORDER_FACE_ID); + + if (face) + { + /* Fill border with internal border face. */ + unsigned long color = face->background; + + w32_fill_area (f, hdc, color, 0, y, width, height); + w32_fill_area (f, hdc, color, FRAME_PIXEL_WIDTH (f) - width, + y, width, height); + } + else + { + w32_clear_area (f, hdc, 0, y, width, height); + w32_clear_area (f, hdc, FRAME_PIXEL_WIDTH (f) - width, + y, width, height); + } release_frame_dc (f, hdc); } unblock_input (); @@ -3908,6 +3922,7 @@ w32_set_vertical_scroll_bar (struct window *w, for them on the frame, we have to clear "under" them. */ w32_clear_area (f, hdc, left, top, width, height); release_frame_dc (f, hdc); + x_clear_under_internal_border (f); } /* Make sure scroll bar is "visible" before moving, to ensure the area of the parent window now exposed will be refreshed. */ @@ -4009,6 +4024,7 @@ w32_set_horizontal_scroll_bar (struct window *w, for them on the frame, we have to clear "under" them. */ w32_clear_area (f, hdc, clear_left, top, clear_width, height); release_frame_dc (f, hdc); + x_clear_under_internal_border (f); } /* Make sure scroll bar is "visible" before moving, to ensure the area of the parent window now exposed will be refreshed. */ @@ -4553,6 +4569,7 @@ x_scroll_bar_clear (struct frame *f) GetClientRect (window, &rect); select_palette (f, hdc); w32_clear_rect (f, hdc, &rect); + x_clear_under_internal_border (f); deselect_palette (f, hdc); ReleaseDC (window, hdc); @@ -4682,6 +4699,7 @@ w32_read_socket (struct terminal *terminal, msg.rect.top, msg.rect.right - msg.rect.left, msg.rect.bottom - msg.rect.top); + x_clear_under_internal_border (f); } } break; @@ -5118,6 +5136,9 @@ w32_read_socket (struct terminal *terminal, } #endif + if (f = x_window_to_frame (dpyinfo, msg.msg.hwnd)) + x_clear_under_internal_border (f); + check_visibility = 1; break; @@ -6392,10 +6413,14 @@ frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y) } -/* focus shifting, raising and lowering. */ +/* Focus shifting, raising and lowering. */ + +/* The NOACTIVATE argument has no effect on Windows. According to the + Windows API: An application cannot activate an inactive window + without also bringing it to the top of the Z order. */ void -x_focus_frame (struct frame *f) +x_focus_frame (struct frame *f, bool noactivate) { #if 0 struct w32_display_info *dpyinfo = &one_w32_display_info; diff --git a/src/w32term.h b/src/w32term.h index 6896ef4f2c..371cf9005b 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -707,7 +707,7 @@ extern BOOL parse_button (int, int, int *, int *); extern void w32_sys_ring_bell (struct frame *f); extern void x_delete_display (struct w32_display_info *dpyinfo); - +extern void x_clear_under_internal_border (struct frame *f); extern void x_query_color (struct frame *, XColor *); #define FILE_NOTIFICATIONS_SIZE 16384 diff --git a/src/window.c b/src/window.c index 58c0c33cbb..2d6f0e48fa 100644 --- a/src/window.c +++ b/src/window.c @@ -492,7 +492,7 @@ select_window (Lisp_Object window, Lisp_Object norecord, record_buffer before returning here. */ goto record_and_return; - if (NILP (norecord)) + if (NILP (norecord) || EQ (norecord, Qmark_for_redisplay)) { /* Mark the window for redisplay since the selected-window has a different mode-line. */ wset_redisplay (XWINDOW (selected_window)); @@ -571,7 +571,8 @@ Return WINDOW. Optional second arg NORECORD non-nil means do not put this buffer at the front of the buffer list and do not make this window the most recently -selected one. +selected one. Also, do not mark WINDOW for redisplay unless NORECORD +equals the special symbol `mark-for-redisplay'. Run `buffer-list-update-hook' unless NORECORD is non-nil. Note that applications and internal routines often select a window temporarily for @@ -7350,6 +7351,7 @@ syms_of_window (void) DEFSYM (Qclone_of, "clone-of"); DEFSYM (Qfloor, "floor"); DEFSYM (Qceiling, "ceiling"); + DEFSYM (Qmark_for_redisplay, "mark-for-redisplay"); staticpro (&Vwindow_list); diff --git a/src/xdisp.c b/src/xdisp.c index 42a59d63b1..58b5ca2f01 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -11379,6 +11379,11 @@ clear_garbaged_frames (void) redraw_frame (f); else clear_current_matrices (f); + +#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_NS) + x_clear_under_internal_border (f); +#endif /* HAVE_WINDOW_SYSTEM && !HAVE_NS */ + fset_redisplay (f); f->garbaged = false; f->resized_p = false; @@ -11441,7 +11446,14 @@ echo_area_display (bool update_frame_p) been called, so that mode lines above the echo area are garbaged. This looks odd, so we prevent it here. */ if (!display_completed) - n = redisplay_mode_lines (FRAME_ROOT_WINDOW (f), false); + { + n = redisplay_mode_lines (FRAME_ROOT_WINDOW (f), false); + +#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_NS) + x_clear_under_internal_border (f); +#endif /* HAVE_WINDOW_SYSTEM && !HAVE_NS */ + + } if (window_height_changed_p /* Don't do this if Emacs is shutting down. Redisplay @@ -14151,6 +14163,10 @@ redisplay_internal (void) if (FRAME_GARBAGED_P (f)) goto retry; +#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_NS) + x_clear_under_internal_border (f); +#endif /* HAVE_WINDOW_SYSTEM && !HAVE_NS */ + /* Prevent various kinds of signals during display update. stdio is not robust about handling signals, which can cause an apparent I/O error. */ diff --git a/src/xfaces.c b/src/xfaces.c index 7fcaef4e41..4714b7b3cb 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -4474,6 +4474,10 @@ lookup_basic_face (struct frame *f, int face_id) case CURSOR_FACE_ID: name = Qcursor; break; case MOUSE_FACE_ID: name = Qmouse; break; case MENU_FACE_ID: name = Qmenu; break; + case WINDOW_DIVIDER_FACE_ID: name = Qwindow_divider; break; + case WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID: name = Qwindow_divider_first_pixel; break; + case WINDOW_DIVIDER_LAST_PIXEL_FACE_ID: name = Qwindow_divider_last_pixel; break; + case INTERNAL_BORDER_FACE_ID: name = Qinternal_border; break; default: emacs_abort (); /* the caller is supposed to pass us a basic face id */ @@ -5168,6 +5172,7 @@ realize_basic_faces (struct frame *f) WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID); realize_named_face (f, Qwindow_divider_last_pixel, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID); + realize_named_face (f, Qinternal_border, INTERNAL_BORDER_FACE_ID); /* Reflect changes in the `menu' face in menu bars. */ if (FRAME_FACE_CACHE (f)->menu_face_changed_p) @@ -6420,11 +6425,12 @@ syms_of_xfaces (void) DEFSYM (Qmouse, "mouse"); DEFSYM (Qmode_line_inactive, "mode-line-inactive"); DEFSYM (Qvertical_border, "vertical-border"); - - /* TTY color-related functions (defined in tty-colors.el). */ DEFSYM (Qwindow_divider, "window-divider"); DEFSYM (Qwindow_divider_first_pixel, "window-divider-first-pixel"); DEFSYM (Qwindow_divider_last_pixel, "window-divider-last-pixel"); + DEFSYM (Qinternal_border, "internal-border"); + + /* TTY color-related functions (defined in tty-colors.el). */ DEFSYM (Qtty_color_desc, "tty-color-desc"); DEFSYM (Qtty_color_standard_values, "tty-color-standard-values"); DEFSYM (Qtty_color_by_index, "tty-color-by-index"); diff --git a/src/xfns.c b/src/xfns.c index 3d667446e6..3257805cab 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -1703,15 +1703,10 @@ x_set_internal_border_width (struct frame *f, Lisp_Object arg, Lisp_Object oldva widget_store_internal_border (FRAME_X_OUTPUT (f)->edit_widget); #endif - if (FRAME_X_WINDOW (f) != 0) + if (FRAME_X_WINDOW (f)) { adjust_frame_size (f, -1, -1, 3, false, Qinternal_border_width); - -#ifdef USE_GTK - xg_clear_under_internal_border (f); -#else x_clear_under_internal_border (f); -#endif } } @@ -4076,7 +4071,7 @@ x_get_focus_frame (struct frame *frame) following a user-command. */ void -x_focus_frame (struct frame *f) +x_focus_frame (struct frame *f, bool noactivate) { Display *dpy = FRAME_X_DISPLAY (f); @@ -4094,7 +4089,8 @@ x_focus_frame (struct frame *f) { XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), RevertToParent, CurrentTime); - x_ewmh_activate_frame (f); + if (!noactivate) + x_ewmh_activate_frame (f); } x_uncatch_errors (); diff --git a/src/xterm.c b/src/xterm.c index 4444a5c187..8dc1067a68 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -714,7 +714,7 @@ x_reset_clip_rectangles (struct frame *f, GC gc) #endif } -static void +void x_fill_rectangle (struct frame *f, GC gc, int x, int y, int width, int height) { #ifdef USE_CAIRO @@ -1295,8 +1295,12 @@ XTbuffer_flipping_unblocked_hook (struct frame *f) show_back_buffer (f); } -/* Clear under internal border if any (GTK has its own version). */ -#ifndef USE_GTK +/** + * x_clear_under_internal_border: + * + * Clear area of frame F's internal border. If the internal border face + * of F has been specified (is not null), fill the area with that face. + */ void x_clear_under_internal_border (struct frame *f) { @@ -1305,17 +1309,39 @@ x_clear_under_internal_border (struct frame *f) int border = FRAME_INTERNAL_BORDER_WIDTH (f); int width = FRAME_PIXEL_WIDTH (f); int height = FRAME_PIXEL_HEIGHT (f); +#ifdef USE_GTK + int margin = 0; +#else int margin = FRAME_TOP_MARGIN_HEIGHT (f); +#endif + struct face *face = FACE_FROM_ID_OR_NULL (f, INTERNAL_BORDER_FACE_ID); block_input (); - x_clear_area (f, 0, 0, border, height); - x_clear_area (f, 0, margin, width, border); - x_clear_area (f, width - border, 0, border, height); - x_clear_area (f, 0, height - border, width, border); + + if (face) + { + unsigned long color = face->background; + Display *display = FRAME_X_DISPLAY (f); + GC gc = f->output_data.x->normal_gc; + + XSetForeground (display, gc, color); + x_fill_rectangle (f, gc, 0, margin, width, border); + x_fill_rectangle (f, gc, 0, 0, border, height); + x_fill_rectangle (f, gc, width - border, 0, border, height); + x_fill_rectangle (f, gc, 0, height - border, width, border); + XSetForeground (display, gc, FRAME_FOREGROUND_PIXEL (f)); + } + else + { + x_clear_area (f, 0, 0, border, height); + x_clear_area (f, 0, margin, width, border); + x_clear_area (f, width - border, 0, border, height); + x_clear_area (f, 0, height - border, width, border); + } + unblock_input (); } } -#endif /* Draw truncation mark bitmaps, continuation mark bitmaps, overlay arrow bitmaps, or clear the fringes if no bitmaps are required @@ -1351,10 +1377,25 @@ x_after_update_window_line (struct window *w, struct glyph_row *desired_row) height > 0)) { int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y)); + struct face *face = FACE_FROM_ID_OR_NULL (f, INTERNAL_BORDER_FACE_ID); block_input (); - x_clear_area (f, 0, y, width, height); - x_clear_area (f, FRAME_PIXEL_WIDTH (f) - width, y, width, height); + if (face) + { + unsigned long color = face->background; + Display *display = FRAME_X_DISPLAY (f); + + XSetForeground (display, f->output_data.x->normal_gc, color); + x_fill_rectangle (f, f->output_data.x->normal_gc, + 0, y, width, height); + x_fill_rectangle (f, f->output_data.x->normal_gc, + FRAME_PIXEL_WIDTH (f) - width, y, width, height); + } + else + { + x_clear_area (f, 0, y, width, height); + x_clear_area (f, FRAME_PIXEL_WIDTH (f) - width, y, width, height); + } unblock_input (); } } @@ -3849,11 +3890,11 @@ x_clear_area (struct frame *f, int x, int y, int width, int height) cairo_fill (cr); x_end_cr_clip (f); #else - if (FRAME_X_DOUBLE_BUFFERED_P (f)) - XFillRectangle (FRAME_X_DISPLAY (f), - FRAME_X_DRAWABLE (f), - f->output_data.x->reverse_gc, - x, y, width, height); + if (FRAME_X_DOUBLE_BUFFERED_P (f)) + XFillRectangle (FRAME_X_DISPLAY (f), + FRAME_X_DRAWABLE (f), + f->output_data.x->reverse_gc, + x, y, width, height); else x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), x, y, width, height, False); @@ -5307,20 +5348,22 @@ xt_horizontal_action_hook (Widget widget, XtPointer client_data, String action_n x_send_scroll_bar_event (window_being_scrolled, scroll_bar_end_scroll, 0, 0, true); w = XWINDOW (window_being_scrolled); - bar = XSCROLL_BAR (w->horizontal_scroll_bar); - - if (bar->dragging != -1) + if (!NILP (w->horizontal_scroll_bar)) { - bar->dragging = -1; - /* The thumb size is incorrect while dragging: fix it. */ - set_horizontal_scroll_bar (w); - } - window_being_scrolled = Qnil; + bar = XSCROLL_BAR (w->horizontal_scroll_bar); + if (bar->dragging != -1) + { + bar->dragging = -1; + /* The thumb size is incorrect while dragging: fix it. */ + set_horizontal_scroll_bar (w); + } + window_being_scrolled = Qnil; #if defined (USE_LUCID) - bar->last_seen_part = scroll_bar_nowhere; + bar->last_seen_part = scroll_bar_nowhere; #endif - /* Xt timeouts no longer needed. */ - toolkit_scroll_bar_interaction = false; + /* Xt timeouts no longer needed. */ + toolkit_scroll_bar_interaction = false; + } } } #endif /* not USE_GTK */ @@ -7920,6 +7963,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, event->xexpose.x, event->xexpose.y, event->xexpose.width, event->xexpose.height, 0); + x_clear_under_internal_border (f); #endif } @@ -7935,6 +7979,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, #endif expose_frame (f, event->xexpose.x, event->xexpose.y, event->xexpose.width, event->xexpose.height); +#ifdef USE_GTK + x_clear_under_internal_border (f); +#endif } if (!FRAME_GARBAGED_P (f)) @@ -7983,7 +8030,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, event->xgraphicsexpose.y, event->xgraphicsexpose.width, event->xgraphicsexpose.height); - show_back_buffer (f); +#ifdef USE_GTK + x_clear_under_internal_border (f); +#endif + show_back_buffer (f); } #ifdef USE_X_TOOLKIT else diff --git a/src/xterm.h b/src/xterm.h index a75257006f..3122a2b208 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -1102,6 +1102,7 @@ extern bool x_alloc_lighter_color_for_widget (Widget, Display *, Colormap, extern bool x_alloc_nearest_color (struct frame *, Colormap, XColor *); extern void x_query_color (struct frame *f, XColor *); extern void x_clear_area (struct frame *f, int, int, int, int); +extern void x_fill_rectangle (struct frame *f, GC, int, int, int, int); #if !defined USE_X_TOOLKIT && !defined USE_GTK extern void x_mouse_leave (struct x_display_info *); #endif commit 8720f601e715e5f1d41f7cf863a525a1cc1bc12c Author: Paul Eggert Date: Wed Apr 12 07:27:23 2017 -0700 Port recent frame changes to --enable-gcc-warnings * src/frame.c (next_frame, prev_frame): Remove now-redundant assertions. * src/frame.h (FOR_EACH_FRAME): Assume Vframe_list is nonempty. diff --git a/src/frame.c b/src/frame.c index ed6c527d4d..e5d80fa825 100644 --- a/src/frame.c +++ b/src/frame.c @@ -1461,9 +1461,6 @@ next_frame (Lisp_Object frame, Lisp_Object minibuf) Lisp_Object f, tail; int passed = 0; - /* There must always be at least one frame in Vframe_list. */ - eassert (CONSP (Vframe_list)); - while (passed < 2) FOR_EACH_FRAME (tail, f) { @@ -1486,9 +1483,6 @@ prev_frame (Lisp_Object frame, Lisp_Object minibuf) { Lisp_Object f, tail, prev = Qnil; - /* There must always be at least one frame in Vframe_list. */ - eassert (CONSP (Vframe_list)); - FOR_EACH_FRAME (tail, f) { if (EQ (frame, f) && !NILP (prev)) diff --git a/src/frame.h b/src/frame.h index 376df52846..84f9a05d77 100644 --- a/src/frame.h +++ b/src/frame.h @@ -1111,7 +1111,8 @@ default_pixels_per_inch_y (void) /* FOR_EACH_FRAME (LIST_VAR, FRAME_VAR) followed by a statement is a `for' loop which iterates over the elements of Vframe_list. The loop will set FRAME_VAR, a Lisp_Object, to each frame in - Vframe_list in succession and execute the statement. LIST_VAR + Vframe_list in succession and execute the statement. Vframe_list + should be nonempty, so the body is executed at least once. LIST_VAR should be a Lisp_Object too; it is used to iterate through the Vframe_list. Note that this macro walks over child frames and the tooltip frame as well. @@ -1121,7 +1122,7 @@ default_pixels_per_inch_y (void) something which executes the statement once. */ #define FOR_EACH_FRAME(list_var, frame_var) \ - for ((list_var) = Vframe_list; \ + for ((list_var) = (eassume (CONSP (Vframe_list)), Vframe_list); \ (CONSP (list_var) \ && (frame_var = XCAR (list_var), true)); \ list_var = XCDR (list_var)) commit 88f43dc30cb8d71830e409973cafbaca13a66a45 Author: Tak Kunihiro Date: Wed Apr 12 16:29:35 2017 +0300 Scroll right and left using wheel-right and wheel-left. These changes also make use of touchpad and trackpad (Bug#26347). * doc/emacs/frames.texi (Mouse Commands): Document horizontal scrolling using the mouse wheel. * lisp/mwheel.el (mwheel-scroll): Respond to wheel-right and wheel-left. (mwheel-tilt-scroll-p, mwheel-flip-direction) (mwheel-scroll-left-function, mwheel-scroll-right-function): New defcustoms. (mouse-wheel-left-event, mouse-wheel-right-event): New variables, events that calls wheel-left/right. * etc/NEWS: Mention horizontal scrolling using the mouse wheel. diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi index d1fd4d0446..68c12d272f 100644 --- a/doc/emacs/frames.texi +++ b/doc/emacs/frames.texi @@ -207,6 +207,15 @@ buffers are scrolled. The variable @code{mouse-wheel-progressive-speed} determines whether the scroll speed is linked to how fast you move the wheel. +@vindex mwheel-tilt-scroll-p +@vindex mwheel-flip-direction +Emacs can also support horizontal scrolling if your mouse's wheel can +be tilted. This feature is off by default; the variable +@code{mwheel-tilt-scroll-p} turns it on. If you'd like to reverse the +direction of horizontal scrolling, customize the variable +@code{mwheel-flip-direction} to a non-@code{nil} value. + + @node Word and Line Mouse @section Mouse Commands for Words and Lines diff --git a/etc/NEWS b/etc/NEWS index 3c328ac58a..799a2b31e5 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -331,6 +331,12 @@ settings of 'scroll-margin' up to half the window size, instead of always restricting the margin to a quarter of the window. +++ +** Emacs can scroll horizontally using mouse, touchpad, and trackbar. +You can enable this by customizing 'mwheel-tilt-scroll-p'. If you +want to reverse the direction of the scroll, customize +'mwheel-flip-direction'. + ++++ ** Emacsclient has a new option -u/--suppress-output. The option suppresses display of return values from the server process. diff --git a/lisp/mwheel.el b/lisp/mwheel.el index 958c6e831b..73fd2b7e11 100644 --- a/lisp/mwheel.el +++ b/lisp/mwheel.el @@ -187,8 +187,8 @@ This can be slightly disconcerting, but some people prefer it." (defun mwheel-scroll (event) "Scroll up or down according to the EVENT. -This should be bound only to mouse buttons 4 and 5 on non-Windows -systems." +This should be bound only to mouse buttons 4, 5, 6, and 7 on +non-Windows systems." (interactive (list last-input-event)) (let* ((selected-window (selected-window)) (scroll-window @@ -250,6 +250,16 @@ systems." (condition-case nil (funcall mwheel-scroll-up-function amt) ;; Make sure we do indeed scroll to the end of the buffer. (end-of-buffer (while t (funcall mwheel-scroll-up-function))))) + ((eq button mouse-wheel-left-event) ; for tilt scroll + (when mwheel-tilt-scroll-p + (funcall (if mwheel-flip-direction + mwheel-scroll-right-function + mwheel-scroll-left-function) amt))) + ((eq button mouse-wheel-right-event) ; for tilt scroll + (when mwheel-tilt-scroll-p + (funcall (if mwheel-flip-direction + mwheel-scroll-left-function + mwheel-scroll-right-function) amt))) (t (error "Bad binding in mwheel-scroll")))) (if (eq scroll-window selected-window) ;; If there is a temporarily active region, deactivate it if @@ -295,7 +305,7 @@ the mode if ARG is omitted or nil." (global-unset-key key)))) ;; Setup bindings as needed. (when mouse-wheel-mode - (dolist (event (list mouse-wheel-down-event mouse-wheel-up-event)) + (dolist (event (list mouse-wheel-down-event mouse-wheel-up-event mouse-wheel-right-event mouse-wheel-left-event)) (dolist (key (mapcar (lambda (amt) `[(,@(if (consp amt) (car amt)) ,event)]) mouse-wheel-scroll-amount)) (global-set-key key 'mwheel-scroll) @@ -307,6 +317,45 @@ the mode if ARG is omitted or nil." "Enable mouse wheel support." (mouse-wheel-mode (if uninstall -1 1))) + +;;; For tilt-scroll +;;; +(defcustom mwheel-tilt-scroll-p nil + "Enable scroll using tilting mouse wheel." + :group 'mouse + :type 'boolean + :version "26.1") + +(defcustom mwheel-flip-direction nil + "Swap direction of 'wheel-right and 'wheel-left." + :group 'mouse + :type 'boolean + :version "26.1") + +(defcustom mwheel-scroll-left-function 'scroll-left + "Function that does the job of scrolling left." + :group 'mouse + :type 'function + :version "26.1") + +(defcustom mwheel-scroll-right-function 'scroll-right + "Function that does the job of scrolling right." + :group 'mouse + :type 'function + :version "26.1") + +(defvar mouse-wheel-left-event + (if (or (featurep 'w32-win) (featurep 'ns-win)) + 'wheel-left + (intern "mouse-6")) + "Event used for scrolling left.") + +(defvar mouse-wheel-right-event + (if (or (featurep 'w32-win) (featurep 'ns-win)) + 'wheel-right + (intern "mouse-7")) + "Event used for scrolling right.") + (provide 'mwheel) ;;; mwheel.el ends here commit cc7a81693638f25cac65bbab013fc23da1ef4bcc Author: Eli Zaretskii Date: Wed Apr 12 12:01:48 2017 +0300 * lisp/Makefile.in (autoloads-force): Fix usage of ".PHONY". diff --git a/lisp/Makefile.in b/lisp/Makefile.in index 63d593c7e6..334f2a44bb 100644 --- a/lisp/Makefile.in +++ b/lisp/Makefile.in @@ -195,7 +195,8 @@ $(lisp)/loaddefs.el: $(LOADDEFS) # autoloads only runs when loaddefs.el is nonexistent, although it # generates a number of different files. Provide a force option to enable # regeneration of all these files. -autoloads-force .PHONY: +.PHONY: autoloads-force +autoloads-force: rm loaddefs.el $(MAKE) autoloads commit 3fdd3bb56c006a2a24761b8fcea0cbd9b0cba422 Author: Martin Rudalics Date: Wed Apr 12 10:38:25 2017 +0200 Add new frame parameters and associated functions Add new frame parameters `undecorated', `override-redirect', `parent-frame', `skip-taskbar', `no-focus-on-map', `no-accept-focus', `z-group', `delete-before', `no-other-frame', `mouse-wheel-frame', `min-width', `min-height'. Add new functions `frame-restack' and `frame-list-z-order'. * lisp/cus-start.el (focus-follows-mouse): Adapt customization type. * lisp/frame.el (handle-delete-frame): Handle child and `delete-before' frames. (other-frame): Stop looking for other frame after one round. (frame-list-z-order, frame-restack): New functions. (delete-other-frames): Handle child frames. * lisp/frameset.el (frameset-persistent-filter-alist) (frameset--record-relationships): Handle `delete-before', `parent-frame' and `mouse-wheel-frame' parameters. Rename latter from `frameset--record-minibuffer-relationships'. (frameset--restore-frame): Handle ‘parent-frame’ parameter specially. (frameset-restore): Handle `delete-before', `parent-frame' and `mouse-wheel-frame' parameters. * lisp/mwheel.el (mwheel-scroll): Handle `mouse-wheel-frame' parameter. * lisp/window.el (window--min-size-ignore-p): Fix doc-string. (mouse-autoselect-window-select, handle-select-window): Major rewrite. Try to not ignore errors. Handle auto-selection of child frames and different values of `focus-follows-mouse'. * src/frame.c (frame_windows_min_size): Handle new `min-width' and `min-height' frame parameters. (make_frame): Initialize new frame structure members. (do_switch_frame): Don't reset internal_last_event_frame for descendant frames. (Fframe_parent, frame_ancestor_p, Fframe_ancestor_p): New functions. (candidate_frame): Don't return `no-other-frame' frame. (other_frames): New function replacing other_visible_frames. (delete_frame): Rewrite. Handle child and `delete-before' frames. (Fmake_frame_invisible): Call other_frames. (store_frame_param): Check `delete-before' and `parent-frame' parameters for circular dependencies. (frame_parms, syms_of_frame): Add entries for and define new frame parameters. (focus_follows_mouse): New meaningful value `auto-raise'. * src/frame.h (z_group): New enumeration type. (frame): New slots parent_frame, undecorated, override_redirect, skip_taskbar, no_focus_on_map, no_accept_focus, z_group. (fset_parent_frame): New inlined function. (FRAME_UNDECORATED, FRAME_OVERRIDE_REDIRECT) (FRAME_PARENT_FRAME, FRAME_SKIP_TASKBAR, FRAME_NO_FOCUS_ON_MAP) (FRAME_NO_ACCEPT_FOCUS, FRAME_Z_GROUP, FRAME_Z_GROUP_NONE) (FRAME_Z_GROUP_ABOVE, FRAME_Z_GROUP_ABOVE_SUSPENDED) (FRAME_Z_GROUP_BELOW): New macros. (frame_ancestor_p): Add declaration. * src/gtkutil.c (xg_create_frame_widgets): Handle `undecorated' and `override-redirect' frame parameters. (x_wm_set_size_hint): None for child frames. (xg_set_undecorated, xg_frame_restack, xg_set_skip_taskbar) (xg_set_no_focus_on_map, xg_set_no_accept_focus) (xg_set_override_redirect): New functions. (xg_update_scrollbar_pos, xg_update_horizontal_scrollbar_pos): Don't let scrollbars obscure child frames. * src/gtkutil.h: (xg_set_undecorated, xg_frame_restack) (xg_set_skip_taskbar, xg_set_no_focus_on_map) (xg_set_no_accept_focus, xg_set_override_redirect): Add extern declarations. * src/nsfns.m (ns_frame_parm_handlers): Add entries for new frame parameters. (Fx_create_frame): Install `min-width' and `min-height' frame parameters. * src/nsterm.m (mouseMoved:): Handle focus_follows_mouse change. * src/w32fns.c (WS_EX_NOACTIVATE): Define if necessary. (x_real_positions): Handle child frames. (x_set_menu_bar_lines): Don't for child frames. (x_set_undecorated, x_set_parent_frame, x_set_skip_taskbar) (x_set_no_focus_on_map, x_set_no_accept_focus) (x_set_z_group): New functions. (w32_createvscrollbar, w32_createhscrollbar): Don't draw scroll bars over child frames. (w32_createwindow): Handle new frame parameters and child frames. (w32_wnd_proc): Let mouse clicks into a child frame activate the frame. Try to handle the `no-accept-focus' parameter. Do SetFocus when our window is brought to top or becomes the foreground window. (w32_window): Don't initialize menu bar for child frames. (Fx_create_frame): Handle new frame parameters. (x_create_tip_frame): Set explicit_parent slot. (w32_dialog_in_progress): New function. (Fx_file_dialog): Handle `z-group-above' frames. (w32_frame_list_z_order, Fw32_frame_list_z_order) (w32_frame_restack, Fw32_frame_restack): New functions. (w32_frame_parm_handlers): Add entries for new frame parameters. * src/w32font.c (Fx_select_font): Handle `z-group-above' frames during font selection dialogue. * src/w32term.c (construct_mouse_wheel): Construct mouse wheel event from F's w32 window. (w32_mouse_position): Handle child frames. (w32_set_vertical_scroll_bar, w32_set_horizontal_scroll_bar): Don't draw scroll bars over child frames. (w32_read_socket): Always erase background of child frames. When generating SELECT_WINDOW_EVENTs handle new value of `focus-follows-mouse' and handle `no-accept-focus' parameter. Handle `mouse-wheel-frame' parameter. (x_calc_absolute_position, x_set_offset, x_set_window_size): Handle child frames. (x_make_frame_visible): Handle child frames specially. Handle `no-focus-on-map' parameter. * src/w32term.h (w32_dialog_in_progress): Add external declaration. * src/xdisp.c (x_consider_frame_title, prepare_menu_bars): Not for child frames. * src/xfns.c (Xm/MwmUtil.h): Include for WM hints. (PropMotifWmHints, PROP_MOTIF_WM_HINTS_ELEMENTS): Define for non-Motif, non-GTK case. (x_real_pos_and_offsets): Handle child frames. (x_set_undecorated, x_set_parent_frame) (x_set_no_focus_on_map, x_set_no_accept_focus) (x_set_override_redirect): New functions. (x_set_menu_bar_lines): Not for child frames. (x_window): Handle `undecorated' and `override_redirect' cases. (Fx_create_frame): Handle new frame parameters. (frame_geometry): Handle child frames and outer border. (x_frame_list_z_order, Fx_frame_list_z_order) (x_frame_restack, Fx_frame_restack): New functions. (Fx_file_dialog, Fx_select_font): Set x_menu_set_in_use. (x_frame_parm_handlers): Add entries for new frame parameters. * src/xmenu.c (x_menu_set_in_use): Handle `z-group-above' frames. * src/xterm.c (x_set_frame_alpha): Don't set alpha of parent for child frames. (XTmouse_position): Handle child frames. (x_scroll_bar_create, x_scroll_bar_expose): Don't let scroll bars obscure child frames. (handle_one_xevent): Handle child frame positions. If necessary set `skip-taskbar' and reassign proper `z-group' when we are mapped. When generating SELECT_WINDOW_EVENTs handle new value of `focus-follows-mouse'. Handle `mouse-wheel-frame' parameter. Let mouse clicks into a child frame activate the frame. (x_calc_absolute_position, x_set_offset): Handle child frames specially. (x_set_skip_taskbar, x_set_z_group): New functions. (x_make_frame_visible): Handle child frames. (ATOM_REFS_INIT): Add entries for Xatom_net_wm_state_skip_taskbar, Xatom_net_wm_state_above, Xatom_net_wm_state_below. * src/xterm.h (top-level): Declare Xatom_net_wm_state_above, Xatom_net_wm_state_below and Xatom_net_wm_state_skip_taskbar. (x_set_skip_taskbar, x_set_z_group): Add extern declarations. diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 51c43c7d21..a507e30ca9 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -286,7 +286,11 @@ Leaving \"Default\" unchecked is equivalent with specifying a default of ;; fns.c (use-dialog-box menu boolean "21.1") (use-file-dialog menu boolean "22.1") - (focus-follows-mouse frames boolean "20.3") + (focus-follows-mouse + frames (choice + (const :tag "Off (nil)" :value nil) + (const :tag "On (t)" :value t) + (const :tag "Auto-raise" :value auto-raise)) "26.1") ;; fontset.c ;; FIXME nil is the initial value, fontset.el setqs it. (vertical-centering-font-regexp display diff --git a/lisp/frame.el b/lisp/frame.el index 4768b5be00..86a0e26e39 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -115,15 +115,19 @@ appended when the minibuffer frame is created." (defun handle-delete-frame (event) "Handle delete-frame events from the X server." (interactive "e") - (let ((frame (posn-window (event-start event))) - (i 0) - (tail (frame-list))) - (while tail - (and (frame-visible-p (car tail)) - (not (eq (car tail) frame)) - (setq i (1+ i))) - (setq tail (cdr tail))) - (if (> i 0) + (let* ((frame (posn-window (event-start event)))) + (if (catch 'other-frame + (dolist (frame-1 (frame-list)) + ;; A valid "other" frame is visible, owns its minibuffer + ;; window, has its `delete-before' parameter unset and is + ;; not a child frame. + (when (and (not (eq frame-1 frame)) + (frame-visible-p frame-1) + (window-live-p (minibuffer-window frame-1)) + (eq (window-frame (minibuffer-window frame-1)) frame-1) + (not (frame-parent frame-1)) + (not (frame-parameter frame-1 'delete-before))) + (throw 'other-frame t)))) (delete-frame frame t) ;; Gildea@x.org says it is ok to ask questions before terminating. (save-buffers-kill-emacs)))) @@ -834,21 +838,24 @@ All frames are arranged in a cyclic order. This command selects the frame ARG steps away in that order. A negative ARG moves in the opposite order. -To make this command work properly, you must tell Emacs -how the system (or the window manager) generally handles -focus-switching between windows. If moving the mouse onto a window -selects it (gives it focus), set `focus-follows-mouse' to t. -Otherwise, that variable should be nil." +To make this command work properly, you must tell Emacs how the +system (or the window manager) generally handles focus-switching +between windows. If moving the mouse onto a window selects +it (gives it focus), set `focus-follows-mouse' to t. Otherwise, +that variable should be nil." (interactive "p") - (let ((frame (selected-frame))) + (let ((sframe (selected-frame)) + (frame (selected-frame))) (while (> arg 0) (setq frame (next-frame frame)) - (while (not (eq (frame-visible-p frame) t)) + (while (and (not (eq frame sframe)) + (not (eq (frame-visible-p frame) t))) (setq frame (next-frame frame))) (setq arg (1- arg))) (while (< arg 0) (setq frame (previous-frame frame)) - (while (not (eq (frame-visible-p frame) t)) + (while (and (not (eq frame sframe)) + (not (eq (frame-visible-p frame) t))) (setq frame (previous-frame frame))) (setq arg (1+ arg))) (select-frame-set-input-focus frame))) @@ -1380,6 +1387,7 @@ and width values are in pixels. '(outer-position 0 . 0) (cons 'outer-size (cons (frame-width frame) (frame-height frame))) '(external-border-size 0 . 0) + '(outer-border-width . 0) '(title-bar-size 0 . 0) '(menu-bar-external . nil) (let ((menu-bar-lines (frame-parameter frame 'menu-bar-lines))) @@ -1490,6 +1498,59 @@ keys and their meanings." for frames = (cdr (assq 'frames attributes)) if (memq frame frames) return attributes)) +(declare-function x-frame-list-z-order "xfns.c" (&optional display)) +(declare-function w32-frame-list-z-order "w32fns.c" (&optional display)) + +(defun frame-list-z-order (&optional display) + "Return list of Emacs' frames, in Z (stacking) order. +The optional argument DISPLAY specifies which display to poll. +DISPLAY should be either a frame or a display name (a string). +If omitted or nil, that stands for the selected frame's display. + +Frames are listed from topmost (first) to bottommost (last). As +a special case, if DISPLAY is non-nil and specifies a live frame, +return the child frames of that frame in Z (stacking) order. + +Return nil if DISPLAY contains no Emacs frame." + (let ((frame-type (framep-on-display display))) + (cond + ((eq frame-type 'x) + (x-frame-list-z-order display)) + ((eq frame-type 'w32) + (w32-frame-list-z-order display))))) + +(declare-function x-frame-restack "xfns.c" (frame1 frame2 &optional above)) +(declare-function w32-frame-restack "w32fns.c" (frame1 frame2 &optional above)) + +(defun frame-restack (frame1 frame2 &optional above) + "Restack FRAME1 below FRAME2. +This implies that if both frames are visible and the display +areas of these frames overlap, FRAME2 will (partially) obscure +FRAME1. If the optional third argument ABOVE is non-nil, restack +FRAME1 above FRAME2. This means that if both frames are visible +and the display areas of these frames overlap, FRAME1 will +\(partially) obscure FRAME2. + +This may be thought of as an atomic action performed in two +steps: The first step removes FRAME1's window-system window from +the display. The second step reinserts FRAME1's window +below (above if ABOVE is true) that of FRAME2. Hence the +position of FRAME2 in its display's Z (stacking) order relative +to all other frames excluding FRAME1 remains unaltered. + +Some window managers may refuse to restack windows. " + (if (and (frame-live-p frame1) + (frame-live-p frame2) + (equal (frame-parameter frame1 'display) + (frame-parameter frame2 'display))) + (let ((frame-type (framep-on-display frame1))) + (cond + ((eq frame-type 'x) + (x-frame-restack frame1 frame2 above)) + ((eq frame-type 'w32) + (w32-frame-restack frame1 frame2 above)))) + (error "Cannot restack frames"))) + (defun frame-size-changed-p (&optional frame) "Return non-nil when the size of FRAME has changed. More precisely, return non-nil when the inner width or height of @@ -1886,7 +1947,7 @@ A geometry specification equivalent to SPEC for FRAME is returned, where the value is a cons with car `+', not numeric. SPEC is a frame geometry spec: (left . VALUE) or (top . VALUE). If VALUE is a number, then it is converted to a cons value, perhaps - relative to the opposite frame edge from that in the original spec. +relative to the opposite frame edge from that in the original spec. FRAME defaults to the selected frame. Examples (measures in pixels) - @@ -1907,24 +1968,36 @@ the opposite frame edge from the edge indicated in the input spec." (defun delete-other-frames (&optional frame) "Delete all frames on FRAME's terminal, except FRAME. If FRAME uses another frame's minibuffer, the minibuffer frame is -left untouched. FRAME must be a live frame and defaults to the -selected one." +left untouched. Do not delete any of FRAME's child frames. If +FRAME is a child frame, delete its siblings only. FRAME must be +a live frame and defaults to the selected one." (interactive) (setq frame (window-normalize-frame frame)) (let ((minibuffer-frame (window-frame (minibuffer-window frame))) (this (next-frame frame t)) + (parent (frame-parent frame)) next) ;; In a first round consider minibuffer-less frames only. (while (not (eq this frame)) (setq next (next-frame this t)) - (unless (eq (window-frame (minibuffer-window this)) this) + (unless (or (eq (window-frame (minibuffer-window this)) this) + ;; When FRAME is a child frame, delete its siblings + ;; only. + (and parent (not (eq (frame-parent this) parent))) + ;; Do not delete a child frame of FRAME. + (eq (frame-parent this) frame)) (delete-frame this)) (setq this next)) ;; In a second round consider all remaining frames. (setq this (next-frame frame t)) (while (not (eq this frame)) (setq next (next-frame this t)) - (unless (eq this minibuffer-frame) + (unless (or (eq this minibuffer-frame) + ;; When FRAME is a child frame, delete its siblings + ;; only. + (and parent (not (eq (frame-parent this) parent))) + ;; Do not delete a child frame of FRAME. + (eq (frame-parent this) frame)) (delete-frame this)) (setq this next)))) diff --git a/lisp/frameset.el b/lisp/frameset.el index 2dd3050ef7..ebf09d3ab5 100644 --- a/lisp/frameset.el +++ b/lisp/frameset.el @@ -446,6 +446,7 @@ DO NOT MODIFY. See `frameset-filter-alist' for a full description.") (buffer-list . :never) (buffer-predicate . :never) (buried-buffer-list . :never) + (delete-before . :never) (font . frameset-filter-shelve-param) (foreground-color . frameset-filter-sanitize-color) (fullscreen . frameset-filter-shelve-param) @@ -455,7 +456,9 @@ DO NOT MODIFY. See `frameset-filter-alist' for a full description.") (GUI:width . frameset-filter-unshelve-param) (height . frameset-filter-shelve-param) (outer-window-id . :never) + (parent-frame . :never) (parent-id . :never) + (mouse-wheel-frame . :never) (tty . frameset-filter-tty-to-GUI) (tty-type . frameset-filter-tty-to-GUI) (width . frameset-filter-shelve-param) @@ -717,9 +720,18 @@ If nil, check all live frames." ;; Saving framesets -(defun frameset--record-minibuffer-relationships (frame-list) - "Process FRAME-LIST and record minibuffer relationships. -FRAME-LIST is a list of frames. Internal use only." +(defun frameset--record-relationships (frame-list) + "Process FRAME-LIST and record relationships. +FRAME-LIST is a list of frames. + +The relationships recorded for each frame are + +- `minibuffer' via `frameset--mini' +- `delete-before' via `frameset--delete-before' +- `parent-frame' via `frameset--parent-frame' +- `mouse-wheel-frame' via `frameset--mouse-wheel-frame' + +Internal use only." ;; Record frames with their own minibuffer (dolist (frame (minibuffer-frame-list)) (when (memq frame frame-list) @@ -730,22 +742,41 @@ FRAME-LIST is a list of frames. Internal use only." (set-frame-parameter frame 'frameset--mini (cons t (eq frame default-minibuffer-frame))))) - ;; Now link minibufferless frames with their minibuffer frames + ;; Now link minibufferless frames with their minibuffer frames and + ;; store `parent-frame', `delete-before' and `mouse-wheel-frame' + ;; relationships in a similar way. (dolist (frame frame-list) - (unless (frame-parameter frame 'frameset--mini) - (frameset--set-id frame) - (let ((mb-frame (window-frame (minibuffer-window frame)))) - ;; For minibufferless frames, frameset--mini is a cons - ;; (nil . FRAME-ID), where FRAME-ID is the frameset--id of - ;; the frame containing its minibuffer window. - ;; FRAME-ID can be set to nil, if FRAME-LIST doesn't contain - ;; the minibuffer frame of a minibufferless frame; we allow - ;; it without trying to second-guess the user. - (set-frame-parameter frame - 'frameset--mini - (cons nil - (and mb-frame - (frameset-frame-id mb-frame)))))))) + (let ((parent-frame (frame-parent frame)) + (delete-before (frame-parameter frame 'delete-before)) + (mouse-wheel-frame (frame-parameter frame 'mouse-wheel-frame)) + (nomini (not (frame-parameter frame 'frameset--mini)))) + (when (or nomini parent-frame delete-before mouse-wheel-frame) + (when nomini + (frameset--set-id frame)) + (when parent-frame + (set-frame-parameter + frame 'frameset--parent-frame (frameset-frame-id parent-frame))) + (when delete-before + (set-frame-parameter + frame 'frameset--delete-before (frameset-frame-id delete-before))) + (when mouse-wheel-frame + (set-frame-parameter + frame 'frameset--mouse-wheel-frame + (frameset-frame-id mouse-wheel-frame))) + (when nomini + (let ((mb-frame (window-frame (minibuffer-window frame)))) + ;; For minibufferless frames, frameset--mini is a cons + ;; (nil . FRAME-ID), where FRAME-ID is the frameset--id of + ;; the frame containing its minibuffer window. + ;; FRAME-ID can be set to nil, if FRAME-LIST doesn't contain + ;; the minibuffer frame of a minibufferless frame; we allow + ;; it without trying to second-guess the user. + (set-frame-parameter + frame + 'frameset--mini + (cons nil + (and mb-frame + (frameset-frame-id mb-frame)))))))))) ;;;###autoload (cl-defun frameset-save (frame-list @@ -768,7 +799,7 @@ PROPERTIES is a user-defined property list to add to the frameset." (cl-delete-if-not predicate list) list))) fs) - (frameset--record-minibuffer-relationships frames) + (frameset--record-relationships frames) (setq fs (frameset--make :app app :name name @@ -993,6 +1024,14 @@ Internal use only." (frameset--initial-params filtered-cfg)))) (puthash frame :created frameset--action-map)) + ;; Try to assign parent-frame right here - it will improve things + ;; for minibuffer-less child frames. + (let* ((frame-id (frame-parameter frame 'frameset--parent-frame)) + (parent-frame + (and frame-id (frameset-frame-with-id frame-id)))) + (when (frame-live-p parent-frame) + (set-frame-parameter frame 'parent-frame parent-frame))) + (modify-frame-parameters frame (if (eq (frame-parameter frame 'fullscreen) fullscreen) ;; Workaround for bug#14949 @@ -1205,6 +1244,29 @@ All keyword parameters default to nil." (error (delay-warning 'frameset (error-message-string err) :error)))))) + ;; Setting the parent frame after the frame has been created is a + ;; pain because one can see the frame move on the screen. Ideally, + ;; we would restore minibuffer equipped child frames after their + ;; respective parents have been made but this might interfere with + ;; the reordering of minibuffer frames. Left to the experts ... + (dolist (frame (frame-list)) + (let* ((frame-id (frame-parameter frame 'frameset--parent-frame)) + (parent-frame + (and frame-id (frameset-frame-with-id frame-id)))) + (when (and (not (eq (frame-parameter frame 'parent-frame) parent-frame)) + (frame-live-p parent-frame)) + (set-frame-parameter frame 'parent-frame parent-frame))) + (let* ((frame-id (frame-parameter frame 'frameset--delete-before)) + (delete-before + (and frame-id (frameset-frame-with-id frame-id)))) + (when (frame-live-p delete-before) + (set-frame-parameter frame 'delete-before delete-before))) + (let* ((frame-id (frame-parameter frame 'frameset--mouse-wheel-frame)) + (mouse-wheel-frame + (and frame-id (frameset-frame-with-id frame-id)))) + (when (frame-live-p mouse-wheel-frame) + (set-frame-parameter frame 'mouse-wheel-frame mouse-wheel-frame)))) + ;; In case we try to delete the initial frame, we want to make sure that ;; other frames are already visible (discussed in thread for bug#14841). (sit-for 0 t) diff --git a/lisp/mwheel.el b/lisp/mwheel.el index eaeb831e84..958c6e831b 100644 --- a/lisp/mwheel.el +++ b/lisp/mwheel.el @@ -190,14 +190,33 @@ This can be slightly disconcerting, but some people prefer it." This should be bound only to mouse buttons 4 and 5 on non-Windows systems." (interactive (list last-input-event)) - (let* ((curwin (if mouse-wheel-follow-mouse - (prog1 - (selected-window) - (select-window (mwheel-event-window event))))) - (buffer (window-buffer curwin)) - (opoint (with-current-buffer buffer - (when (eq (car-safe transient-mark-mode) 'only) - (point)))) + (let* ((selected-window (selected-window)) + (scroll-window + (or (catch 'found + (let* ((window (if mouse-wheel-follow-mouse + (mwheel-event-window event) + (selected-window))) + (frame (when (window-live-p window) + (frame-parameter + (window-frame window) 'mouse-wheel-frame)))) + (when (frame-live-p frame) + (let* ((pos (mouse-absolute-pixel-position)) + (pos-x (car pos)) + (pos-y (cdr pos))) + (walk-window-tree + (lambda (window-1) + (let ((edges (window-edges window-1 nil t t))) + (when (and (<= (nth 0 edges) pos-x) + (<= pos-x (nth 2 edges)) + (<= (nth 1 edges) pos-y) + (<= pos-y (nth 3 edges))) + (throw 'found window-1)))) + frame nil t))))) + (mwheel-event-window event))) + (old-point + (and (eq scroll-window selected-window) + (eq (car-safe transient-mark-mode) 'only) + (window-point))) (mods (delq 'click (delq 'double (delq 'triple (event-modifiers event))))) (amt (assoc mods mouse-wheel-scroll-amount))) @@ -232,18 +251,18 @@ systems." ;; Make sure we do indeed scroll to the end of the buffer. (end-of-buffer (while t (funcall mwheel-scroll-up-function))))) (t (error "Bad binding in mwheel-scroll")))) - (if curwin (select-window curwin))) - ;; If there is a temporarily active region, deactivate it if - ;; scrolling moves point. - (when opoint - (with-current-buffer buffer - (when (/= opoint (point)) - ;; Call `deactivate-mark' at the original position, so that - ;; the original region is saved to the X selection. - (let ((newpoint (point))) - (goto-char opoint) - (deactivate-mark) - (goto-char newpoint)))))) + (if (eq scroll-window selected-window) + ;; If there is a temporarily active region, deactivate it if + ;; scrolling moved point. + (when (and old-point (/= old-point (window-point))) + ;; Call `deactivate-mark' at the original position, so that + ;; the original region is saved to the X selection. + (let ((new-point (window-point))) + (goto-char old-point) + (deactivate-mark) + (goto-char new-point))) + (select-window selected-window t)))) + (when (and mouse-wheel-click-event mouse-wheel-inhibit-click-time) (if mwheel-inhibit-click-event-timer (cancel-timer mwheel-inhibit-click-event-timer) diff --git a/lisp/window.el b/lisp/window.el index 505024342e..bea8383fcd 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -1533,7 +1533,7 @@ return the minimum pixel-size of WINDOW." (window-normalize-window window) horizontal ignore pixelwise)) (defun window--min-size-ignore-p (window ignore) - "Return non-nil if IGNORE says to ignore height restrictions for WINDOW." + "Return non-nil if IGNORE says to ignore size restrictions for WINDOW." (if (window-valid-p ignore) (eq window ignore) (not (memq ignore '(nil preserved))))) @@ -8735,13 +8735,14 @@ means suspend autoselection." If the mouse position has stabilized in a non-selected window, select that window. The minibuffer window is selected only if the minibuffer is active. This function is run by `mouse-autoselect-window-timer'." - (ignore-errors - (let* ((mouse-position (mouse-position)) - (window - (ignore-errors - (window-at (cadr mouse-position) (cddr mouse-position) - (car mouse-position))))) - (cond + (let* ((mouse-position (mouse-position)) + (mouse-x (and (numberp (cadr mouse-position)) + (cadr mouse-position))) + (mouse-y (and (numberp (cddr mouse-position)) + (cddr mouse-position))) + (frame (and mouse-x mouse-y (car mouse-position))) + (window (and frame (window-at mouse-x mouse-y frame)))) + (cond ((or (and (fboundp 'menu-or-popup-active-p) (menu-or-popup-active-p)) (and window (let ((coords (coordinates-in-window-p @@ -8752,72 +8753,63 @@ is active. This function is run by `mouse-autoselect-window-timer'." ;; text region of WINDOW: Suspend autoselection temporarily. (mouse-autoselect-window-start mouse-position nil t)) ((or (eq mouse-autoselect-window-state 'suspend) - ;; When the mouse is at its first recorded position, restart - ;; delayed autoselection. This works around a scenario with - ;; two two-window frames with identical dimensions: select the - ;; first window of the first frame, switch to the second - ;; frame, move the mouse to its second window, minimize the - ;; second frame. Now the second window of the first frame - ;; gets selected although the mouse never really "moved" into - ;; that window. - (and (numberp mouse-autoselect-window) - (equal (mouse-position) mouse-autoselect-window-position-1))) - ;; Delayed autoselection was temporarily suspended, reenable it. - (mouse-autoselect-window-start mouse-position)) - ((and window (not (eq window (selected-window))) - (or (not (numberp mouse-autoselect-window)) - (and (>= mouse-autoselect-window 0) - ;; If `mouse-autoselect-window' is non-negative, - ;; select window if it's the same as before. - (eq window mouse-autoselect-window-window)) - ;; Otherwise select window iff the mouse is at the same - ;; position as before. Observe that the first test - ;; after starting autoselection usually fails since the - ;; value of `mouse-autoselect-window-position' recorded - ;; there is the position where the mouse has entered the - ;; new window and not necessarily where the mouse has - ;; stopped moving. - (equal mouse-position mouse-autoselect-window-position)) - ;; The minibuffer is a candidate window if it's active. - (or (not (window-minibuffer-p window)) - (eq window (active-minibuffer-window)))) - ;; Mouse position has stabilized in non-selected window: Cancel - ;; delayed autoselection and try to select that window. - (mouse-autoselect-window-cancel t) - ;; Select window where mouse appears unless the selected window is the - ;; minibuffer. Use `unread-command-events' in order to execute pre- - ;; and post-command hooks and trigger idle timers. To avoid delaying - ;; autoselection again, set `mouse-autoselect-window-state'." - (unless (window-minibuffer-p) - (setq mouse-autoselect-window-state 'select) - (setq unread-command-events - (cons (list 'select-window (list window)) - unread-command-events)))) - ((or (and window (eq window (selected-window))) - (not (numberp mouse-autoselect-window)) - (equal mouse-position mouse-autoselect-window-position)) - ;; Mouse position has either stabilized in the selected window or at - ;; `mouse-autoselect-window-position': Cancel delayed autoselection. - (mouse-autoselect-window-cancel t)) - (t - ;; Mouse position has not stabilized yet, resume delayed - ;; autoselection. - (mouse-autoselect-window-start mouse-position window)))))) + ;; When the mouse is at its first recorded position, restart + ;; delayed autoselection. This works around a scenario with + ;; two two-window frames with identical dimensions: select the + ;; first window of the first frame, switch to the second + ;; frame, move the mouse to its second window, minimize the + ;; second frame. Now the second window of the first frame + ;; gets selected although the mouse never really "moved" into + ;; that window. + (and (numberp mouse-autoselect-window) + (equal (mouse-position) mouse-autoselect-window-position-1))) + ;; Delayed autoselection was temporarily suspended, reenable it. + (mouse-autoselect-window-start mouse-position)) + ((and window + (or (not (numberp mouse-autoselect-window)) + (and (>= mouse-autoselect-window 0) + ;; If `mouse-autoselect-window' is non-negative, + ;; select window if it's the same as before. + (eq window mouse-autoselect-window-window)) + ;; Otherwise select window iff the mouse is at the same + ;; position as before. Observe that the first test + ;; after starting autoselection usually fails since the + ;; value of `mouse-autoselect-window-position' recorded + ;; there is the position where the mouse has entered the + ;; new window and not necessarily where the mouse has + ;; stopped moving. + (equal mouse-position mouse-autoselect-window-position)) + ;; The minibuffer is a candidate window if it's active. + (or (not (window-minibuffer-p window)) + (eq window (active-minibuffer-window)))) + ;; Mouse position has stabilized in non-selected window: Cancel + ;; delayed autoselection and try to select that window. + (mouse-autoselect-window-cancel t) + ;; Use `unread-command-events' in order to execute pre- and + ;; post-command hooks and trigger idle timers. To avoid delaying + ;; autoselection again, set `mouse-autoselect-window-state'." + (setq mouse-autoselect-window-state 'select) + (setq unread-command-events + (cons (list 'select-window (list window)) + unread-command-events))) + ((or (not (numberp mouse-autoselect-window)) + (equal mouse-position mouse-autoselect-window-position)) + ;; Mouse position has stabilized at + ;; `mouse-autoselect-window-position': Cancel delayed + ;; autoselection. + (mouse-autoselect-window-cancel t)) + (window + ;; Mouse position has not stabilized yet, resume delayed + ;; autoselection. + (mouse-autoselect-window-start mouse-position window))))) (defun handle-select-window (event) "Handle select-window events." (interactive "^e") - (let ((window (posn-window (event-start event)))) + (let* ((window (posn-window (event-start event))) + (frame (and (window-live-p window) (window-frame window))) + (old-frame (selected-frame))) (unless (or (not (window-live-p window)) - ;; Don't switch if we're currently in the minibuffer. - ;; This tries to work around problems where the - ;; minibuffer gets unselected unexpectedly, and where - ;; you then have to move your mouse all the way down to - ;; the minibuffer to select it. - (window-minibuffer-p) - ;; Don't switch to minibuffer window unless it's active. - (and (window-minibuffer-p window) - (not (minibuffer-window-active-p window))) ;; Don't switch when autoselection shall be delayed. (and (numberp mouse-autoselect-window) (not (eq mouse-autoselect-window-state 'select)) @@ -8830,15 +8822,40 @@ is active. This function is run by `mouse-autoselect-window-timer'." (mouse-autoselect-window-start position window) ;; Executing a command cancels delayed autoselection. (add-hook - 'pre-command-hook 'mouse-autoselect-window-cancel)))) - (when mouse-autoselect-window - ;; Reset state of delayed autoselection. - (setq mouse-autoselect-window-state nil) - ;; Run `mouse-leave-buffer-hook' when autoselecting window. - (run-hooks 'mouse-leave-buffer-hook)) + 'pre-command-hook 'mouse-autoselect-window-cancel))) + ;; Don't switch to a `no-accept-focus' frame unless it's + ;; already selected. + (and (not (eq frame (selected-frame))) + (frame-parameter frame 'no-accept-focus)) + ;; Don't switch to minibuffer window unless it's active. + (and (window-minibuffer-p window) + (not (minibuffer-window-active-p window)))) + ;; Reset state of delayed autoselection. + (setq mouse-autoselect-window-state nil) + ;; Run `mouse-leave-buffer-hook' when autoselecting window. + (run-hooks 'mouse-leave-buffer-hook) ;; Clear echo area. (message nil) - (select-window window)))) + ;; Select the window before giving the frame focus since otherwise + ;; we might get two windows with an active cursor. + (select-window window) + (cond + ((or (not (memq (window-system frame) '(x w32 ns))) + (not focus-follows-mouse) + ;; Focus FRAME if it's either a child frame or an ancestor + ;; of the frame switched from. + (and (not (frame-parameter frame 'parent-frame)) + (not (frame-ancestor-p frame old-frame))))) + ((eq focus-follows-mouse 'auto-raise) + ;; Focus and auto-raise frame. + (x-focus-frame frame) + ;; This doesn't seem to work when we move from a normal frame + ;; right into the child frame of another frame - we should raise + ;; that child frame's ancestor frame first ... + (raise-frame frame)) + (t + ;; Just focus frame. + (x-focus-frame frame)))))) (defun truncated-partial-width-window-p (&optional window) "Return non-nil if lines in WINDOW are specifically truncated due to its width. diff --git a/src/frame.c b/src/frame.c index 5f57d4a0c2..ed6c527d4d 100644 --- a/src/frame.c +++ b/src/frame.c @@ -324,11 +324,51 @@ DEFUN ("frame-windows-min-size", Fframe_windows_min_size, return make_number (0); } +/** + * frame_windows_min_size: + * + * Return the minimum number of lines (columns if HORIZONTAL is non-nil) + * of FRAME. If PIXELWISE is non-nil, return the minimum height (width) + * in pixels. + * + * This value is calculated by the function `frame-windows-min-size' in + * window.el unless the `min-height' (`min-width' if HORIZONTAL is + * non-nil) parameter of FRAME is non-nil thus explicitly specifying the + * value to be returned. In that latter case IGNORE is ignored. + * + * If `frame-windows-min-size' is called, it will make sure that the + * return value accomodates all windows of FRAME respecting the values + * of `window-min-height' (`window-min-width' if HORIZONTAL is non-nil). + * With IGNORE non-nil the values of these variables are ignored. + * + * In either case never return a value less than 1. + */ static int frame_windows_min_size (Lisp_Object frame, Lisp_Object horizontal, Lisp_Object ignore, Lisp_Object pixelwise) { - return XINT (call4 (Qframe_windows_min_size, frame, horizontal, + struct frame *f = XFRAME (frame); + Lisp_Object par_size; + + if ((!NILP (horizontal) + && NUMBERP (par_size = get_frame_param (f, Qmin_width))) + || (NILP (horizontal) + && NUMBERP (par_size = get_frame_param (f, Qmin_height)))) + { + int min_size = XINT (par_size); + + /* Don't allow phantom frames. */ + if (min_size < 1) + min_size = 1; + + return (NILP (pixelwise) + ? min_size + : min_size * (NILP (horizontal) + ? FRAME_LINE_HEIGHT (f) + : FRAME_COLUMN_WIDTH (f))); + } + else + return XINT (call4 (Qframe_windows_min_size, frame, horizontal, ignore, pixelwise)); } @@ -643,6 +683,16 @@ make_frame (bool mini_p) f->vertical_scroll_bar_type = vertical_scroll_bar_none; f->horizontal_scroll_bars = false; f->want_fullscreen = FULLSCREEN_NONE; +#if ! defined (HAVE_NS) + f->undecorated = false; +#ifndef HAVE_NTGUI + f->override_redirect = false; +#endif + f->skip_taskbar = false; + f->no_focus_on_map = false; + f->no_accept_focus = false; + f->z_group = z_group_none; +#endif #if ! defined (USE_GTK) && ! defined (HAVE_NS) f->last_tool_bar_item = -1; #endif @@ -1215,7 +1265,10 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor (select-window (frame-root-window (new-frame))) doesn't end up with your typing being interpreted in the new frame instead of the one you're actually typing in. */ - internal_last_event_frame = Qnil; +#ifdef HAVE_WINDOW_SYSTEM + if (!frame_ancestor_p (f, sf)) +#endif + internal_last_event_frame = Qnil; return frame; } @@ -1283,6 +1336,72 @@ DEFUN ("frame-list", Fframe_list, Sframe_list, return frames; } +DEFUN ("frame-parent", Fframe_parent, Sframe_parent, + 0, 1, 0, + doc: /* Return the parent frame of FRAME. +The parent frame of FRAME is the Emacs frame whose window-system window +is the parent window of FRAME's window-system window. When such a frame +exists, FRAME is considered a child frame of that frame. + +Return nil if FRAME has no parent frame. This means that FRAME's +window-system window is either a "top-level" window (a window whose +parent window is the window-system's root window) or an embedded window +\(a window whose parent window is owned by some other application). */) + (Lisp_Object frame) +{ + struct frame *f = decode_live_frame (frame); + struct frame *p = FRAME_PARENT_FRAME (f); + Lisp_Object parent; + + /* Can't return f->parent_frame directly since it might not be defined + for this platform. */ + if (p) + { + XSETFRAME (parent, p); + + return parent; + } + else + return Qnil; +} + +#ifdef HAVE_WINDOW_SYSTEM +bool +frame_ancestor_p (struct frame *af, struct frame *df) +{ + struct frame *pf = FRAME_PARENT_FRAME (df); + + while (pf) + { + if (pf == af) + return true; + else + pf = FRAME_PARENT_FRAME (pf); + } + + return false; +} +#endif + +DEFUN ("frame-ancestor-p", Fframe_ancestor_p, Sframe_ancestor_p, + 2, 2, 0, + doc: /* Return non-nil if ANCESTOR is an ancestor of DESCENDANT. +ANCESTOR is an ancestor of DESCENDANT when it is either DESCENDANT's +parent frame or it is an ancestor of DESCENDANT's parent frame. Both, +ANCESTOR and DESCENDANT must be live frames and default to the selected +frame. */) + (Lisp_Object ancestor, Lisp_Object descendant) +{ +#ifdef HAVE_WINDOW_SYSTEM + struct frame *af = decode_live_frame (ancestor); + struct frame *df = decode_live_frame (descendant); + + return frame_ancestor_p (af, df) ? Qt : Qnil; +#else + return Qnil; +#endif + } + /* Return CANDIDATE if it can be used as 'other-than-FRAME' frame on the same tty (for tty frames) or among frames which uses FRAME's keyboard. If MINIBUF is nil, do not consider minibuffer-only candidate. @@ -1303,7 +1422,9 @@ candidate_frame (Lisp_Object candidate, Lisp_Object frame, Lisp_Object minibuf) || (FRAME_TERMCAP_P (c) && FRAME_TERMCAP_P (f) && FRAME_TTY (c) == FRAME_TTY (f))) { - if (NILP (minibuf)) + if (!NILP (get_frame_param (c, Qno_other_frame))) + return Qnil; + else if (NILP (minibuf)) { if (!FRAME_MINIBUF_ONLY_P (c)) return candidate; @@ -1441,35 +1562,65 @@ DEFUN ("last-nonminibuffer-frame", Flast_nonminibuf_frame, return frame; } -/* Return 1 if it is ok to delete frame F; - 0 if all frames aside from F are invisible. - (Exception: if F is the terminal frame, and we are using X, return 1.) */ +/** + * other_frames: + * + * Return true if there exists at least one visible or iconified frame + * but F. Return false otherwise. + * + * Always return false when all remaining frames are either tooltip or + * child frames or frames with a non-nil `delete-before' parameter. If + * INVISIBLE is false, also return false when the minibuffer window of + * all remaining frames is on F. + + * If F is the terminal frame and we are using X, return true if at + * least one X frame exists. */ +static bool +other_frames (struct frame *f, bool invisible) +{ + Lisp_Object frames, frame, frame1; + struct frame *f1; + Lisp_Object minibuffer_window = FRAME_MINIBUF_WINDOW (f); -static int -other_visible_frames (struct frame *f) -{ - Lisp_Object frames, this; + XSETFRAME (frame, f); + if (WINDOWP (minibuffer_window) + && !EQ (frame, WINDOW_FRAME (XWINDOW (minibuffer_window)))) + minibuffer_window = Qnil; - FOR_EACH_FRAME (frames, this) + FOR_EACH_FRAME (frames, frame1) { - if (f == XFRAME (this)) - continue; - - /* Verify that we can still talk to the frame's X window, - and note any recent change in visibility. */ + f1 = XFRAME (frame1); + if (f != f1) + { + /* Verify that we can still talk to the frame's X window, and + note any recent change in visibility. */ #ifdef HAVE_X_WINDOWS - if (FRAME_WINDOW_P (XFRAME (this))) - x_sync (XFRAME (this)); + if (FRAME_WINDOW_P (f1)) + x_sync (f1); #endif - - if (FRAME_VISIBLE_P (XFRAME (this)) - || FRAME_ICONIFIED_P (XFRAME (this)) - /* Allow deleting the terminal frame when at least one X - frame exists. */ - || (FRAME_WINDOW_P (XFRAME (this)) && !FRAME_WINDOW_P (f))) - return 1; + if (NILP (Fframe_parameter (frame1, Qtooltip)) + /* Tooltips and child frames don't count. */ + && !FRAME_PARENT_FRAME (f1) + /* Frames with a non-nil `delete-before' parameter don't + count - either they depend on us or they depend on a + frame that we will have to find right here. */ + && NILP (get_frame_param (f1, Qdelete_before)) + /* Frames whose minibuffer window is on F don't count + unless INVISIBLE is set - in that case F is either made + invisible and may be autoraised from such a frame or + the FORCE argument of delete_frame was non-nil. */ + && (invisible || NILP (minibuffer_window) + || !EQ (FRAME_MINIBUF_WINDOW (f1), minibuffer_window)) + /* At least one visible/iconified frame must remain. */ + && (FRAME_VISIBLE_P (f1) || FRAME_ICONIFIED_P (f1) + /* Allow deleting the terminal frame when at least one + X frame exists. */ + || (FRAME_WINDOW_P (f1) && !FRAME_WINDOW_P (f)))) + return true; + } } - return 0; + + return false; } /* Make sure that minibuf_window doesn't refer to FRAME's minibuffer @@ -1518,53 +1669,65 @@ check_minibuf_window (Lisp_Object frame, int select) } -/* Delete FRAME. When FORCE equals Qnoelisp, delete FRAME - unconditionally. x_connection_closed and delete_terminal use - this. Any other value of FORCE implements the semantics - described for Fdelete_frame. */ +/** + * delete_frame: + * + * Delete FRAME. When FORCE equals Qnoelisp, delete FRAME + * unconditionally. x_connection_closed and delete_terminal use this. + * Any other value of FORCE implements the semantics described for + * Fdelete_frame. */ Lisp_Object delete_frame (Lisp_Object frame, Lisp_Object force) { struct frame *f = decode_any_frame (frame); struct frame *sf; struct kboard *kb; - + Lisp_Object frames, frame1; int minibuffer_selected, is_tooltip_frame; + bool nochild = !FRAME_PARENT_FRAME (f); - if (! FRAME_LIVE_P (f)) + if (!FRAME_LIVE_P (f)) return Qnil; - - if (NILP (force) && !other_visible_frames (f)) - error ("Attempt to delete the sole visible or iconified frame"); - - /* x_connection_closed must have set FORCE to `noelisp' in order - to delete the last frame, if it is gone. */ - if (NILP (XCDR (Vframe_list)) && !EQ (force, Qnoelisp)) - error ("Attempt to delete the only frame"); + else if (!EQ (force, Qnoelisp) && !other_frames (f, !NILP (force))) + { + if (NILP (force)) + error ("Attempt to delete the sole visible or iconified frame"); + else + error ("Attempt to delete the only frame"); + } XSETFRAME (frame, f); + /* Softly delete all frames with this frame as their parent frame or + as their `delete-before' frame parameter value. */ + FOR_EACH_FRAME (frames, frame1) + if (FRAME_PARENT_FRAME (XFRAME (frame1)) == f + /* Process `delete-before' parameter iff FRAME is not a child + frame. This avoids that we enter an infinite chain of mixed + dependencies. */ + || (nochild + && EQ (get_frame_param (XFRAME (frame1), Qdelete_before), frame))) + delete_frame (frame1, Qnil); + /* Does this frame have a minibuffer, and is it the surrogate minibuffer for any other frame? */ if (FRAME_HAS_MINIBUF_P (f)) { - Lisp_Object frames, this; - - FOR_EACH_FRAME (frames, this) + FOR_EACH_FRAME (frames, frame1) { Lisp_Object fminiw; - if (EQ (this, frame)) + if (EQ (frame1, frame)) continue; - fminiw = FRAME_MINIBUF_WINDOW (XFRAME (this)); + fminiw = FRAME_MINIBUF_WINDOW (XFRAME (frame1)); if (WINDOWP (fminiw) && EQ (frame, WINDOW_FRAME (XWINDOW (fminiw)))) { /* If we MUST delete this frame, delete the other first. But do this only if FORCE equals `noelisp'. */ if (EQ (force, Qnoelisp)) - delete_frame (this, Qnoelisp); + delete_frame (frame1, Qnoelisp); else error ("Attempt to delete a surrogate minibuffer frame"); } @@ -1593,20 +1756,26 @@ delete_frame (Lisp_Object frame, Lisp_Object force) safe_call2 (Qrun_hook_with_args, Qdelete_frame_functions, frame); } - /* The hook may sometimes (indirectly) cause the frame to be deleted. */ - if (! FRAME_LIVE_P (f)) + /* delete_frame_functions may have deleted any frame, including this + one. */ + if (!FRAME_LIVE_P (f)) return Qnil; + else if (!EQ (force, Qnoelisp) && !other_frames (f, !NILP (force))) + { + if (NILP (force)) + error ("Attempt to delete the sole visible or iconified frame"); + else + error ("Attempt to delete the only frame"); + } /* At this point, we are committed to deleting the frame. There is no more chance for errors to prevent it. */ - minibuffer_selected = EQ (minibuf_window, selected_window); sf = SELECTED_FRAME (); /* Don't let the frame remain selected. */ if (f == sf) { Lisp_Object tail; - Lisp_Object frame1 = Qnil; /* Look for another visible frame on the same terminal. Do not call next_frame here because it may loop forever. @@ -1746,16 +1915,15 @@ delete_frame (Lisp_Object frame, Lisp_Object force) another one. */ if (f == last_nonminibuf_frame) { - Lisp_Object frames, this; - last_nonminibuf_frame = 0; - FOR_EACH_FRAME (frames, this) + FOR_EACH_FRAME (frames, frame1) { - f = XFRAME (this); - if (!FRAME_MINIBUF_ONLY_P (f)) + struct frame *f1 = XFRAME (frame1); + + if (!FRAME_MINIBUF_ONLY_P (f1)) { - last_nonminibuf_frame = f; + last_nonminibuf_frame = f1; break; } } @@ -1765,13 +1933,12 @@ delete_frame (Lisp_Object frame, Lisp_Object force) single-kboard state if we're in it for this kboard. */ if (kb != NULL) { - Lisp_Object frames, this; /* Some frame we found on the same kboard, or nil if there are none. */ Lisp_Object frame_on_same_kboard = Qnil; - FOR_EACH_FRAME (frames, this) - if (kb == FRAME_KBOARD (XFRAME (this))) - frame_on_same_kboard = this; + FOR_EACH_FRAME (frames, frame1) + if (kb == FRAME_KBOARD (XFRAME (frame1))) + frame_on_same_kboard = frame1; if (NILP (frame_on_same_kboard)) not_single_kboard_state (kb); @@ -1783,29 +1950,27 @@ delete_frame (Lisp_Object frame, Lisp_Object force) frames with other windows. */ if (kb != NULL && EQ (frame, KVAR (kb, Vdefault_minibuffer_frame))) { - Lisp_Object frames, this; - /* The last frame we saw with a minibuffer, minibuffer-only or not. */ Lisp_Object frame_with_minibuf = Qnil; /* Some frame we found on the same kboard, or nil if there are none. */ Lisp_Object frame_on_same_kboard = Qnil; - FOR_EACH_FRAME (frames, this) + FOR_EACH_FRAME (frames, frame1) { - struct frame *f1 = XFRAME (this); + struct frame *f1 = XFRAME (frame1); /* Consider only frames on the same kboard and only those with minibuffers. */ if (kb == FRAME_KBOARD (f1) && FRAME_HAS_MINIBUF_P (f1)) { - frame_with_minibuf = this; + frame_with_minibuf = frame1; if (FRAME_MINIBUF_ONLY_P (f1)) break; } if (kb == FRAME_KBOARD (f1)) - frame_on_same_kboard = this; + frame_on_same_kboard = frame1; } if (!NILP (frame_on_same_kboard)) @@ -2118,7 +2283,7 @@ displayed in the terminal. */) { struct frame *f = decode_live_frame (frame); - if (NILP (force) && !other_visible_frames (f)) + if (NILP (force) && !other_frames (f, true)) error ("Attempt to make invisible the sole visible or iconified frame"); /* Don't allow minibuf_window to remain on an invisible frame. */ @@ -2453,9 +2618,39 @@ store_frame_param (struct frame *f, Lisp_Object prop, Lisp_Object val) } } + /* Check these parameters for circular dependeny. This does not check + for interdependencies between these properties. Hence you can + still create circular dependencies with different properties, for + example a chain of frames F1->F2->...Fn such that F1 is an ancestor + frame of Fn and thus cannot be deleted before Fn and a second chain + Fn->Fn-1->...F1 such that Fn cannot be deleted before F1. */ + else if (EQ (prop, Qparent_frame) || EQ (prop, Qdelete_before)) + { + Lisp_Object oldval = Fcdr (Fassq (prop, f->param_alist)); + + if (!EQ (oldval, val) && !NILP (val)) + { + Lisp_Object frame; + Lisp_Object frame1 = val; + + if (!FRAMEP (frame1) || !FRAME_LIVE_P (XFRAME (frame1))) + error ("Invalid `%s' frame parameter", + SSDATA (SYMBOL_NAME (prop))); + + XSETFRAME (frame, f); + + while (FRAMEP (frame1) && FRAME_LIVE_P (XFRAME (frame1))) + if (EQ (frame1, frame)) + error ("Circular specification of `%s' frame parameter", + SSDATA (SYMBOL_NAME (prop))); + else + frame1 = get_frame_param (XFRAME (frame1), prop); + } + } + /* The buffer-list parameters are stored in a special place and not in the alist. All buffers must be live. */ - if (EQ (prop, Qbuffer_list)) + else if (EQ (prop, Qbuffer_list)) { Lisp_Object list = Qnil; for (; CONSP (val); val = XCDR (val)) @@ -2464,7 +2659,7 @@ store_frame_param (struct frame *f, Lisp_Object prop, Lisp_Object val) fset_buffer_list (f, Fnreverse (list)); return; } - if (EQ (prop, Qburied_buffer_list)) + else if (EQ (prop, Qburied_buffer_list)) { Lisp_Object list = Qnil; for (; CONSP (val); val = XCDR (val)) @@ -3095,6 +3290,13 @@ static const struct frame_parm_table frame_parms[] = {"sticky", SYMBOL_INDEX (Qsticky)}, {"tool-bar-position", SYMBOL_INDEX (Qtool_bar_position)}, {"inhibit-double-buffering", SYMBOL_INDEX (Qinhibit_double_buffering)}, + {"undecorated", SYMBOL_INDEX (Qundecorated)}, + {"parent-frame", SYMBOL_INDEX (Qparent_frame)}, + {"skip-taskbar", SYMBOL_INDEX (Qskip_taskbar)}, + {"no-focus-on-map", SYMBOL_INDEX (Qno_focus_on_map)}, + {"no-accept-focus", SYMBOL_INDEX (Qno_accept_focus)}, + {"z-group", SYMBOL_INDEX (Qz_group)}, + {"override-redirect", SYMBOL_INDEX (Qoverride_redirect)}, }; #ifdef HAVE_WINDOW_SYSTEM @@ -4882,6 +5084,14 @@ syms_of_frame (void) DEFSYM (Qheight, "height"); DEFSYM (Qicon, "icon"); DEFSYM (Qminibuffer, "minibuffer"); + DEFSYM (Qundecorated, "undecorated"); + DEFSYM (Qparent_frame, "parent-frame"); + DEFSYM (Qskip_taskbar, "skip-taskbar"); + DEFSYM (Qno_focus_on_map, "no-focus-on-map"); + DEFSYM (Qno_accept_focus, "no-accept-focus"); + DEFSYM (Qz_group, "z-group"); + DEFSYM (Qoverride_redirect, "override-redirect"); + DEFSYM (Qdelete_before, "delete-before"); DEFSYM (Qmodeline, "modeline"); DEFSYM (Qonly, "only"); DEFSYM (Qnone, "none"); @@ -4978,6 +5188,7 @@ syms_of_frame (void) DEFSYM (Qauto_raise, "auto-raise"); DEFSYM (Qborder_color, "border-color"); DEFSYM (Qborder_width, "border-width"); + DEFSYM (Qouter_border_width, "outer-border-width"); DEFSYM (Qbottom_divider_width, "bottom-divider-width"); DEFSYM (Qcursor_color, "cursor-color"); DEFSYM (Qcursor_type, "cursor-type"); @@ -5011,6 +5222,12 @@ syms_of_frame (void) DEFSYM (Qvisibility, "visibility"); DEFSYM (Qwait_for_wm, "wait-for-wm"); DEFSYM (Qinhibit_double_buffering, "inhibit-double-buffering"); + DEFSYM (Qno_other_frame, "no-other-frame"); + DEFSYM (Qbelow, "below"); + DEFSYM (Qabove_suspended, "above-suspended"); + DEFSYM (Qmin_width, "min-width"); + DEFSYM (Qmin_height, "min-height"); + DEFSYM (Qmouse_wheel_frame, "mouse-wheel-frame"); { int i; @@ -5179,12 +5396,51 @@ displayed. This variable is local to the current terminal and cannot be buffer-local. */); - DEFVAR_BOOL ("focus-follows-mouse", focus_follows_mouse, + DEFVAR_LISP ("focus-follows-mouse", focus_follows_mouse, doc: /* Non-nil if window system changes focus when you move the mouse. You should set this variable to tell Emacs how your window manager handles focus, since there is no way in general for Emacs to find out -automatically. See also `mouse-autoselect-window'. */); - focus_follows_mouse = 0; +automatically. + +There are three meaningful values: + +- The default nil should be used when your window manager follows a + "click-to-focus" policy where you have to click the mouse inside of a + frame in order for that frame to get focus. + +- The value t should be used when your window manager has the focus + automatically follow the position of the mouse pointer but a window + that gains focus is not raised automatically. + +- The value `auto-raise' should be used when your window manager has the + focus automatically follow the position of the mouse pointer and a + window that gains focus is raised automatically. + +If this option is non-nil, Emacs moves the mouse pointer to the frame +selected by `select-frame-set-input-focus'. This function is used by a +number of commands like, for example, `other-frame' and `pop-to-buffer'. +If this option is nil and your focus follows mouse window manager does +not autonomously move the mouse pointer to the newly selected frame, the +previously selected window manager window might get reselected instead +immediately. + +The distinction between the values t and `auto-raise' is not needed for +"normal" frames because the window manager takes care of raising them. +Setting this to `auto-raise' will, however, override the standard +behavior of a window manager that does not automatically raise the frame +that gets focus. Setting this to `auto-raise' is also necessary to +automatically raise child frames which are usually left alone by the +window manager. + +Note that this option does not distinguish "sloppy" focus (where the +frame that previously had focus retains focus as long as the mouse +pointer does not move into another window manager window) from "strict" +focus (where a frame immediately loses focus when it's left by the mouse +pointer). + +In order to extend a "focus follows mouse" policy to individual Emacs +windows, customize the variable `mouse-autoselect-window'. */); + focus_follows_mouse = Qnil; DEFVAR_BOOL ("frame-resize-pixelwise", frame_resize_pixelwise, doc: /* Non-nil means resize frames pixelwise. @@ -5286,6 +5542,8 @@ Gtk+ tooltips are not used) and on Windows. */); defsubr (&Sselect_frame); defsubr (&Sselected_frame); defsubr (&Sframe_list); + defsubr (&Sframe_parent); + defsubr (&Sframe_ancestor_p); defsubr (&Snext_frame); defsubr (&Sprevious_frame); defsubr (&Slast_nonminibuf_frame); diff --git a/src/frame.h b/src/frame.h index 5f18901a17..376df52846 100644 --- a/src/frame.h +++ b/src/frame.h @@ -45,6 +45,13 @@ enum fullscreen_type #endif }; +enum z_group +{ + z_group_none, + z_group_above, + z_group_below, + z_group_above_suspended, +}; #endif /* HAVE_WINDOW_SYSTEM */ /* The structure representing a frame. */ @@ -68,6 +75,11 @@ struct frame Usually it is nil. */ Lisp_Object title; +#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_NS) + /* This frame's parent frame, if it has one. */ + Lisp_Object parent_frame; +#endif /* HAVE_WINDOW_SYSTEM and not HAVE_NS */ + /* The frame which should receive keystrokes that occur in this frame, or nil if they should go to the frame itself. This is usually nil, but if the frame is minibufferless, we can use this @@ -320,6 +332,30 @@ struct frame bool_bf horizontal_scroll_bars : 1; #endif /* HAVE_WINDOW_SYSTEM */ +#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_NS) + /* True if this is an undecorated frame. */ + bool_bf undecorated : 1; + +#ifndef HAVE_NTGUI + /* True if this is an override_redirect frame. */ + bool_bf override_redirect : 1; +#endif + + /* Nonzero if this frame's icon should not appear on its display's taskbar. */ + bool_bf skip_taskbar : 1; + + /* Nonzero if this frame's window F's X window does not want to + receive input focus when it is mapped. */ + bool_bf no_focus_on_map : 1; + + /* Nonzero if this frame's window does not want to receive input focus + via mouse clicks or by moving the mouse into it. */ + bool_bf no_accept_focus : 1; + + /* The z-group this frame's window belongs to. */ + ENUM_BF (z_group) z_group : 2; +#endif /* HAVE_WINDOW_SYSTEM and not HAVE_NS */ + /* Whether new_height and new_width shall be interpreted in pixels. */ bool_bf new_pixelwise : 1; @@ -534,6 +570,13 @@ fset_face_alist (struct frame *f, Lisp_Object val) { f->face_alist = val; } +#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_NS) +INLINE void +fset_parent_frame (struct frame *f, Lisp_Object val) +{ + f->parent_frame = val; +} +#endif INLINE void fset_focus_frame (struct frame *f, Lisp_Object val) { @@ -854,7 +897,6 @@ default_pixels_per_inch_y (void) #define FRAME_FOCUS_FRAME(f) f->focus_frame #ifdef HAVE_WINDOW_SYSTEM - /* This frame slot says whether scroll bars are currently enabled for frame F, and which side they are on. */ #define FRAME_VERTICAL_SCROLL_BAR_TYPE(f) ((f)->vertical_scroll_bar_type) @@ -864,17 +906,47 @@ default_pixels_per_inch_y (void) ((f)->vertical_scroll_bar_type == vertical_scroll_bar_left) #define FRAME_HAS_VERTICAL_SCROLL_BARS_ON_RIGHT(f) \ ((f)->vertical_scroll_bar_type == vertical_scroll_bar_right) - #else /* not HAVE_WINDOW_SYSTEM */ - /* If there is no window system, there are no scroll bars. */ #define FRAME_VERTICAL_SCROLL_BAR_TYPE(f) ((void) f, vertical_scroll_bar_none) #define FRAME_HAS_VERTICAL_SCROLL_BARS(f) ((void) f, 0) #define FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT(f) ((void) f, 0) #define FRAME_HAS_VERTICAL_SCROLL_BARS_ON_RIGHT(f) ((void) f, 0) - #endif /* HAVE_WINDOW_SYSTEM */ +#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_NS) +#define FRAME_UNDECORATED(f) ((f)->undecorated) +#ifdef HAVE_NTGUI +#define FRAME_OVERRIDE_REDIRECT(f) ((void) f, 0) +#else +#define FRAME_OVERRIDE_REDIRECT(f) ((f)->override_redirect) +#endif +#define FRAME_PARENT_FRAME(f) \ + (NILP ((f)->parent_frame) \ + ? NULL \ + : XFRAME ((f)->parent_frame)) +#define FRAME_SKIP_TASKBAR(f) ((f)->skip_taskbar) +#define FRAME_NO_FOCUS_ON_MAP(f) ((f)->no_focus_on_map) +#define FRAME_NO_ACCEPT_FOCUS(f) ((f)->no_accept_focus) +#define FRAME_Z_GROUP(f) ((f)->z_group) +#define FRAME_Z_GROUP_NONE(f) ((f)->z_group == z_group_none) +#define FRAME_Z_GROUP_ABOVE(f) ((f)->z_group == z_group_above) +#define FRAME_Z_GROUP_ABOVE_SUSPENDED(f) \ + ((f)->z_group == z_group_above_suspended) +#define FRAME_Z_GROUP_BELOW(f) ((f)->z_group == z_group_below) +#else /* not HAVE_WINDOW_SYSTEM or HAVE_NS */ +#define FRAME_UNDECORATED(f) ((void) f, 0) +#define FRAME_OVERRIDE_REDIRECT(f) ((void) f, 0) +#define FRAME_PARENT_FRAME(f) ((void) f, NULL) +#define FRAME_SKIP_TASKBAR(f) ((void) f, 0) +#define FRAME_NO_FOCUS_ON_MAP(f) ((void) f, 0) +#define FRAME_NO_ACCEPT_FOCUS(f) ((void) f, 0) +#define FRAME_Z_GROUP(f) ((void) f, z_group_none) +#define FRAME_Z_GROUP_NONE(f) ((void) f, true) +#define FRAME_Z_GROUP_ABOVE(f) ((void) f, false) +#define FRAME_Z_GROUP_BELOW(f) ((void) f, false) +#endif /* HAVE_WINDOW_SYSTEM and not HAVE_NS */ + /* Whether horizontal scroll bars are currently enabled for frame F. */ #if USE_HORIZONTAL_SCROLL_BARS #define FRAME_HAS_HORIZONTAL_SCROLL_BARS(f) \ @@ -1041,7 +1113,8 @@ default_pixels_per_inch_y (void) loop will set FRAME_VAR, a Lisp_Object, to each frame in Vframe_list in succession and execute the statement. LIST_VAR should be a Lisp_Object too; it is used to iterate through the - Vframe_list. + Vframe_list. Note that this macro walks over child frames and + the tooltip frame as well. This macro is a holdover from a time when multiple frames weren't always supported. An alternate definition of the macro would expand to @@ -1221,7 +1294,7 @@ FRAME_INTERNAL_BORDER_WIDTH (struct frame *f) return frame_dimension (f->internal_border_width); } -/* Pixel-size of window border lines */ +/* Pixel-size of window divider lines */ INLINE int FRAME_RIGHT_DIVIDER_WIDTH (struct frame *f) { @@ -1446,6 +1519,7 @@ extern void x_activate_menubar (struct frame *); extern void x_real_positions (struct frame *, int *, int *); extern void free_frame_menubar (struct frame *); extern void x_free_frame_resources (struct frame *); +extern bool frame_ancestor_p (struct frame *af, struct frame *df); #if defined HAVE_X_WINDOWS extern void x_wm_set_icon_position (struct frame *, int, int); diff --git a/src/gtkutil.c b/src/gtkutil.c index 63f0143641..227a062bff 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -1200,7 +1200,14 @@ xg_create_frame_widgets (struct frame *f) else if (! NILP (f->name)) title = SSDATA (ENCODE_UTF_8 (f->name)); - if (title) gtk_window_set_title (GTK_WINDOW (wtop), title); + if (title) + gtk_window_set_title (GTK_WINDOW (wtop), title); + + if (FRAME_UNDECORATED (f)) + { + gtk_window_set_decorated (GTK_WINDOW (wtop), FALSE); + store_frame_param (f, Qundecorated, Qt); + } FRAME_GTK_OUTER_WIDGET (f) = wtop; FRAME_GTK_WIDGET (f) = wfixed; @@ -1275,6 +1282,14 @@ xg_create_frame_widgets (struct frame *f) gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE); #endif + if (FRAME_OVERRIDE_REDIRECT (f)) + { + GdkWindow *gwin = gtk_widget_get_window (wtop); + + if (gwin) + gdk_window_set_override_redirect (gwin, TRUE); + } + #ifdef USE_GTK_TOOLTIP /* Steal a tool tip window we can move ourselves. */ f->output_data.x->ttip_widget = 0; @@ -1356,7 +1371,9 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position) /* Don't set size hints during initialization; that apparently leads to a race condition. See the thread at http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00033.html */ - if (NILP (Vafter_init_time) || !FRAME_GTK_OUTER_WIDGET (f)) + if (NILP (Vafter_init_time) + || !FRAME_GTK_OUTER_WIDGET (f) + || FRAME_PARENT_FRAME (f)) return; XSETFRAME (frame, f); @@ -1489,6 +1506,100 @@ xg_set_background_color (struct frame *f, unsigned long bg) } } +/* Change the frame's decoration (title bar + resize borders). This + might not work with all window managers. */ +void +xg_set_undecorated (struct frame *f, Lisp_Object undecorated) +{ + if (FRAME_GTK_WIDGET (f)) + { + block_input (); + gtk_window_set_decorated (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), + NILP (undecorated) ? TRUE : FALSE); + unblock_input (); + } +} + + +/* Restack F1 below F2, above if ABOVE_FLAG is true. This might not + work with all window managers. */ +void +xg_frame_restack (struct frame *f1, struct frame *f2, bool above_flag) +{ + block_input (); + if (FRAME_GTK_OUTER_WIDGET (f1) && FRAME_GTK_OUTER_WIDGET (f2)) + { + GdkWindow *gwin1 = gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f1)); + GdkWindow *gwin2 = gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f2)); + Lisp_Object frame1, frame2; + + XSETFRAME (frame1, f1); + XSETFRAME (frame2, f2); + + gdk_window_restack (gwin1, gwin2, above_flag); + x_sync (f1); + } + unblock_input (); +} + + +/* Don't show frame in taskbar, don't ALT-TAB to it. */ +void +xg_set_skip_taskbar (struct frame *f, Lisp_Object skip_taskbar) +{ + block_input (); + if (FRAME_GTK_WIDGET (f)) + gdk_window_set_skip_taskbar_hint + (gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f)), + NILP (skip_taskbar) ? FALSE : TRUE); + unblock_input (); +} + + +/* Don't give frame focus. */ +void +xg_set_no_focus_on_map (struct frame *f, Lisp_Object no_focus_on_map) +{ + block_input (); + if (FRAME_GTK_WIDGET (f)) + { + GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)); + gboolean gno_focus_on_map = NILP (no_focus_on_map) ? TRUE : FALSE; + + gtk_window_set_focus_on_map (gwin, gno_focus_on_map); + } + unblock_input (); +} + + +void +xg_set_no_accept_focus (struct frame *f, Lisp_Object no_accept_focus) +{ + block_input (); + if (FRAME_GTK_WIDGET (f)) + { + GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)); + gboolean gno_accept_focus = NILP (no_accept_focus) ? TRUE : FALSE; + + gtk_window_set_accept_focus (gwin, gno_accept_focus); + } + unblock_input (); +} + +void +xg_set_override_redirect (struct frame *f, Lisp_Object override_redirect) +{ + block_input (); + + if (FRAME_GTK_OUTER_WIDGET (f)) + { + GdkWindow *gwin = gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f)); + + gdk_window_set_override_redirect (gwin, NILP (override_redirect) ? FALSE : TRUE); + } + + unblock_input (); +} /* Set the frame icon to ICON_PIXMAP/MASK. This must be done with GTK functions so GTK does not overwrite the icon. */ @@ -3769,6 +3880,7 @@ xg_update_scrollbar_pos (struct frame *f, GtkWidget *wparent = gtk_widget_get_parent (wscroll); gint msl; int scale = xg_get_gdk_scale (); + bool hidden; top /= scale; left /= scale; @@ -3793,6 +3905,7 @@ xg_update_scrollbar_pos (struct frame *f, the height is less than the min size. */ gtk_widget_hide (wparent); gtk_widget_hide (wscroll); + hidden = true; } else { @@ -3807,6 +3920,15 @@ xg_update_scrollbar_pos (struct frame *f, x_clear_area (f, oldx, oldy, oldw, oldh); } + if (!hidden) + { + GtkWidget *scrollbar = xg_get_widget_from_map (scrollbar_id); + GtkWidget *webox = gtk_widget_get_parent (scrollbar); + + /* Don't obscure any child frames. */ + XLowerWindow (FRAME_X_DISPLAY (f), GTK_WIDGET_TO_X_WIN (webox)); + } + /* GTK does not redraw until the main loop is entered again, but if there are no X events pending we will not enter it. So we sync here to get some events. */ @@ -3872,6 +3994,15 @@ xg_update_horizontal_scrollbar_pos (struct frame *f, if there are no X events pending we will not enter it. So we sync here to get some events. */ + { + GtkWidget *scrollbar = + xg_get_widget_from_map (scrollbar_id); + GtkWidget *webox = gtk_widget_get_parent (scrollbar); + + /* Don't obscure any child frames. */ + XLowerWindow (FRAME_X_DISPLAY (f), GTK_WIDGET_TO_X_WIN (webox)); + } + x_sync (f); SET_FRAME_GARBAGED (f); cancel_mouse_face (f); diff --git a/src/gtkutil.h b/src/gtkutil.h index d67a7bc432..244549fc54 100644 --- a/src/gtkutil.h +++ b/src/gtkutil.h @@ -172,6 +172,13 @@ extern void xg_set_frame_icon (struct frame *f, Pixmap icon_pixmap, Pixmap icon_mask); +extern void xg_set_undecorated (struct frame *f, Lisp_Object undecorated); +extern void xg_frame_restack (struct frame *f1, struct frame *f2, bool above); +extern void xg_set_skip_taskbar (struct frame *f, Lisp_Object skip_taskbar); +extern void xg_set_no_focus_on_map (struct frame *f, Lisp_Object no_focus_on_map); +extern void xg_set_no_accept_focus (struct frame *f, Lisp_Object no_accept_focus); +extern void xg_set_override_redirect (struct frame *f, Lisp_Object override_redirect); + extern bool xg_prepare_tooltip (struct frame *f, Lisp_Object string, int *width, diff --git a/src/nsfns.m b/src/nsfns.m index 9e904c6838..e8f035f0e5 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -972,6 +972,13 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side 0, /* x_set_sticky */ 0, /* x_set_tool_bar_position */ 0, /* x_set_inhibit_double_buffering */ + 0, /* x_set_undecorated */ + 0, /* x_set_parent_frame */ + 0, /* x_set_skip_taskbar */ + 0, /* x_set_no_focus_on_map */ + 0, /* x_set_no_accept_focus */ + 0, /* x_set_z_group */ + 0, /* x_set_override_redirect */ }; @@ -1248,6 +1255,12 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side init_frame_faces (f); /* Read comment about this code in corresponding place in xfns.c. */ + tem = x_get_arg (dpyinfo, parms, Qmin_width, NULL, NULL, RES_TYPE_NUMBER); + if (NUMBERP (tem)) + store_frame_param (f, Qmin_width, tem); + tem = x_get_arg (dpyinfo, parms, Qmin_height, NULL, NULL, RES_TYPE_NUMBER); + if (NUMBERP (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, 1, Qx_create_frame_1); diff --git a/src/nsterm.m b/src/nsterm.m index b03ad52621..162980a651 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -6288,7 +6288,7 @@ - (void)mouseMoved: (NSEvent *)e if (WINDOWP (window) && !EQ (window, last_mouse_window) && !EQ (window, selected_window) - && (focus_follows_mouse + && (!NILP (focus_follows_mouse) || (EQ (XWINDOW (window)->frame, XWINDOW (selected_window)->frame)))) { diff --git a/src/w32fns.c b/src/w32fns.c index dd16d74439..f7d3b722ab 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -256,6 +256,10 @@ static unsigned int sound_type = 0xFFFFFFFF; # define WTS_SESSION_LOCK 0x7 #endif +#ifndef WS_EX_NOACTIVATE +#define WS_EX_NOACTIVATE 0x08000000L +#endif + /* Keyboard hook state data. */ static struct { @@ -367,17 +371,20 @@ void x_set_title (struct frame *, Lisp_Object, Lisp_Object); void x_real_positions (struct frame *f, int *xptr, int *yptr) { - POINT pt; RECT rect; /* Get the bounds of the WM window. */ GetWindowRect (FRAME_W32_WINDOW (f), &rect); - pt.x = 0; - pt.y = 0; + if (FRAME_PARENT_FRAME (f)) + { + /* For a child window we have to get its coordinates wrt its + parent. */ + HWND parent_hwnd = FRAME_W32_WINDOW (FRAME_PARENT_FRAME (f)); - /* Convert (0, 0) in the client area to screen co-ordinates. */ - ClientToScreen (FRAME_W32_WINDOW (f), &pt); + if (parent_hwnd) + MapWindowPoints (HWND_DESKTOP, parent_hwnd, (LPPOINT) &rect, 2); + } *xptr = rect.left; *yptr = rect.top; @@ -1682,7 +1689,7 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) most of the commands try to apply themselves to the minibuffer frame itself, and get an error because you can't switch buffers in or split the minibuffer window. */ - if (FRAME_MINIBUF_ONLY_P (f)) + if (FRAME_MINIBUF_ONLY_P (f) || FRAME_PARENT_FRAME (f)) return; if (INTEGERP (value)) @@ -1955,6 +1962,233 @@ x_set_scroll_bar_default_height (struct frame *f) FRAME_CONFIG_SCROLL_BAR_LINES (f) = (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + unit - 1) / unit; } + +/** + * x_set_undecorated: + * + * Set frame F's `undecorated' parameter. If non-nil, F's window-system + * window is drawn without decorations, title, minimize/maximize boxes + * and external borders. This usually means that the window cannot be + * dragged, resized, iconified, maximized or deleted with the mouse. If + * nil, draw the frame with all the elements listed above unless these + * have been suspended via window manager settings. + * + * Some window managers may not honor this parameter. + */ +static void +x_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) +{ + HWND hwnd = FRAME_W32_WINDOW (f); + DWORD dwStyle = GetWindowLong (hwnd, GWL_STYLE); + Lisp_Object border_width = Fcdr (Fassq (Qborder_width, f->param_alist)); + + block_input (); + if (!NILP (new_value) && !FRAME_UNDECORATED (f)) + { + dwStyle = ((dwStyle & ~WS_THICKFRAME & ~WS_CAPTION) + | ((NUMBERP (border_width) && (XINT (border_width) > 0)) + ? WS_BORDER : false)); + SetWindowLong (hwnd, GWL_STYLE, dwStyle); + SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE + | SWP_FRAMECHANGED); + FRAME_UNDECORATED (f) = true; + } + else if (NILP (new_value) && FRAME_UNDECORATED (f)) + { + SetWindowLong (hwnd, GWL_STYLE, dwStyle | WS_THICKFRAME | WS_CAPTION + | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU); + SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE + | SWP_FRAMECHANGED); + FRAME_UNDECORATED (f) = false; + } + unblock_input (); +} + +/** + * x_set_parent_frame: + * + * Set frame F's `parent-frame' parameter. If non-nil, make F a child + * frame of the frame specified by that parameter. Technically, this + * makes F's window-system window a child window of the parent frame's + * window-system window. If nil, make F's window-system window a + * top-level window--a child of its display's root window. + * + * A child frame is clipped at the native edges of its parent frame. + * Its `left' and `top' parameters specify positions relative to the + * top-left corner of its parent frame's native rectangle. Usually, + * moving a parent frame moves all its child frames too, keeping their + * position relative to the parent unaltered. When a parent frame is + * iconified or made invisible, its child frames are made invisible. + * When a parent frame is deleted, its child frames are deleted too. + * + * A visible child frame always appears on top of its parent frame thus + * obscuring parts of it. When a frame has more than one child frame, + * their stacking order is specified just as that of non-child frames + * relative to their display. + * + * Whether a child frame has a menu or tool bar may be window-system or + * window manager dependent. It's advisable to disable both via the + * frame parameter settings. + * + * Some window managers may not honor this parameter. + */ +static void +x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) +{ + struct frame *p = NULL; + + if (!NILP (new_value) + && (!FRAMEP (new_value) + || !FRAME_LIVE_P (p = XFRAME (new_value)) + || !FRAME_W32_P (p))) + { + store_frame_param (f, Qparent_frame, old_value); + error ("Invalid specification of `parent-frame'"); + } + + if (p != FRAME_PARENT_FRAME (f)) + { + HWND hwnd = FRAME_W32_WINDOW (f); + HWND hwnd_parent = p ? FRAME_W32_WINDOW (p) : NULL; + HWND hwnd_value; + + block_input (); + hwnd_value = SetParent (hwnd, hwnd_parent); + unblock_input (); + + if (hwnd_value) + fset_parent_frame (f, new_value); + else + { + store_frame_param (f, Qparent_frame, old_value); + error ("Reparenting frame failed"); + } + } +} + +/** + * x_set_skip_taskbar: + * + * Set frame F's `skip-taskbar' parameter. If non-nil, this should + * remove F's icon from the taskbar associated with the display of F's + * window-system window and inhibit switching to F's window via + * -. On Windows iconifying F will "roll in" its window at + * the bottom of the desktop. If nil, lift these restrictions. + * + * Some window managers may not honor this parameter. + */ +static void +x_set_skip_taskbar (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) +{ + if (!EQ (new_value, old_value)) + { + HWND hwnd = FRAME_W32_WINDOW (f); + DWORD exStyle = GetWindowLong (hwnd, GWL_EXSTYLE); + + block_input (); + /* Temporarily hide the window while changing its WS_EX_NOACTIVATE + setting. */ + ShowWindow (hwnd, SW_HIDE); + if (!NILP (new_value)) + SetWindowLong (hwnd, GWL_EXSTYLE, exStyle | WS_EX_NOACTIVATE); + else + SetWindowLong (hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_NOACTIVATE); + ShowWindow (hwnd, SW_SHOWNOACTIVATE); + unblock_input (); + + FRAME_SKIP_TASKBAR (f) = !NILP (new_value); + } +} + +/** + * x_set_no_focus_on_map: + * + * Set frame F's `no-focus-on-map' parameter which, if non-nil, means + * that F's window-system window does not want to receive input focus + * when it is mapped. (A frame's window is mapped when the frame is + * displayed for the first time and when the frame changes its state + * from `iconified' or `invisible' to `visible'.) + * + * Some window managers may not honor this parameter. + */ +static void +x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) +{ + if (!EQ (new_value, old_value)) + FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value); +} + +/** + * x_set_no_accept_focus: + * + * Set frame F's `no-accept-focus' parameter which, if non-nil, hints + * that F's window-system window does not want to receive input focus + * via mouse clicks or by moving the mouse into it. + * + * If non-nil, this may have the unwanted side-effect that a user cannot + * scroll a non-selected frame with the mouse. + * + * Some window managers may not honor this parameter. + */ +static void +x_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) +{ + if (!EQ (new_value, old_value)) + FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value); +} + +/** + * x_set_z_group: + * + * Set frame F's `z-group' parameter. If `above', F's window-system + * window is displayed above all windows that do not have the `above' + * property set. If nil, F's window is shown below all windows that + * have the `above' property set and above all windows that have the + * `below' property set. If `below', F's window is displayed below all + * windows that do not have the `below' property set. + * + * Some window managers may not honor this parameter. The value `below' + * is not supported on Windows. + */ +static void +x_set_z_group (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) +{ + HWND hwnd = FRAME_W32_WINDOW (f); + + if (NILP (new_value)) + { + block_input (); + SetWindowPos (hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE + | SWP_NOOWNERZORDER); + unblock_input (); + FRAME_Z_GROUP (f) = z_group_none; + } + else if (EQ (new_value, Qabove)) + { + block_input (); + SetWindowPos (hwnd, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE + | SWP_NOOWNERZORDER); + unblock_input (); + FRAME_Z_GROUP (f) = z_group_above; + } + else if (EQ (new_value, Qabove_suspended)) + { + block_input (); + SetWindowPos (hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE + | SWP_NOOWNERZORDER); + unblock_input (); + FRAME_Z_GROUP (f) = z_group_above_suspended; + } + else if (EQ (new_value, Qbelow)) + error ("Value `below' for z-group is not supported on Windows"); + else + error ("Invalid z-group specification"); +} /* Subroutines for creating a frame. */ @@ -2013,7 +2247,12 @@ w32_init_class (HINSTANCE hinst) static HWND w32_createvscrollbar (struct frame *f, struct scroll_bar * bar) { - return CreateWindow ("SCROLLBAR", "", SBS_VERT | WS_CHILD | WS_VISIBLE, + return CreateWindow ("SCROLLBAR", "", + /* Clip siblings so we don't draw over child + frames. Apparently this is not always + sufficient so we also try to make bar windows + bottommost. */ + SBS_VERT | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, /* Position and size of scroll bar. */ bar->left, bar->top, bar->width, bar->height, FRAME_W32_WINDOW (f), NULL, hinst, NULL); @@ -2022,7 +2261,12 @@ w32_createvscrollbar (struct frame *f, struct scroll_bar * bar) static HWND w32_createhscrollbar (struct frame *f, struct scroll_bar * bar) { - return CreateWindow ("SCROLLBAR", "", SBS_HORZ | WS_CHILD | WS_VISIBLE, + return CreateWindow ("SCROLLBAR", "", + /* Clip siblings so we don't draw over child + frames. Apparently this is not always + sufficient so we also try to make bar windows + bottommost. */ + SBS_HORZ | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, /* Position and size of scroll bar. */ bar->left, bar->top, bar->width, bar->height, FRAME_W32_WINDOW (f), NULL, hinst, NULL); @@ -2031,20 +2275,55 @@ w32_createhscrollbar (struct frame *f, struct scroll_bar * bar) static void w32_createwindow (struct frame *f, int *coords) { - HWND hwnd; + HWND hwnd = NULL, parent_hwnd = NULL; RECT rect; - int top; - int left; + DWORD dwStyle; + int top, left; + Lisp_Object border_width = Fcdr (Fassq (Qborder_width, f->param_alist)); + + if (FRAME_PARENT_FRAME (f) && FRAME_W32_P (FRAME_PARENT_FRAME (f))) + { + parent_hwnd = FRAME_W32_WINDOW (FRAME_PARENT_FRAME (f)); + f->output_data.w32->dwStyle = WS_CHILD | WS_CLIPSIBLINGS; + + if (FRAME_UNDECORATED (f)) + { + /* If we want a thin border, specify it here. */ + if (NUMBERP (border_width) && (XINT (border_width) > 0)) + f->output_data.w32->dwStyle = + f->output_data.w32->dwStyle | WS_BORDER; + } + else + /* To decorate a child frame, list all needed elements. */ + f->output_data.w32->dwStyle = + f->output_data.w32->dwStyle | WS_THICKFRAME | WS_CAPTION + | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU; + } + else if (FRAME_UNDECORATED (f)) + { + /* All attempts to start with ~WS_OVERLAPPEDWINDOW or overlapped + with all other style elements negated failed here. */ + f->output_data.w32->dwStyle = WS_POPUP; + + /* If we want a thin border, specify it here. */ + if (NUMBERP (border_width) && (XINT (border_width) > 0)) + f->output_data.w32->dwStyle = + f->output_data.w32->dwStyle | WS_BORDER; + } + else + f->output_data.w32->dwStyle = WS_OVERLAPPEDWINDOW; + + /* Always clip children. */ + f->output_data.w32->dwStyle = f->output_data.w32->dwStyle | WS_CLIPCHILDREN; rect.left = rect.top = 0; rect.right = FRAME_PIXEL_WIDTH (f); rect.bottom = FRAME_PIXEL_HEIGHT (f); AdjustWindowRect (&rect, f->output_data.w32->dwStyle, - FRAME_EXTERNAL_MENU_BAR (f)); + FRAME_EXTERNAL_MENU_BAR (f) && !parent_hwnd); /* Do first time app init */ - w32_init_class (hinst); if (f->size_hint_flags & USPosition || f->size_hint_flags & PPosition) @@ -2059,18 +2338,16 @@ w32_createwindow (struct frame *f, int *coords) } FRAME_W32_WINDOW (f) = hwnd - = CreateWindow (EMACS_CLASS, - f->namebuf, - f->output_data.w32->dwStyle | WS_CLIPCHILDREN, - left, top, - rect.right - rect.left, rect.bottom - rect.top, - NULL, - NULL, - hinst, - NULL); + = CreateWindow (EMACS_CLASS, f->namebuf, f->output_data.w32->dwStyle, + left, top, rect.right - rect.left, rect.bottom - rect.top, + parent_hwnd, NULL, hinst, NULL); if (hwnd) { + if (FRAME_SKIP_TASKBAR (f)) + SetWindowLong (hwnd, GWL_EXSTYLE, + GetWindowLong (hwnd, GWL_EXSTYLE) | WS_EX_NOACTIVATE); + SetWindowLong (hwnd, WND_FONTWIDTH_INDEX, FRAME_COLUMN_WIDTH (f)); SetWindowLong (hwnd, WND_LINEHEIGHT_INDEX, FRAME_LINE_HEIGHT (f)); SetWindowLong (hwnd, WND_BORDER_INDEX, FRAME_INTERNAL_BORDER_WIDTH (f)); @@ -2086,6 +2363,12 @@ w32_createwindow (struct frame *f, int *coords) /* Update frame positions. */ GetWindowRect (hwnd, &rect); + + if (parent_hwnd) + /* For a child window we have to get its coordinates wrt its + parent. */ + MapWindowPoints (HWND_DESKTOP, parent_hwnd, (LPPOINT) &rect, 2); + f->left_pos = rect.left; f->top_pos = rect.top; } @@ -4381,6 +4664,22 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } } + if (f && (msg == WM_LBUTTONDOWN || msg == WM_RBUTTONDOWN + || msg == WM_MBUTTONDOWN ||msg == WM_XBUTTONDOWN) + && !FRAME_NO_ACCEPT_FOCUS (f)) + /* When clicking into a child frame or when clicking into a + parent frame with the child frame selected and + `no-accept-focus' is not set, select the clicked frame. */ + { + struct frame *p = FRAME_PARENT_FRAME (XFRAME (selected_frame)); + + if (FRAME_PARENT_FRAME (f) || f == p) + { + SetFocus (hwnd); + SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } + } + wmsg.dwModifiers = w32_get_modifiers (); my_post_msg (&wmsg, hwnd, msg, wParam, lParam); signal_user_input (); @@ -4486,6 +4785,10 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) if (w32_pass_multimedia_buttons_to_system) goto dflt; /* Otherwise, pass to lisp, the same way we do with mousehwheel. */ + + /* FIXME!!! This is never reached so what's the purpose? If the + non-zero return remark below is right we're doing it wrong all + the time. */ case WM_MOUSEHWHEEL: wmsg.dwModifiers = w32_get_modifiers (); my_post_msg (&wmsg, hwnd, msg, wParam, lParam); @@ -4712,19 +5015,34 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } return 0; -#if 0 + case WM_MOUSEACTIVATE: + /* WM_MOUSEACTIVATE is the only way on Windows to implement the + `no-accept-focus' frame parameter. This means that one can't + use the mouse to scroll a window on a non-selected frame. */ + /* Still not right - can't distinguish between clicks in the client area of the frame from clicks forwarded from the scroll bars - may have to hook WM_NCHITTEST to remember the mouse - position and then check if it is in the client area ourselves. */ - case WM_MOUSEACTIVATE: + position and then check if it is in the client area + ourselves. */ + /* Discard the mouse click that activates a frame, allowing the user to click anywhere without changing point (or worse!). Don't eat mouse clicks on scrollbars though!! */ - if (LOWORD (lParam) == HTCLIENT ) - return MA_ACTIVATEANDEAT; + + if ((f = x_window_to_frame (dpyinfo, hwnd)) + && FRAME_NO_ACCEPT_FOCUS (f) + /* Ignore child frames, they don't accept focus anyway. */ + && !FRAME_PARENT_FRAME (f)) + { + Lisp_Object frame; + + XSETFRAME (frame, f); + if (!EQ (selected_frame, frame)) + /* Don't discard the message, GTK doesn't either. */ + return MA_NOACTIVATE; /* ANDEAT; */ + } goto dflt; -#endif case WM_MOUSELEAVE: /* No longer tracking mouse. */ @@ -4903,6 +5221,10 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) AttachThreadInput (GetCurrentThreadId (), foreground_thread, FALSE); + /* SetFocus to give/remove focus to/from a child window. */ + if (msg == WM_EMACS_SETFOREGROUND) + SetFocus ((HWND) wParam); + return retval; } @@ -5134,7 +5456,8 @@ w32_window (struct frame *f, long window_prompting, bool minibuffer_only) unblock_input (); - if (!minibuffer_only && FRAME_EXTERNAL_MENU_BAR (f)) + if (!minibuffer_only && FRAME_EXTERNAL_MENU_BAR (f) + && !FRAME_PARENT_FRAME (f)) initialize_frame_menubar (f); if (FRAME_W32_WINDOW (f) == 0) @@ -5322,7 +5645,7 @@ This function is an internal primitive--use `make-frame' instead. */) ptrdiff_t count = SPECPDL_INDEX (); Lisp_Object display; struct w32_display_info *dpyinfo = NULL; - Lisp_Object parent; + Lisp_Object parent, parent_frame; struct kboard *kb; int x_width = 0, x_height = 0; @@ -5359,10 +5682,11 @@ 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, parameters, Qparent_id, NULL, NULL, RES_TYPE_NUMBER); + parent = x_get_arg (dpyinfo, parameters, Qparent_id, NULL, NULL, + RES_TYPE_NUMBER); if (EQ (parent, Qunbound)) parent = Qnil; - if (! NILP (parent)) + else if (!NILP (parent)) CHECK_NUMBER (parent); /* make_frame_without_minibuffer can run Lisp code and garbage collect. */ @@ -5385,6 +5709,31 @@ This function is an internal primitive--use `make-frame' instead. */) XSETFRAME (frame, f); + parent_frame = x_get_arg (dpyinfo, parameters, Qparent_frame, NULL, NULL, + RES_TYPE_SYMBOL); + /* Apply `parent-frame' parameter only when no `parent-id' was + specified. */ + if (!NILP (parent_frame) + && (!NILP (parent) + || !FRAMEP (parent_frame) + || !FRAME_LIVE_P (XFRAME (parent_frame)) + || !FRAME_W32_P (XFRAME (parent_frame)))) + parent_frame = Qnil; + + fset_parent_frame (f, parent_frame); + store_frame_param (f, Qparent_frame, parent_frame); + + tem = x_get_arg (dpyinfo, parameters, Qundecorated, NULL, NULL, + RES_TYPE_BOOLEAN); + FRAME_UNDECORATED (f) = !NILP (tem) && !EQ (tem, Qunbound); + store_frame_param (f, Qundecorated, FRAME_UNDECORATED (f) ? Qt : Qnil); + + tem = x_get_arg (dpyinfo, parameters, Qskip_taskbar, NULL, NULL, + RES_TYPE_BOOLEAN); + FRAME_SKIP_TASKBAR (f) = !NILP (tem) && !EQ (tem, Qunbound); + store_frame_param (f, Qskip_taskbar, + (NILP (tem) || EQ (tem, Qunbound)) ? Qnil : Qt); + /* By default, make scrollbars the system standard width and height. */ FRAME_CONFIG_SCROLL_BAR_WIDTH (f) = GetSystemMetrics (SM_CXVSCROLL); FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) = GetSystemMetrics (SM_CXHSCROLL); @@ -5412,7 +5761,9 @@ This function is an internal primitive--use `make-frame' instead. */) dpyinfo_refcount = dpyinfo->reference_count; #endif /* GLYPH_DEBUG */ - /* Specify the parent under which to make this window. */ + /* Specify the parent under which to make this window - this seems to + have no effect on Windows because parent_desc is explicitly reset + below. */ if (!NILP (parent)) { /* Cast to UINT_PTR shuts up compiler warnings about cast to @@ -5496,23 +5847,42 @@ This function is an internal primitive--use `make-frame' instead. */) "leftFringe", "LeftFringe", RES_TYPE_NUMBER); x_default_parameter (f, parameters, Qright_fringe, Qnil, "rightFringe", "RightFringe", RES_TYPE_NUMBER); - /* Process alpha here (Bug#16619). */ - x_default_parameter (f, parameters, Qalpha, Qnil, - "alpha", "Alpha", RES_TYPE_NUMBER); + x_default_parameter (f, parameters, Qno_focus_on_map, Qnil, + NULL, NULL, RES_TYPE_BOOLEAN); + x_default_parameter (f, parameters, Qno_accept_focus, Qnil, + NULL, NULL, RES_TYPE_BOOLEAN); + + /* Process alpha here (Bug#16619). On XP this fails with child + frames. For `no-focus-on-map' frames delay processing of alpha + until the frame becomes visible. */ + if (!FRAME_NO_FOCUS_ON_MAP (f)) + x_default_parameter (f, parameters, Qalpha, Qnil, + "alpha", "Alpha", RES_TYPE_NUMBER); /* Init faces first since we need the frame's column width/line height in various occasions. */ init_frame_faces (f); - /* The following call of change_frame_size is needed since otherwise + /* 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. + 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 ... */ + 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, parameters, Qmin_width, NULL, NULL, + RES_TYPE_NUMBER); + if (NUMBERP (tem)) + store_frame_param (f, Qmin_width, tem); + tem = x_get_arg (dpyinfo, parameters, Qmin_height, NULL, NULL, + RES_TYPE_NUMBER); + if (NUMBERP (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); @@ -5520,10 +5890,17 @@ This function is an internal primitive--use `make-frame' instead. */) /* The X resources controlling the menu-bar and tool-bar are processed specially at startup, and reflected in the mode variables; ignore them here. */ - x_default_parameter (f, parameters, Qmenu_bar_lines, - NILP (Vmenu_bar_mode) - ? make_number (0) : make_number (1), - NULL, NULL, RES_TYPE_NUMBER); + if (NILP (parent_frame)) + { + x_default_parameter (f, parameters, Qmenu_bar_lines, + NILP (Vmenu_bar_mode) + ? make_number (0) : make_number (1), + NULL, NULL, RES_TYPE_NUMBER); + } + else + /* No menu bar for child frames. */ + store_frame_param (f, Qmenu_bar_lines, make_number (0)); + x_default_parameter (f, parameters, Qtool_bar_lines, NILP (Vtool_bar_mode) ? make_number (0) : make_number (1), @@ -5534,9 +5911,7 @@ This function is an internal primitive--use `make-frame' instead. */) x_default_parameter (f, parameters, Qtitle, Qnil, "title", "Title", RES_TYPE_STRING); - f->output_data.w32->dwStyle = WS_OVERLAPPEDWINDOW; f->output_data.w32->parent_desc = FRAME_DISPLAY_INFO (f)->root_window; - f->output_data.w32->text_cursor = w32_load_cursor (IDC_IBEAM); f->output_data.w32->nontext_cursor = w32_load_cursor (IDC_ARROW); f->output_data.w32->modeline_cursor = w32_load_cursor (IDC_ARROW); @@ -5601,29 +5976,36 @@ This function is an internal primitive--use `make-frame' instead. */) adjust_frame_size call. */ x_default_parameter (f, parameters, Qfullscreen, Qnil, "fullscreen", "Fullscreen", RES_TYPE_SYMBOL); + x_default_parameter (f, parameters, Qz_group, Qnil, + NULL, NULL, 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.w32->explicit_parent) + if (!f->output_data.w32->explicit_parent) { - Lisp_Object visibility; - - visibility = x_get_arg (dpyinfo, parameters, Qvisibility, 0, 0, RES_TYPE_SYMBOL); - if (EQ (visibility, Qunbound)) - visibility = Qt; + Lisp_Object visibility + = x_get_arg (dpyinfo, parameters, Qvisibility, 0, 0, RES_TYPE_SYMBOL); if (EQ (visibility, Qicon)) x_iconify_frame (f); - else if (! NILP (visibility)) - x_make_frame_visible (f); else { - /* Must have been Qnil. */ - ; + if (EQ (visibility, Qunbound)) + visibility = Qt; + + if (!NILP (visibility)) + x_make_frame_visible (f); } + + store_frame_param (f, Qvisibility, visibility); } + /* For `no-focus-on-map' frames set alpha here. */ + if (FRAME_NO_FOCUS_ON_MAP (f)) + x_default_parameter (f, parameters, Qalpha, Qnil, + "alpha", "Alpha", RES_TYPE_NUMBER); + /* Initialize `default-minibuffer-frame' in case this is the first frame on this terminal. */ if (FRAME_HAS_MINIBUF_P (f) @@ -6572,8 +6954,6 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, Lisp_Object parms) dpyinfo_refcount = dpyinfo->reference_count; #endif /* GLYPH_DEBUG */ FRAME_KBOARD (f) = kb; - f->output_data.w32->parent_desc = FRAME_DISPLAY_INFO (f)->root_window; - f->output_data.w32->explicit_parent = false; /* Set the name; the functions to which we pass f expect the name to be set. */ @@ -6639,6 +7019,7 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, Lisp_Object parms) f->output_data.w32->dwStyle = WS_BORDER | WS_POPUP | WS_DISABLED; f->output_data.w32->parent_desc = FRAME_DISPLAY_INFO (f)->root_window; + f->output_data.w32->explicit_parent = false; x_figure_window_size (f, parms, true, &x_width, &x_height); @@ -7282,6 +7663,23 @@ file_dialog_callback (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return 0; } +void +w32_dialog_in_progress (Lisp_Object in_progress) +{ + Lisp_Object frames, frame; + + /* Don't let frames in `above' z-group obscure popups. */ + FOR_EACH_FRAME (frames, frame) + { + struct frame *f = XFRAME (frame); + + if (!NILP (in_progress) && FRAME_Z_GROUP_ABOVE (f)) + x_set_z_group (f, Qabove_suspended, Qabove); + else if (NILP (in_progress) && FRAME_Z_GROUP_ABOVE_SUSPENDED (f)) + x_set_z_group (f, Qabove, Qabove_suspended); + } +} + 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 @@ -7513,8 +7911,12 @@ value of DIR as in previous invocations; this is standard Windows behavior. */) { int count = SPECPDL_INDEX (); + + w32_dialog_in_progress (Qt); + /* Prevent redisplay. */ specbind (Qinhibit_redisplay, Qt); + record_unwind_protect (w32_dialog_in_progress, Qnil); block_input (); if (use_unicode) { @@ -8559,6 +8961,136 @@ menu bar or tool bar of FRAME. */) } } +/** + * w32_frame_list_z_order: + * + * Recursively add list of all frames on the display specified via + * DPYINFO and whose window-system window's parent is specified by + * WINDOW to FRAMES and return FRAMES. + */ +static Lisp_Object +w32_frame_list_z_order (struct w32_display_info *dpyinfo, HWND window) +{ + Lisp_Object frame, frames = Qnil; + + while (window) + { + struct frame *f = x_window_to_frame (dpyinfo, window); + + if (f) + { + XSETFRAME (frame, f); + frames = Fcons (frame, frames); + } + + block_input (); + window = GetNextWindow (window, GW_HWNDNEXT); + unblock_input (); + } + + return Fnreverse (frames); +} + +DEFUN ("w32-frame-list-z-order", Fw32_frame_list_z_order, + Sw32_frame_list_z_order, 0, 1, 0, + doc: /* Return list of Emacs' frames, in Z (stacking) order. +The optional argument DISPLAY specifies which display to ask about. +DISPLAY should be either a frame or a display name (a string). If +omitted or nil, that stands for the selected frame's display. + +As a special case, if DISPLAY is non-nil and specifies a live frame, +return the child frames of that frame in Z (stacking) order. + +Frames are listed from topmost (first) to bottommost (last). */) + (Lisp_Object display) +{ + struct w32_display_info *dpyinfo = check_x_display_info (display); + HWND window; + + block_input (); + if (FRAMEP (display) && FRAME_LIVE_P (XFRAME (display))) + window = GetWindow (FRAME_W32_WINDOW (XFRAME (display)), GW_CHILD); + else + window = GetTopWindow (NULL); + unblock_input (); + + return w32_frame_list_z_order (dpyinfo, window); +} + +/** + * w32_frame_restack: + * + * Restack frame F1 below frame F2, above if ABOVE_FLAG is non-nil. In + * practice this is a two-step action: The first step removes F1's + * window-system window from the display. The second step reinserts + * F1's window below (above if ABOVE_FLAG is true) that of F2. + */ +static void +w32_frame_restack (struct frame *f1, struct frame *f2, bool above_flag) +{ + HWND hwnd1 = FRAME_W32_WINDOW (f1); + HWND hwnd2 = FRAME_W32_WINDOW (f2); + + block_input (); + if (above_flag) + /* Put F1 above F2 in the z-order. */ + { + if (GetNextWindow (hwnd1, GW_HWNDNEXT) != hwnd2) + { + /* Make sure F1 is below F2 first because we must not + change the relative position of F2 wrt any other + window but F1. */ + if (GetNextWindow (hwnd2, GW_HWNDNEXT) != hwnd1) + SetWindowPos (hwnd1, hwnd2, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE + | SWP_FRAMECHANGED); + /* Now put F1 above F2. */ + SetWindowPos (hwnd2, hwnd1, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE + | SWP_FRAMECHANGED); + } + } + else if (GetNextWindow (hwnd2, GW_HWNDNEXT) != hwnd1) + /* Put F1 below F2 in the z-order. */ + SetWindowPos (hwnd1, hwnd2, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE + | SWP_FRAMECHANGED); + unblock_input (); +} + +DEFUN ("w32-frame-restack", Fw32_frame_restack, Sw32_frame_restack, 2, 3, 0, + doc: /* Restack FRAME1 below FRAME2. +This means that if both frames are visible and the display areas of +these frames overlap, FRAME2 (partially) obscures FRAME1. If optional +third argument ABOVE is non-nil, restack FRAME1 above FRAME2. This +means that if both frames are visible and the display areas of these +frames overlap, FRAME1 (partially) obscures FRAME2. + +This may be thought of as an atomic action performed in two steps: The +first step removes FRAME1's window-system window from the display. The +second step reinserts FRAME1's window below (above if ABOVE is true) +that of FRAME2. Hence the position of FRAME2 in its display's Z +\(stacking) order relative to all other frames excluding FRAME1 remains +unaltered. + +Some window managers may refuse to restack windows. */) + (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object above) +{ + struct frame *f1 = decode_live_frame (frame1); + struct frame *f2 = decode_live_frame (frame2); + + if (FRAME_W32_P (f1) && FRAME_W32_P (f2)) + { + w32_frame_restack (f1, f2, !NILP (above)); + return Qt; + } + else + { + error ("Cannot restack frames"); + return Qnil; + } +} + DEFUN ("w32-mouse-absolute-pixel-position", Fw32_mouse_absolute_pixel_position, Sw32_mouse_absolute_pixel_position, 0, 0, 0, doc: /* Return absolute position of mouse cursor in pixels. @@ -9753,6 +10285,13 @@ frame_parm_handler w32_frame_parm_handlers[] = 0, /* x_set_sticky */ 0, /* x_set_tool_bar_position */ 0, /* x_set_inhibit_double_buffering */ + x_set_undecorated, + x_set_parent_frame, + x_set_skip_taskbar, + x_set_no_focus_on_map, + x_set_no_accept_focus, + x_set_z_group, + 0, /* x_set_override_redirect */ }; void @@ -10132,6 +10671,8 @@ tip frame. */); defsubr (&Sx_display_list); defsubr (&Sw32_frame_geometry); defsubr (&Sw32_frame_edges); + defsubr (&Sw32_frame_list_z_order); + defsubr (&Sw32_frame_restack); defsubr (&Sw32_mouse_absolute_pixel_position); defsubr (&Sw32_set_mouse_absolute_pixel_position); defsubr (&Sx_synchronize); diff --git a/src/w32font.c b/src/w32font.c index 37df1bc43c..ef6eac44a6 100644 --- a/src/w32font.c +++ b/src/w32font.c @@ -2553,11 +2553,22 @@ in the font selection dialog. */) SelectObject (hdc, oldobj); ReleaseDC (FRAME_W32_WINDOW (f), hdc); - if (!ChooseFont (&cf) - || logfont_to_fcname (&lf, cf.iPointSize, buf, 100) < 0) - return Qnil; + { + int count = SPECPDL_INDEX (); + Lisp_Object value = Qnil; + + w32_dialog_in_progress (Qt); + specbind (Qinhibit_redisplay, Qt); + record_unwind_protect (w32_dialog_in_progress, Qnil); + + if (ChooseFont (&cf) + && logfont_to_fcname (&lf, cf.iPointSize, buf, 100) >= 0) + value = DECODE_SYSTEM (build_string (buf)); - return DECODE_SYSTEM (build_string (buf)); + unbind_to (count, Qnil); + + return value; + } } static const char *const w32font_booleans [] = { diff --git a/src/w32term.c b/src/w32term.c index 31f0b4a2fa..b50f0d39a4 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -3095,7 +3095,8 @@ construct_mouse_wheel (struct input_event *result, W32Msg *msg, struct frame *f) coordinates, so cast to short to interpret them correctly. */ p.x = (short) LOWORD (msg->msg.lParam); p.y = (short) HIWORD (msg->msg.lParam); - ScreenToClient (msg->msg.hwnd, &p); + /* For the case that F's w32 window is not msg->msg.hwnd. */ + ScreenToClient (FRAME_W32_WINDOW (f), &p); XSETINT (result->x, p.x); XSETINT (result->y, p.y); XSETFRAME (result->frame_or_window, f); @@ -3446,8 +3447,22 @@ w32_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window, /* If mouse was grabbed on a frame, give coords for that frame even if the mouse is now outside it. Otherwise check for window under mouse on one of our frames. */ - f1 = (x_mouse_grabbed (dpyinfo) ? dpyinfo->last_mouse_frame - : x_any_window_to_frame (dpyinfo, WindowFromPoint (pt))); + if (x_mouse_grabbed (dpyinfo)) + f1 = dpyinfo->last_mouse_frame; + else + { + HWND wfp = WindowFromPoint (pt); + + if (wfp && (f1 = x_any_window_to_frame (dpyinfo, wfp))) + { + HWND cwfp = ChildWindowFromPoint (wfp, pt); + struct frame *f2; + + /* If cwfp exists it should be one of our windows ... */ + if (cwfp && (f2 = x_any_window_to_frame (dpyinfo, cwfp))) + f1 = f2; + } + } /* If not, is it one of our scroll bars? */ if (! f1) @@ -3897,7 +3912,10 @@ w32_set_vertical_scroll_bar (struct window *w, /* Make sure scroll bar is "visible" before moving, to ensure the area of the parent window now exposed will be refreshed. */ my_show_window (f, hwnd, SW_HIDE); - MoveWindow (hwnd, left, top, width, max (height, 1), TRUE); +/** MoveWindow (hwnd, left, top, width, max (height, 1), TRUE); **/ + /* Try to not draw over child frames. */ + SetWindowPos (hwnd, HWND_BOTTOM, left, top, width, max (height, 1), + SWP_FRAMECHANGED); si.cbSize = sizeof (si); si.fMask = SIF_RANGE; @@ -3995,7 +4013,10 @@ w32_set_horizontal_scroll_bar (struct window *w, /* Make sure scroll bar is "visible" before moving, to ensure the area of the parent window now exposed will be refreshed. */ my_show_window (f, hwnd, SW_HIDE); - MoveWindow (hwnd, left, top, width, max (height, 1), TRUE); +/** MoveWindow (hwnd, left, top, width, max (height, 1), TRUE); **/ + /* Try to not draw over child frames. */ + SetWindowPos (hwnd, HWND_BOTTOM, left, top, max (width, 1), height, + SWP_FRAMECHANGED); /* +++ SetScrollInfo +++ */ si.cbSize = sizeof (si); @@ -4649,7 +4670,7 @@ w32_read_socket (struct terminal *terminal, in that case expose_frame will do nothing, and if the various redisplay flags happen to be unset, we are left with a blank frame. */ - if (!FRAME_GARBAGED_P (f)) + if (!FRAME_GARBAGED_P (f) || FRAME_PARENT_FRAME (f)) { HDC hdc = get_frame_dc (f); @@ -4835,8 +4856,15 @@ w32_read_socket (struct terminal *terminal, if (f) { - /* Generate SELECT_WINDOW_EVENTs when needed. */ - if (!NILP (Vmouse_autoselect_window)) + /* Maybe generate SELECT_WINDOW_EVENTs for + `mouse-autoselect-window'. */ + if (!NILP (Vmouse_autoselect_window) + && (f == XFRAME (selected_frame) + /* Switch to f from another frame iff + focus_follows_mouse is set and f accepts + focus. */ + || (!NILP (focus_follows_mouse) + && !FRAME_NO_ACCEPT_FOCUS (f)))) { static Lisp_Object last_mouse_window; Lisp_Object window = window_from_coordinates @@ -4848,20 +4876,16 @@ w32_read_socket (struct terminal *terminal, only when it is active. */ if (WINDOWP (window) && !EQ (window, last_mouse_window) - && !EQ (window, selected_window) - /* For click-to-focus window managers - create event iff we don't leave the - selected frame. */ - && (focus_follows_mouse - || (EQ (XWINDOW (window)->frame, - XWINDOW (selected_window)->frame)))) + && !EQ (window, selected_window)) { inev.kind = SELECT_WINDOW_EVENT; inev.frame_or_window = window; } + /* Remember the last window where we saw the mouse. */ last_mouse_window = window; } + if (!note_mouse_movement (f, &msg.msg)) help_echo_string = previous_help_echo_string; } @@ -4927,7 +4951,10 @@ w32_read_socket (struct terminal *terminal, if (tool_bar_p || (dpyinfo->w32_focus_frame - && f != dpyinfo->w32_focus_frame)) + && f != dpyinfo->w32_focus_frame + /* This does not help when the click happens in + a grand-parent frame. */ + && !frame_ancestor_p (f, dpyinfo->w32_focus_frame))) inev.kind = NO_EVENT; } @@ -4964,21 +4991,40 @@ w32_read_socket (struct terminal *terminal, if (f) { - if (!dpyinfo->w32_focus_frame || f == dpyinfo->w32_focus_frame) + /* Emit an Emacs wheel-up/down event. */ { - /* Emit an Emacs wheel-up/down event. */ construct_mouse_wheel (&inev, &msg, f); + + /* Ignore any mouse motion that happened before this + event; any subsequent mouse-movement Emacs events + should reflect only motion after the ButtonPress. */ + f->mouse_moved = false; + f->last_tool_bar_item = -1; + dpyinfo->last_mouse_frame = f; + } + else if (FRAME_NO_ACCEPT_FOCUS (f) + && !x_mouse_grabbed (dpyinfo)) + { + Lisp_Object frame1 = get_frame_param (f, Qmouse_wheel_frame); + struct frame *f1 = FRAMEP (frame1) ? XFRAME (frame1) : NULL; + + if (f1 && FRAME_LIVE_P (f1) && FRAME_W32_P (f1)) + { + construct_mouse_wheel (&inev, &msg, f1); + f1->mouse_moved = false; + f1->last_tool_bar_item = -1; + dpyinfo->last_mouse_frame = f1; + } + else + dpyinfo->last_mouse_frame = f; } - /* Ignore any mouse motion that happened before this - event; any subsequent mouse-movement Emacs events - should reflect only motion after the - ButtonPress. */ - f->mouse_moved = false; - f->last_tool_bar_item = -1; + else + dpyinfo->last_mouse_frame = f; } - dpyinfo->last_mouse_frame = f; + else + dpyinfo->last_mouse_frame = f; } break; @@ -5031,6 +5077,7 @@ w32_read_socket (struct terminal *terminal, w32fullscreen_hook (f); } } + check_visibility = 1; break; @@ -5969,6 +6016,8 @@ x_calc_absolute_position (struct frame *f) are computed correctly (Bug#21173). */ int display_left = 0; int display_top = 0; + struct frame *p = FRAME_PARENT_FRAME (f); + if (flags & (XNegative | YNegative)) { Lisp_Object list; @@ -5997,18 +6046,34 @@ x_calc_absolute_position (struct frame *f) /* Treat negative positions as relative to the rightmost bottommost position that fits on the screen. */ if (flags & XNegative) - f->left_pos = (x_display_pixel_width (FRAME_DISPLAY_INFO (f)) - + display_left - - FRAME_PIXEL_WIDTH (f) - + f->left_pos - - (left_right_borders_width - 1)); + { + if (p) + f->left_pos = (FRAME_PIXEL_WIDTH (p) + - FRAME_PIXEL_WIDTH (f) + + f->left_pos + - (left_right_borders_width - 1)); + else + f->left_pos = (x_display_pixel_width (FRAME_DISPLAY_INFO (f)) + + display_left + - FRAME_PIXEL_WIDTH (f) + + f->left_pos + - (left_right_borders_width - 1)); + } if (flags & YNegative) - f->top_pos = (x_display_pixel_height (FRAME_DISPLAY_INFO (f)) - + display_top - - FRAME_PIXEL_HEIGHT (f) - + f->top_pos - - (top_bottom_borders_height - 1)); + { + if (p) + f->top_pos = (FRAME_PIXEL_HEIGHT (p) + - FRAME_PIXEL_HEIGHT (f) + + f->top_pos + - (top_bottom_borders_height - 1)); + else + f->top_pos = (x_display_pixel_height (FRAME_DISPLAY_INFO (f)) + + display_top + - FRAME_PIXEL_HEIGHT (f) + + f->top_pos + - (top_bottom_borders_height - 1)); + } /* The left_pos and top_pos are now relative to the top and left screen edges, so the flags should correspond. */ @@ -6046,11 +6111,16 @@ x_set_offset (struct frame *f, register int xoff, register int yoff, modified_left = f->left_pos; modified_top = f->top_pos; - my_set_window_pos (FRAME_W32_WINDOW (f), - NULL, - modified_left, modified_top, - 0, 0, - SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); + if (!FRAME_PARENT_FRAME (f)) + my_set_window_pos (FRAME_W32_WINDOW (f), NULL, + modified_left, modified_top, + 0, 0, + SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); + else + my_set_window_pos (FRAME_W32_WINDOW (f), HWND_TOP, + modified_left, modified_top, + 0, 0, + SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); unblock_input (); } @@ -6254,11 +6324,18 @@ x_set_window_size (struct frame *f, bool change_gravity, Fcons (make_number (rect.right - rect.left), make_number (rect.bottom - rect.top)))); - my_set_window_pos (FRAME_W32_WINDOW (f), NULL, - 0, 0, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); + if (!FRAME_PARENT_FRAME (f)) + my_set_window_pos (FRAME_W32_WINDOW (f), NULL, + 0, 0, + rect.right - rect.left, + rect.bottom - rect.top, + SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); + else + my_set_window_pos (FRAME_W32_WINDOW (f), HWND_TOP, + 0, 0, + rect.right - rect.left, + rect.bottom - rect.top, + SWP_NOMOVE | SWP_NOACTIVATE); change_frame_size (f, ((pixelwidth == 0) @@ -6447,18 +6524,21 @@ x_make_frame_visible (struct frame *f) if (! FRAME_ICONIFIED_P (f) && ! f->output_data.w32->asked_for_visible) { - RECT workarea_rect; - RECT window_rect; - - /* Adjust vertical window position in order to avoid being - covered by a taskbar placed at the bottom of the desktop. */ - SystemParametersInfo (SPI_GETWORKAREA, 0, &workarea_rect, 0); - GetWindowRect (FRAME_W32_WINDOW (f), &window_rect); - if (window_rect.bottom > workarea_rect.bottom - && window_rect.top > workarea_rect.top) - f->top_pos = max (window_rect.top - - window_rect.bottom + workarea_rect.bottom, - workarea_rect.top); + if (!FRAME_PARENT_FRAME (f)) + { + RECT workarea_rect; + RECT window_rect; + + /* Adjust vertical window position in order to avoid being + covered by a taskbar placed at the bottom of the desktop. */ + SystemParametersInfo (SPI_GETWORKAREA, 0, &workarea_rect, 0); + GetWindowRect (FRAME_W32_WINDOW (f), &window_rect); + if (window_rect.bottom > workarea_rect.bottom + && window_rect.top > workarea_rect.top) + f->top_pos = max (window_rect.top + - window_rect.bottom + workarea_rect.bottom, + workarea_rect.top); + } x_set_offset (f, f->left_pos, f->top_pos, 0); } @@ -6473,7 +6553,11 @@ x_make_frame_visible (struct frame *f) set for minimized windows that are still visible, so use that to determine the appropriate flag to pass ShowWindow. */ my_show_window (f, FRAME_W32_WINDOW (f), - FRAME_ICONIFIED_P (f) ? SW_RESTORE : SW_SHOWNORMAL); + FRAME_ICONIFIED_P (f) + ? SW_RESTORE + : FRAME_NO_FOCUS_ON_MAP (f) + ? SW_SHOWNOACTIVATE + : SW_SHOWNORMAL); } /* Synchronize to ensure Emacs knows the frame is visible diff --git a/src/w32term.h b/src/w32term.h index 990d3794b2..6896ef4f2c 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -246,6 +246,7 @@ extern void x_set_internal_border_width (struct frame *f, Lisp_Object value, Lisp_Object oldval); extern void initialize_frame_menubar (struct frame *); +extern void w32_dialog_in_progress (Lisp_Object in_progress); /* w32inevt.c */ extern int w32_kbd_patch_key (KEY_EVENT_RECORD *event, int cpId); diff --git a/src/xdisp.c b/src/xdisp.c index af086d17eb..42a59d63b1 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -11767,6 +11767,7 @@ x_consider_frame_title (Lisp_Object frame) && FRAME_KBOARD (tf) == FRAME_KBOARD (f) && !FRAME_MINIBUF_ONLY_P (tf) && !EQ (other_frame, tip_frame) + && !FRAME_PARENT_FRAME (tf) && (FRAME_VISIBLE_P (tf) || FRAME_ICONIFIED_P (tf))) break; } @@ -11883,6 +11884,7 @@ prepare_menu_bars (void) continue; if (!EQ (frame, tooltip_frame) + && !FRAME_PARENT_FRAME (f) && (FRAME_ICONIFIED_P (f) || FRAME_VISIBLE_P (f) == 1 /* Exclude TTY frames that are obscured because they @@ -11929,6 +11931,10 @@ prepare_menu_bars (void) continue; run_window_size_change_functions (frame); + + if (FRAME_PARENT_FRAME (f)) + continue; + menu_bar_hooks_run = update_menu_bar (f, false, menu_bar_hooks_run); #ifdef HAVE_WINDOW_SYSTEM update_tool_bar (f, false); diff --git a/src/xfns.c b/src/xfns.c index d3e0839d8a..3d667446e6 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -90,6 +90,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #endif #ifdef USE_LUCID @@ -117,6 +118,35 @@ static ptrdiff_t image_cache_refcount; static int dpyinfo_refcount; #endif +#ifndef USE_MOTIF +#ifndef USE_GTK +/** #define MWM_HINTS_FUNCTIONS (1L << 0) **/ +#define MWM_HINTS_DECORATIONS (1L << 1) +/** #define MWM_HINTS_INPUT_MODE (1L << 2) **/ +/** #define MWM_HINTS_STATUS (1L << 3) **/ + +#define MWM_DECOR_ALL (1L << 0) +/** #define MWM_DECOR_BORDER (1L << 1) **/ +/** #define MWM_DECOR_RESIZEH (1L << 2) **/ +/** #define MWM_DECOR_TITLE (1L << 3) **/ +/** #define MWM_DECOR_MENU (1L << 4) **/ +/** #define MWM_DECOR_MINIMIZE (1L << 5) **/ +/** #define MWM_DECOR_MAXIMIZE (1L << 6) **/ + +/** #define _XA_MOTIF_WM_HINTS "_MOTIF_WM_HINTS" **/ + +typedef struct { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; +} PropMotifWmHints; + +#define PROP_MOTIF_WM_HINTS_ELEMENTS 5 +#endif /* NOT USE_GTK */ +#endif /* NOT USE_MOTIF */ + static struct x_display_info *x_display_info_for_name (Lisp_Object); static void set_up_x_back_buffer (struct frame *f); @@ -185,7 +215,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 = f->output_data.x->parent_desc; + Window win = (FRAME_PARENT_FRAME (f) + ? FRAME_X_WINDOW (FRAME_PARENT_FRAME (f)) + : f->output_data.x->parent_desc); struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); long max_len = 400; Atom target_type = XA_CARDINAL; @@ -323,7 +355,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) + if ((dpyinfo->root_window == f->output_data.x->parent_desc) + && !FRAME_PARENT_FRAME (f)) /* 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, @@ -437,7 +470,8 @@ x_real_pos_and_offsets (struct frame *f, #endif } - if (dpyinfo->root_window == f->output_data.x->parent_desc) + if ((dpyinfo->root_window == f->output_data.x->parent_desc) + && !FRAME_PARENT_FRAME (f)) { /* Try _NET_FRAME_EXTENTS if our parent is the root window. */ #ifdef USE_XCB @@ -735,6 +769,204 @@ x_set_inhibit_double_buffering (struct frame *f, unblock_input (); } +/** + * x_set_undecorated: + * + * Set frame F's `undecorated' parameter. If non-nil, F's window-system + * window is drawn without decorations, title, minimize/maximize boxes + * and external borders. This usually means that the window cannot be + * dragged, resized, iconified, maximized or deleted with the mouse. If + * nil, draw the frame with all the elements listed above unless these + * have been suspended via window manager settings. + * + * Some window managers may not honor this parameter. + */ +static void +x_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) +{ + if (!EQ (new_value, old_value)) + { + FRAME_UNDECORATED (f) = NILP (new_value) ? false : true; +#ifdef USE_GTK + xg_set_undecorated (f, new_value); +#else + Display *dpy = FRAME_X_DISPLAY (f); + PropMotifWmHints hints; + Atom prop = XInternAtom (dpy, "_MOTIF_WM_HINTS", False); + + memset (&hints, 0, sizeof(hints)); + hints.flags = MWM_HINTS_DECORATIONS; + hints.decorations = NILP (new_value) ? MWM_DECOR_ALL : 0; + + block_input (); + /* For some reason the third and fourth argument in the following + call must be identic: In the corresponding XGetWindowProperty + call in getMotifHints, xfwm has the third and seventh arg both + display_info->atoms[MOTIF_WM_HINTS]. Obviously, YMMV. */ + XChangeProperty (dpy, FRAME_OUTER_WINDOW (f), prop, prop, 32, + PropModeReplace, (unsigned char *) &hints, + PROP_MOTIF_WM_HINTS_ELEMENTS); + unblock_input (); + +#endif /* USE_GTK */ + } +} + +/** + * x_set_parent_frame: + * + * Set frame F's `parent-frame' parameter. If non-nil, make F a child + * frame of the frame specified by that parameter. Technically, this + * makes F's window-system window a child window of the parent frame's + * window-system window. If nil, make F's window-system window a + * top-level window--a child of its display's root window. + * + * A child frame is clipped at the native edges of its parent frame. + * Its `left' and `top' parameters specify positions relative to the + * top-left corner of its parent frame's native rectangle. Usually, + * moving a parent frame moves all its child frames too, keeping their + * position relative to the parent unaltered. When a parent frame is + * iconified or made invisible, its child frames are made invisible. + * When a parent frame is deleted, its child frames are deleted too. + * + * A visible child frame always appears on top of its parent frame thus + * obscuring parts of it. When a frame has more than one child frame, + * their stacking order is specified just as that of non-child frames + * relative to their display. + * + * Whether a child frame has a menu or tool bar may be window-system or + * window manager dependent. It's advisable to disable both via the + * frame parameter settings. + * + * Some window managers may not honor this parameter. + */ +static void +x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) +{ + struct frame *p = NULL; + + if (!NILP (new_value) + && (!FRAMEP (new_value) + || !FRAME_LIVE_P (p = XFRAME (new_value)) + || !FRAME_X_P (p))) + { + store_frame_param (f, Qparent_frame, old_value); + error ("Invalid specification of `parent-frame'"); + } + + if (p != FRAME_PARENT_FRAME (f)) + { + block_input (); + XReparentWindow + (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), + p ? FRAME_X_WINDOW (p) : DefaultRootWindow (FRAME_X_DISPLAY (f)), + f->left_pos, f->top_pos); + unblock_input (); + + fset_parent_frame (f, new_value); + } +} + +/** + * x_set_no_focus_on_map: + * + * Set frame F's `no-focus-on-map' parameter which, if non-nil, means + * that F's window-system window does not want to receive input focus + * when it is mapped. (A frame's window is mapped when the frame is + * displayed for the first time and when the frame changes its state + * from `iconified' or `invisible' to `visible'.) + * + * Some window managers may not honor this parameter. + */ +static void +x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) +{ + if (!EQ (new_value, old_value)) + { +#ifdef USE_GTK + 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); + Time timestamp = NILP (new_value) ? CurrentTime : 0; + + XChangeProperty (dpy, FRAME_OUTER_WINDOW (f), prop, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) ×tamp, 1); +#endif /* USE_GTK */ + FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value); + } +} + +/** + * x_set_no_accept_focus: + * + * Set frame F's `no-accept-focus' parameter which, if non-nil, hints + * that F's window-system window does not want to receive input focus + * via mouse clicks or by moving the mouse into it. + * + * If non-nil, this may have the unwanted side-effect that a user cannot + * scroll a non-selected frame with the mouse. + * + * Some window managers may not honor this parameter. + */ +static void +x_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) +{ + if (!EQ (new_value, old_value)) + { +#ifdef USE_GTK + xg_set_no_accept_focus (f, new_value); +#else /* not USE_GTK */ +#ifdef USE_X_TOOLKIT + Arg al[1]; + + XtSetArg (al[0], XtNinput, NILP (new_value) ? True : False); + XtSetValues (f->output_data.x->widget, al, 1); +#else /* not USE_X_TOOLKIT */ + Window window = FRAME_X_WINDOW (f); + + f->output_data.x->wm_hints.input = NILP (new_value) ? True : False; + XSetWMHints (FRAME_X_DISPLAY (f), window, &f->output_data.x->wm_hints); +#endif /* USE_X_TOOLKIT */ +#endif /* USE_GTK */ + FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value); + } +} + +/** + * x_set_override_redirect: + * + * Set frame F's `override_redirect' parameter which, if non-nil, hints + * that the window manager doesn't want to deal with F. Usually, such + * frames have no decorations and always appear on top of all frames. + * + * Some window managers may not honor this parameter. + */ +static void +x_set_override_redirect (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) +{ + if (!EQ (new_value, old_value)) + { + /* Here (xfwm) override_redirect can be changed for invisible + frames only. */ + x_make_frame_invisible (f); + +#ifdef USE_GTK + xg_set_override_redirect (f, new_value); +#else /* not USE_GTK */ + XSetWindowAttributes attributes; + + attributes.override_redirect = NILP (new_value) ? False : True; + XChangeWindowAttributes (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), + CWOverrideRedirect, &attributes); +#endif + x_make_frame_visible (f); + FRAME_OVERRIDE_REDIRECT (f) = !NILP (new_value); + } +} + + #ifdef USE_GTK /* Set icon from FILE for frame F. By using GTK functions the icon @@ -1272,7 +1504,7 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) most of the commands try to apply themselves to the minibuffer frame itself, and get an error because you can't switch buffers in or split the minibuffer window. */ - if (FRAME_MINIBUF_ONLY_P (f)) + if (FRAME_MINIBUF_ONLY_P (f) || FRAME_PARENT_FRAME (f)) return; if (TYPE_RANGED_INTEGERP (int, value)) @@ -2693,7 +2925,7 @@ x_window (struct frame *f, long window_prompting) and specify it. Note that we do not specify here whether the position is a user-specified or program-specified one. - We pass that information later, in x_wm_set_size_hints. */ + We pass that information later, in x_wm_set_size_hint. */ { int left = f->left_pos; bool xneg = (window_prompting & XNegative) != 0; @@ -2783,7 +3015,8 @@ x_window (struct frame *f, long window_prompting) } #endif /* HAVE_X_I18N */ - attribute_mask = CWEventMask; + attributes.override_redirect = FRAME_OVERRIDE_REDIRECT (f); + attribute_mask = CWEventMask | CWOverrideRedirect; XChangeWindowAttributes (XtDisplay (shell_widget), XtWindow (shell_widget), attribute_mask, &attributes); @@ -2803,6 +3036,25 @@ x_window (struct frame *f, long window_prompting) x_set_name (f, name, explicit); } + if (FRAME_UNDECORATED (f)) + { + Display *dpy = FRAME_X_DISPLAY (f); + PropMotifWmHints hints; + Atom prop = XInternAtom (dpy, "_MOTIF_WM_HINTS", False); + + memset (&hints, 0, sizeof(hints)); + hints.flags = MWM_HINTS_DECORATIONS; + hints.decorations = 0; + + /* For some reason the third and fourth argument in the following + call must be identic: In the corresponding XGetWindowProperty + call in getMotifHints, xfwm has the third and seventh arg both + display_info->atoms[MOTIF_WM_HINTS]. Obviously, YMMV. */ + XChangeProperty (dpy, FRAME_OUTER_WINDOW (f), prop, prop, 32, + PropModeReplace, (unsigned char *) &hints, + PROP_MOTIF_WM_HINTS_ELEMENTS); + } + XDefineCursor (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), f->output_data.x->current_cursor = f->output_data.x->text_cursor); @@ -2870,8 +3122,9 @@ x_window (struct frame *f) attributes.save_under = True; attributes.event_mask = STANDARD_EVENT_SET; attributes.colormap = FRAME_X_COLORMAP (f); + attributes.override_redirect = FRAME_OVERRIDE_REDIRECT (f); attribute_mask = (CWBackPixel | CWBorderPixel | CWBitGravity | CWEventMask - | CWColormap); + | CWOverrideRedirect | CWColormap); block_input (); FRAME_X_WINDOW (f) @@ -2943,6 +3196,26 @@ x_window (struct frame *f) x_set_name (f, name, explicit); } + if (FRAME_UNDECORATED (f)) + { + Display *dpy = FRAME_X_DISPLAY (f); + PropMotifWmHints hints; + Atom prop = XInternAtom (dpy, "_MOTIF_WM_HINTS", False); + + memset (&hints, 0, sizeof(hints)); + hints.flags = MWM_HINTS_DECORATIONS; + hints.decorations = 0; + + /* For some reason the third and fourth argument in the following + call must be identic: In the corresponding XGetWindowProperty + call in getMotifHints, xfwm has the third and seventh arg both + display_info->atoms[MOTIF_WM_HINTS]. Obviously, YMMV. */ + XChangeProperty (dpy, FRAME_OUTER_WINDOW (f), prop, prop, 32, + PropModeReplace, (unsigned char *) &hints, + PROP_MOTIF_WM_HINTS_ELEMENTS); + } + + XDefineCursor (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), f->output_data.x->current_cursor = f->output_data.x->text_cursor); @@ -3285,11 +3558,12 @@ This function is an internal primitive--use `make-frame' instead. */) Lisp_Object frame, tem; Lisp_Object name; bool minibuffer_only = false; + bool undecorated = false, override_redirect = false; long window_prompting = 0; ptrdiff_t count = SPECPDL_INDEX (); Lisp_Object display; struct x_display_info *dpyinfo = NULL; - Lisp_Object parent; + Lisp_Object parent, parent_frame; struct kboard *kb; int x_width = 0, x_height = 0; @@ -3341,6 +3615,36 @@ 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); + /* Accept parent-frame iff parent-id was not specified. */ + if (!NILP (parent) + || EQ (parent_frame, Qunbound) + || NILP (parent_frame) + || !FRAMEP (parent_frame) + || !FRAME_LIVE_P (XFRAME (parent_frame)) + || !FRAME_X_P (XFRAME (parent_frame))) + parent_frame = Qnil; + + 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))) + 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))) + override_redirect = true; + + FRAME_OVERRIDE_REDIRECT (f) = override_redirect; + store_frame_param (f, Qoverride_redirect, override_redirect ? Qt : Qnil); + XSETFRAME (frame, f); f->terminal = dpyinfo->terminal; @@ -3528,15 +3832,24 @@ This function is an internal primitive--use `make-frame' instead. */) init_iterator with a null face cache, which should not happen. */ init_frame_faces (f); - /* The following call of change_frame_size is needed since otherwise + /* 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. + 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 ... */ + 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)) + store_frame_param (f, Qmin_width, tem); + tem = x_get_arg (dpyinfo, parms, Qmin_height, NULL, NULL, RES_TYPE_NUMBER); + if (NUMBERP (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); @@ -3611,6 +3924,21 @@ This function is an internal primitive--use `make-frame' instead. */) x_default_parameter (f, parms, Qalpha, Qnil, "alpha", "Alpha", RES_TYPE_NUMBER); + if (!NILP (parent_frame)) + { + struct frame *p = XFRAME (parent_frame); + + block_input (); + XReparentWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), + FRAME_X_WINDOW (p), f->left_pos, f->top_pos); + 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); + #if defined (USE_X_TOOLKIT) || defined (USE_GTK) /* Create the menu bar. */ if (!minibuffer_only && FRAME_EXTERNAL_MENU_BAR (f)) @@ -3656,23 +3984,23 @@ This function is an internal primitive--use `make-frame' instead. */) /* 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) + if (!f->output_data.x->explicit_parent) { - Lisp_Object visibility; - - visibility = x_get_arg (dpyinfo, parms, Qvisibility, 0, 0, - RES_TYPE_SYMBOL); - if (EQ (visibility, Qunbound)) - visibility = Qt; + Lisp_Object visibility + = x_get_arg (dpyinfo, parms, Qvisibility, 0, 0, RES_TYPE_SYMBOL); if (EQ (visibility, Qicon)) x_iconify_frame (f); - else if (! NILP (visibility)) - x_make_frame_visible (f); else { - /* Must have been Qnil. */ + if (EQ (visibility, Qunbound)) + visibility = Qt; + + if (!NILP (visibility)) + x_make_frame_visible (f); } + + store_frame_param (f, Qvisibility, visibility); } block_input (); @@ -3685,14 +4013,21 @@ This function is an internal primitive--use `make-frame' instead. */) if (dpyinfo->client_leader_window != 0) { XChangeProperty (FRAME_X_DISPLAY (f), - FRAME_OUTER_WINDOW (f), - dpyinfo->Xatom_wm_client_leader, - XA_WINDOW, 32, PropModeReplace, - (unsigned char *) &dpyinfo->client_leader_window, 1); + FRAME_OUTER_WINDOW (f), + dpyinfo->Xatom_wm_client_leader, + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &dpyinfo->client_leader_window, 1); } unblock_input (); + /* Works iff frame has been already mapped. */ + x_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); + /* Initialize `default-minibuffer-frame' in case this is the first frame on this terminal. */ if (FRAME_HAS_MINIBUF_P (f) @@ -3710,7 +4045,7 @@ This function is an internal primitive--use `make-frame' instead. */) and similar functions. */ Vwindow_list = Qnil; - return unbind_to (count, frame); + return unbind_to (count, frame); } @@ -4644,9 +4979,9 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) struct frame *f = decode_live_frame (frame); /** XWindowAttributes atts; **/ Window rootw; - unsigned int ign, native_width, native_height; - int xy_ign, xptr, yptr; - int left_off, right_off, top_off, bottom_off; + unsigned int ign, native_width, native_height, x_border_width = 0; + int x_native = 0, y_native = 0, xptr = 0, yptr = 0; + int left_off = 0, right_off = 0, top_off = 0, bottom_off = 0; int outer_left, outer_top, outer_right, outer_bottom; int native_left, native_top, native_right, native_bottom; int inner_left, inner_top, inner_right, inner_bottom; @@ -4660,25 +4995,51 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) block_input (); XGetGeometry (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), - &rootw, &xy_ign, &xy_ign, &native_width, &native_height, - &ign, &ign); + &rootw, &x_native, &y_native, &native_width, &native_height, + &x_border_width, &ign); /** XGetWindowAttributes (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), &atts); **/ - x_real_pos_and_offsets (f, &left_off, &right_off, &top_off, &bottom_off, - NULL, NULL, &xptr, &yptr, NULL); + if (!FRAME_PARENT_FRAME (f)) + x_real_pos_and_offsets (f, &left_off, &right_off, &top_off, &bottom_off, + NULL, NULL, &xptr, &yptr, NULL); unblock_input (); /** native_width = atts.width; **/ /** native_height = atts.height; **/ - outer_left = xptr; - outer_top = yptr; - outer_right = outer_left + left_off + native_width + right_off; - outer_bottom = outer_top + top_off + native_height + bottom_off; + if (FRAME_PARENT_FRAME (f)) + { + Lisp_Object parent, edges; + + XSETFRAME (parent, FRAME_PARENT_FRAME (f)); + 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)); + } + + outer_left = x_native; + outer_top = y_native; + outer_right = outer_left + native_width + 2 * x_border_width; + outer_bottom = outer_top + native_height + 2 * x_border_width; + + native_left = x_native + x_border_width; + native_top = y_native + x_border_width; + native_right = native_left + native_width; + native_bottom = native_top + native_height; + } + else + { + outer_left = xptr; + outer_top = yptr; + outer_right = outer_left + left_off + native_width + right_off; + outer_bottom = outer_top + top_off + native_height + bottom_off; - native_left = outer_left + left_off; - native_top = outer_top + top_off; - native_right = native_left + native_width; - native_bottom = native_top + native_height; + native_left = outer_left + left_off; + native_top = outer_top + top_off; + native_right = native_left + native_width; + native_bottom = native_top + native_height; + } internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f); inner_left = native_left + internal_border_width; @@ -4749,7 +5110,7 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) make_number (inner_right), make_number (inner_bottom)); else return - listn (CONSTYPE_HEAP, 10, + listn (CONSTYPE_HEAP, 11, Fcons (Qouter_position, Fcons (make_number (outer_left), make_number (outer_top))), @@ -4760,6 +5121,7 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) Fcons (Qexternal_border_size, Fcons (make_number (right_off), make_number (bottom_off))), + Fcons (Qouter_border_width, make_number (x_border_width)), /* Approximate. */ Fcons (Qtitle_bar_size, Fcons (make_number (0), @@ -4788,7 +5150,8 @@ and width values are in pixels. `outer-size' is a cons of the outer width and height of FRAME. The outer size includes the title bar and the external borders as well as - any menu and/or tool bar of frame. + any menu and/or tool bar of frame. For a child frame the value + includes FRAME's X borders, if any. `external-border-size' is a cons of the horizontal and vertical width of FRAME's external borders as supplied by the window manager. @@ -4815,7 +5178,11 @@ and width values are in pixels. FRAME. `internal-border-width' is the width of the internal border of - FRAME. */) + 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. */) (Lisp_Object frame) { return frame_geometry (frame, Qnil); @@ -4845,6 +5212,139 @@ menu bar or tool bar of FRAME. */) : Qnative_edges)); } +/** + * x_frame_list_z_order: + * + * Recursively add list of all frames on the display specified via + * DPYINFO and whose window-system window's parent is specified by + * WINDOW to FRAMES and return FRAMES. + */ +static Lisp_Object +x_frame_list_z_order (Display* dpy, Window window) +{ + Window root, parent, *children; + unsigned int nchildren; + int i; + Lisp_Object frames = Qnil; + + 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); + } + + if (children) XFree ((char *)children); + } + else + unblock_input (); + + return frames; +} + + +DEFUN ("x-frame-list-z-order", Fx_frame_list_z_order, + Sx_frame_list_z_order, 0, 1, 0, + doc: /* Return list of Emacs' frames, in Z (stacking) order. +The optional argument TERMINAL specifies which display to ask about. +TERMINAL should be either a frame or a display name (a string). If +omitted or nil, that stands for the selected frame's display. Return +nil if TERMINAL contains no Emacs frame. + +As a special case, if TERMINAL is non-nil and specifies a live frame, +return the child frames of that frame in Z (stacking) order. + +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))) + window = FRAME_X_WINDOW (XFRAME (terminal)); + else + window = dpyinfo->root_window; + + return x_frame_list_z_order (dpy, window); +} + +/** + * x_frame_restack: + * + * Restack frame F1 below frame F2, above if ABOVE_FLAG is non-nil. In + * practice this is a two-step action: The first step removes F1's + * window-system window from the display. The second step reinserts + * F1's window below (above if ABOVE_FLAG is true) that of F2. + */ +static void +x_frame_restack (struct frame *f1, struct frame *f2, bool above_flag) +{ +#ifdef USE_GTK + block_input (); + xg_frame_restack (f1, f2, above_flag); + unblock_input (); +#else + Display *dpy = FRAME_X_DISPLAY (f1); + Window window1 = FRAME_OUTER_WINDOW (f1); + XWindowChanges wc; + unsigned long mask = (CWSibling | CWStackMode); + + wc.sibling = FRAME_OUTER_WINDOW (f2); + wc.stack_mode = above_flag ? Above : Below; + block_input (); + /* Configure the window manager window (a normal XConfigureWindow + won't cut it). This should also work for child frames. */ + XReconfigureWMWindow (dpy, window1, FRAME_X_SCREEN_NUMBER (f1), mask, &wc); + unblock_input (); +#endif /* USE_GTK */ +} + + +DEFUN ("x-frame-restack", Fx_frame_restack, Sx_frame_restack, 2, 3, 0, + doc: /* Restack FRAME1 below FRAME2. +This means that if both frames are visible and the display areas of +these frames overlap, FRAME2 (partially) obscures FRAME1. If optional +third argument ABOVE is non-nil, restack FRAME1 above FRAME2. This +means that if both frames are visible and the display areas of these +frames overlap, FRAME1 (partially) obscures FRAME2. + +This may be thought of as an atomic action performed in two steps: The +first step removes FRAME1's window-step window from the display. The +second step reinserts FRAME1's window below (above if ABOVE is true) +that of FRAME2. Hence the position of FRAME2 in its display's Z +\(stacking) order relative to all other frames excluding FRAME1 remains +unaltered. + +Some window managers may refuse to restack windows. */) + (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object above) +{ + 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; + } +} + + DEFUN ("x-mouse-absolute-pixel-position", Fx_mouse_absolute_pixel_position, Sx_mouse_absolute_pixel_position, 0, 0, 0, doc: /* Return absolute position of mouse cursor in pixels. @@ -6585,6 +7085,8 @@ value of DIR as in previous invocations; this is standard Windows behavior. */) if (popup_activated ()) error ("Trying to use a menu from within a menu-entry"); + else + x_menu_set_in_use (true); CHECK_STRING (prompt); CHECK_STRING (dir); @@ -6641,6 +7143,8 @@ nil, it defaults to the selected frame. */) if (popup_activated ()) error ("Trying to use a menu from within a menu-entry"); + else + x_menu_set_in_use (true); /* Prevent redisplay. */ specbind (Qinhibit_redisplay, Qt); @@ -6979,6 +7483,13 @@ frame_parm_handler x_frame_parm_handlers[] = x_set_sticky, x_set_tool_bar_position, x_set_inhibit_double_buffering, + x_set_undecorated, + x_set_parent_frame, + x_set_skip_taskbar, + x_set_no_focus_on_map, + x_set_no_accept_focus, + x_set_z_group, + x_set_override_redirect, }; void @@ -7183,6 +7694,8 @@ When using Gtk+ tooltips, the tooltip face is not used. */); defsubr (&Sx_display_monitor_attributes_list); defsubr (&Sx_frame_geometry); defsubr (&Sx_frame_edges); + defsubr (&Sx_frame_list_z_order); + defsubr (&Sx_frame_restack); defsubr (&Sx_mouse_absolute_pixel_position); defsubr (&Sx_set_mouse_absolute_pixel_position); defsubr (&Sx_wm_set_size_hint); diff --git a/src/xmenu.c b/src/xmenu.c index 249cd6903f..2805249164 100644 --- a/src/xmenu.c +++ b/src/xmenu.c @@ -140,14 +140,26 @@ menubar_id_to_frame (LWLIB_ID id) void x_menu_set_in_use (bool in_use) { + Lisp_Object frames, frame; + menu_items_inuse = in_use ? Qt : Qnil; popup_activated_flag = in_use; #ifdef USE_X_TOOLKIT if (popup_activated_flag) x_activate_timeout_atimer (); #endif -} + /* Don't let frames in `above' z-group obscure popups. */ + FOR_EACH_FRAME (frames, frame) + { + struct frame *f = XFRAME (frame); + + if (in_use && FRAME_Z_GROUP_ABOVE (f)) + x_set_z_group (f, Qabove_suspended, Qabove); + else if (!in_use && FRAME_Z_GROUP_ABOVE_SUSPENDED (f)) + x_set_z_group (f, Qabove, Qabove_suspended); + } +} #endif /* Wait for an X event to arrive or for a timer to expire. */ diff --git a/src/xterm.c b/src/xterm.c index 1d14407aa4..4444a5c187 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -945,11 +945,14 @@ x_set_frame_alpha (struct frame *f) Do this unconditionally as this function is called on reparent when alpha has not changed on the frame. */ - parent = x_find_topmost_parent (f); - if (parent != None) - XChangeProperty (dpy, parent, dpyinfo->Xatom_net_wm_window_opacity, - XA_CARDINAL, 32, PropModeReplace, - (unsigned char *) &opac, 1); + if (!FRAME_PARENT_FRAME (f)) + { + parent = x_find_topmost_parent (f); + if (parent != None) + XChangeProperty (dpy, parent, dpyinfo->Xatom_net_wm_window_opacity, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &opac, 1); + } /* return unless necessary */ { @@ -4964,6 +4967,9 @@ XTmouse_position (struct frame **fp, int insist, Lisp_Object *bar_window, containing the pointer. */ { Window win, child; +#ifdef USE_GTK + Window first_win = 0; +#endif int win_x, win_y; int parent_x = 0, parent_y = 0; @@ -5010,20 +5016,37 @@ XTmouse_position (struct frame **fp, int insist, Lisp_Object *bar_window, &child); if (child == None || child == win) - break; + { +#ifdef USE_GTK + /* On GTK we have not inspected WIN yet. If it has + a frame and that frame has a parent, use it. */ + struct frame *f = x_window_to_frame (dpyinfo, win); + + if (f && FRAME_PARENT_FRAME (f)) + first_win = win; +#endif + break; + } #ifdef USE_GTK /* We don't wan't to know the innermost window. We want the edit window. For non-Gtk+ the innermost window is the edit window. For Gtk+ it might not be. It might be the tool bar for example. */ if (x_window_to_frame (dpyinfo, win)) - break; + /* But don't hurry. We might find a child frame + beneath. */ + first_win = win; #endif win = child; parent_x = win_x; parent_y = win_y; } +#ifdef USE_GTK + if (first_win) + win = first_win; +#endif + /* Now we know that: win is the innermost window containing the pointer (XTC says it has no child containing the pointer), @@ -6496,10 +6519,14 @@ x_scroll_bar_create (struct window *w, int top, int left, Widget scroll_bar = SCROLL_BAR_X_WIDGET (FRAME_X_DISPLAY (f), bar); XtConfigureWidget (scroll_bar, left, top, width, max (height, 1), 0); XtMapWidget (scroll_bar); + /* Don't obscure any child frames. */ + XLowerWindow (FRAME_X_DISPLAY (f), bar->x_window); #endif /* not USE_GTK */ } #else /* not USE_TOOLKIT_SCROLL_BARS */ - XMapRaised (FRAME_X_DISPLAY (f), bar->x_window); + XMapWindow (FRAME_X_DISPLAY (f), bar->x_window); + /* Don't obscure any child frames. */ + XLowerWindow (FRAME_X_DISPLAY (f), bar->x_window); #endif /* not USE_TOOLKIT_SCROLL_BARS */ unblock_input (); @@ -7067,10 +7094,10 @@ x_scroll_bar_expose (struct scroll_bar *bar, const XEvent *event) /* x, y, width, height */ 0, 0, bar->width - 1, bar->height - 1); - /* Restore the foreground color of the GC if we changed it above. */ - if (f->output_data.x->scroll_bar_foreground_pixel != -1) - XSetForeground (FRAME_X_DISPLAY (f), gc, - FRAME_FOREGROUND_PIXEL (f)); + /* Restore the foreground color of the GC if we changed it above. */ + if (f->output_data.x->scroll_bar_foreground_pixel != -1) + XSetForeground (FRAME_X_DISPLAY (f), gc, + FRAME_FOREGROUND_PIXEL (f)); unblock_input (); @@ -7839,8 +7866,21 @@ handle_one_xevent (struct x_display_info *dpyinfo, f = x_top_window_to_frame (dpyinfo, event->xreparent.window); if (f) { - f->output_data.x->parent_desc = event->xreparent.parent; - x_real_positions (f, &f->left_pos, &f->top_pos); + /* Maybe we shouldn't set this for child frames ?? */ + f->output_data.x->parent_desc = event->xreparent.parent; + if (!FRAME_PARENT_FRAME (f)) + x_real_positions (f, &f->left_pos, &f->top_pos); + else + { + Window root; + unsigned int dummy_uint; + + block_input (); + XGetGeometry (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), + &root, &f->left_pos, &f->top_pos, + &dummy_uint, &dummy_uint, &dummy_uint, &dummy_uint); + unblock_input (); + } /* Perhaps reparented due to a WM restart. Reset this. */ FRAME_DISPLAY_INFO (f)->wm_type = X_WMTYPE_UNKNOWN; @@ -8000,7 +8040,26 @@ handle_one_xevent (struct x_display_info *dpyinfo, /* Check if fullscreen was specified before we where mapped the first time, i.e. from the command line. */ if (!f->output_data.x->has_been_visible) - x_check_fullscreen (f); + { + + x_check_fullscreen (f); +#ifndef USE_GTK + /* For systems that cannot synthesize `skip_taskbar' for + unmapped windows do the following. */ + if (FRAME_SKIP_TASKBAR (f)) + x_set_skip_taskbar (f, Qt, Qnil); +#endif /* Not USE_GTK */ + } + + if (!iconified) + { + /* The `z-group' is reset every time a frame becomes + invisible. Handle this here. */ + if (FRAME_Z_GROUP (f) == z_group_above) + x_set_z_group (f, Qabove, Qnil); + else if (FRAME_Z_GROUP (f) == z_group_below) + x_set_z_group (f, Qbelow, Qnil); + } SET_FRAME_VISIBLE (f, 1); SET_FRAME_ICONIFIED (f, false); @@ -8444,34 +8503,46 @@ handle_one_xevent (struct x_display_info *dpyinfo, #endif if (f) { - - /* Generate SELECT_WINDOW_EVENTs when needed. - Don't let popup menus influence things (bug#1261). */ - if (!NILP (Vmouse_autoselect_window) && !popup_activated ()) + /* Maybe generate a SELECT_WINDOW_EVENT for + `mouse-autoselect-window' but don't let popup menus + interfere with this (Bug#1261). */ + if (!NILP (Vmouse_autoselect_window) + && !popup_activated () + /* Don't switch if we're currently in the minibuffer. + This tries to work around problems where the + minibuffer gets unselected unexpectedly, and where + you then have to move your mouse all the way down to + the minibuffer to select it. */ + && !MINI_WINDOW_P (XWINDOW (selected_window)) + /* With `focus-follows-mouse' non-nil create an event + also when the target window is on another frame. */ + && (f == XFRAME (selected_frame) + || !NILP (focus_follows_mouse))) { static Lisp_Object last_mouse_window; Lisp_Object window = window_from_coordinates (f, event->xmotion.x, event->xmotion.y, 0, false); - /* Window will be selected only when it is not selected now and - last mouse movement event was not in it. Minibuffer window - will be selected only when it is active. */ + /* A window will be autoselected only when it is not + selected now and the last mouse movement event was + not in it. The remainder of the code is a bit vague + wrt what a "window" is. For immediate autoselection, + the window is usually the entire window but for GTK + where the scroll bars don't count. For delayed + autoselection the window is usually the window's text + area including the margins. */ if (WINDOWP (window) && !EQ (window, last_mouse_window) - && !EQ (window, selected_window) - /* For click-to-focus window managers - create event iff we don't leave the - selected frame. */ - && (focus_follows_mouse - || (EQ (XWINDOW (window)->frame, - XWINDOW (selected_window)->frame)))) + && !EQ (window, selected_window)) { inev.ie.kind = SELECT_WINDOW_EVENT; inev.ie.frame_or_window = window; } + /* Remember the last window where we saw the mouse. */ last_mouse_window = window; } + if (!note_mouse_movement (f, &event->xmotion)) help_echo_string = previous_help_echo_string; } @@ -8621,7 +8692,19 @@ handle_one_xevent (struct x_display_info *dpyinfo, XSETFRAME (frame, f); - x_real_positions (f, &f->left_pos, &f->top_pos); + if (!FRAME_PARENT_FRAME (f)) + x_real_positions (f, &f->left_pos, &f->top_pos); + else + { + Window root; + unsigned int dummy_uint; + + block_input (); + XGetGeometry (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), + &root, &f->left_pos, &f->top_pos, + &dummy_uint, &dummy_uint, &dummy_uint, &dummy_uint); + unblock_input (); + } if (old_left != f->left_pos || old_top != f->top_pos) { @@ -8650,8 +8733,35 @@ handle_one_xevent (struct x_display_info *dpyinfo, dpyinfo->last_mouse_glyph_frame = NULL; x_display_set_last_user_time (dpyinfo, event->xbutton.time); - f = (x_mouse_grabbed (dpyinfo) ? dpyinfo->last_mouse_frame - : x_window_to_frame (dpyinfo, event->xbutton.window)); + if (x_mouse_grabbed (dpyinfo)) + f = dpyinfo->last_mouse_frame; + else + { + f = x_window_to_frame (dpyinfo, event->xbutton.window); + + if (f && event->xbutton.type == ButtonPress + && !popup_activated () + && !x_window_to_scroll_bar (event->xbutton.display, + event->xbutton.window, 2) + && !FRAME_NO_ACCEPT_FOCUS (f)) + { + /* When clicking into a child frame or when clicking + into a parent frame with the child frame selected and + `no-accept-focus' is not set, select the clicked + frame. */ + struct frame *hf = dpyinfo->x_highlight_frame; + + if (FRAME_PARENT_FRAME (f) || frame_ancestor_p (f, hf)) + { + block_input (); + XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), + RevertToParent, CurrentTime); + if (FRAME_PARENT_FRAME (f)) + XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f)); + unblock_input (); + } + } + } #ifdef USE_GTK if (f && xg_event_is_for_scrollbar (f, event)) @@ -10007,6 +10117,7 @@ static void x_calc_absolute_position (struct frame *f) { int flags = f->size_hint_flags; + struct frame *p = FRAME_PARENT_FRAME (f); /* We have nothing to do if the current position is already for the top-left corner. */ @@ -10015,32 +10126,72 @@ x_calc_absolute_position (struct frame *f) /* Treat negative positions as relative to the leftmost bottommost position that fits on the screen. */ - if (flags & XNegative) - f->left_pos = x_display_pixel_width (FRAME_DISPLAY_INFO (f)) - - FRAME_PIXEL_WIDTH (f) + f->left_pos; + if ((flags & XNegative) && (f->left_pos <= 0)) + { + int width = FRAME_PIXEL_WIDTH (f); - { - int height = FRAME_PIXEL_HEIGHT (f); + /* A frame that has been visible at least once should have outer + edges. */ + if (f->output_data.x->has_been_visible && !p) + { + Lisp_Object frame; + Lisp_Object edges = Qnil; + + XSETFRAME (frame, f); + edges = Fx_frame_edges (frame, Qouter_edges); + if (!NILP (edges)) + width = (XINT (Fnth (make_number (2), edges)) + - XINT (Fnth (make_number (0), edges))); + } + + if (p) + f->left_pos = (FRAME_PIXEL_WIDTH (p) - width - 2 * f->border_width + + f->left_pos); + else + f->left_pos = (x_display_pixel_width (FRAME_DISPLAY_INFO (f)) + - width + f->left_pos); + + } + + if ((flags & YNegative) && (f->top_pos <= 0)) + { + int height = FRAME_PIXEL_HEIGHT (f); #if defined USE_X_TOOLKIT && defined USE_MOTIF - /* Something is fishy here. When using Motif, starting Emacs with - `-g -0-0', the frame appears too low by a few pixels. + /* Something is fishy here. When using Motif, starting Emacs with + `-g -0-0', the frame appears too low by a few pixels. - This seems to be so because initially, while Emacs is starting, - the column widget's height and the frame's pixel height are - different. The column widget's height is the right one. In - later invocations, when Emacs is up, the frame's pixel height - is right, though. + This seems to be so because initially, while Emacs is starting, + the column widget's height and the frame's pixel height are + different. The column widget's height is the right one. In + later invocations, when Emacs is up, the frame's pixel height + is right, though. - It's not obvious where the initial small difference comes from. - 2000-12-01, gerd. */ + It's not obvious where the initial small difference comes from. + 2000-12-01, gerd. */ - XtVaGetValues (f->output_data.x->column_widget, XtNheight, &height, NULL); + XtVaGetValues (f->output_data.x->column_widget, XtNheight, &height, NULL); #endif - if (flags & YNegative) - f->top_pos = x_display_pixel_height (FRAME_DISPLAY_INFO (f)) - - height + f->top_pos; + if (f->output_data.x->has_been_visible && !p) + { + Lisp_Object frame; + Lisp_Object edges = Qnil; + + XSETFRAME (frame, f); + if (NILP (edges)) + edges = Fx_frame_edges (frame, Qouter_edges); + if (!NILP (edges)) + height = (XINT (Fnth (make_number (3), edges)) + - XINT (Fnth (make_number (1), edges))); + } + + if (p) + f->top_pos = (FRAME_PIXEL_HEIGHT (p) - height - 2 * f->border_width + + f->top_pos); + else + f->top_pos = (x_display_pixel_height (FRAME_DISPLAY_INFO (f)) + - height + f->top_pos); } /* The left_pos and top_pos @@ -10125,6 +10276,7 @@ x_set_offset (struct frame *f, register int xoff, register int yoff, int change_ need to compute the top/left offset adjustment for this frame. */ if (change_gravity != 0 + && !FRAME_PARENT_FRAME (f) && (FRAME_DISPLAY_INFO (f)->wm_type == X_WMTYPE_UNKNOWN || (FRAME_DISPLAY_INFO (f)->wm_type == X_WMTYPE_A && (FRAME_X_OUTPUT (f)->move_offset_left == 0 @@ -10255,6 +10407,92 @@ x_set_sticky (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) dpyinfo->Xatom_net_wm_state_sticky, None); } +/** + * x_set_skip_taskbar: + * + * Set frame F's `skip-taskbar' parameter. If non-nil, this should + * remove F's icon from the taskbar associated with the display of F's + * window-system window and inhibit switching to F's window via + * -. If nil, lift these restrictions. + * + * Some window managers may not honor this parameter. + */ +void +x_set_skip_taskbar (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) +{ + if (!EQ (new_value, old_value)) + { +#ifdef USE_GTK + xg_set_skip_taskbar (f, new_value); +#else + Lisp_Object frame; + struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); + + XSETFRAME (frame, f); + set_wm_state (frame, !NILP (new_value), + dpyinfo->Xatom_net_wm_state_skip_taskbar, None); +#endif /* USE_GTK */ + FRAME_SKIP_TASKBAR (f) = !NILP (new_value); + } +} + +/** + * x_set_z_group: + * + * Set frame F's `z-group' parameter. If `above', F's window-system + * window is displayed above all windows that do not have the `above' + * property set. If nil, F's window is shown below all windows that + * have the `above' property set and above all windows that have the + * `below' property set. If `below', F's window is displayed below all + * windows that do not have the `below' property set. + * + * Some window managers may not honor this parameter. + */ +void +x_set_z_group (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) +{ + /* We don't care about old_value. The window manager might have + reset the value without telling us. */ + Lisp_Object frame; + struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); + + XSETFRAME (frame, f); + + if (NILP (new_value)) + { + set_wm_state (frame, false, + dpyinfo->Xatom_net_wm_state_above, None); + set_wm_state (frame, false, + dpyinfo->Xatom_net_wm_state_below, None); + FRAME_Z_GROUP (f) = z_group_none; + } + else if (EQ (new_value, Qabove)) + { + set_wm_state (frame, true, + dpyinfo->Xatom_net_wm_state_above, None); + set_wm_state (frame, false, + dpyinfo->Xatom_net_wm_state_below, None); + FRAME_Z_GROUP (f) = z_group_above; + } + else if (EQ (new_value, Qbelow)) + { + set_wm_state (frame, false, + dpyinfo->Xatom_net_wm_state_above, None); + set_wm_state (frame, true, + dpyinfo->Xatom_net_wm_state_below, None); + FRAME_Z_GROUP (f) = z_group_below; + } + else if (EQ (new_value, Qabove_suspended)) + { + set_wm_state (frame, false, + dpyinfo->Xatom_net_wm_state_above, None); + FRAME_Z_GROUP (f) = z_group_above_suspended; + } + else + error ("Invalid z-group specification"); +} + + /* Return the current _NET_WM_STATE. SIZE_STATE is set to one of the FULLSCREEN_* values. Set *STICKY to the sticky state. @@ -10758,7 +10996,8 @@ x_set_window_size_1 (struct frame *f, bool change_gravity, int old_height = FRAME_PIXEL_HEIGHT (f); Lisp_Object fullscreen = get_frame_param (f, Qfullscreen); - if (change_gravity) f->win_gravity = NorthWestGravity; + if (change_gravity) + f->win_gravity = NorthWestGravity; x_wm_set_size_hint (f, 0, false); /* When the frame is fullheight and we only want to change the width @@ -11047,6 +11286,26 @@ xembed_send_message (struct frame *f, Time t, enum xembed_message msg, void x_make_frame_visible (struct frame *f) { + if (FRAME_PARENT_FRAME (f)) + { + if (!FRAME_VISIBLE_P (f)) + { + block_input (); +#ifdef USE_GTK + gtk_widget_show_all (FRAME_GTK_OUTER_WIDGET (f)); + XMoveWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), + f->left_pos, f->top_pos); +#else + XMapRaised (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f)); +#endif + unblock_input (); + + SET_FRAME_VISIBLE (f, true); + SET_FRAME_ICONIFIED (f, false); + } + return; + } + block_input (); x_set_bitmap_icon (f); @@ -11115,9 +11374,10 @@ x_make_frame_visible (struct frame *f) because the window manager may choose the position and we don't want to override it. */ - if (! FRAME_VISIBLE_P (f) - && ! FRAME_ICONIFIED_P (f) - && ! FRAME_X_EMBEDDED_P (f) + if (!FRAME_VISIBLE_P (f) + && !FRAME_ICONIFIED_P (f) + && !FRAME_X_EMBEDDED_P (f) + && !FRAME_PARENT_FRAME (f) && f->win_gravity == NorthWestGravity && previously_visible) { @@ -11180,15 +11440,15 @@ x_make_frame_invisible (struct frame *f) xembed_set_info (f, 0); else #endif - { - if (! XWithdrawWindow (FRAME_X_DISPLAY (f), window, - DefaultScreen (FRAME_X_DISPLAY (f)))) - { - unblock_input (); - error ("Can't notify window manager of window withdrawal"); - } - } + if (! XWithdrawWindow (FRAME_X_DISPLAY (f), window, + DefaultScreen (FRAME_X_DISPLAY (f)))) + { + unblock_input (); + error ("Can't notify window manager of window withdrawal"); + } + + x_sync (f); /* We can't distinguish this from iconification just by the event that we get from the server. @@ -11198,8 +11458,6 @@ x_make_frame_invisible (struct frame *f) SET_FRAME_VISIBLE (f, 0); SET_FRAME_ICONIFIED (f, false); - x_sync (f); - unblock_input (); } @@ -12355,6 +12613,9 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) ATOM_REFS_INIT ("SM_CLIENT_ID", Xatom_SM_CLIENT_ID) ATOM_REFS_INIT ("_XSETTINGS_SETTINGS", Xatom_xsettings_prop) ATOM_REFS_INIT ("MANAGER", Xatom_xsettings_mgr) + ATOM_REFS_INIT ("_NET_WM_STATE_SKIP_TASKBAR", Xatom_net_wm_state_skip_taskbar) + ATOM_REFS_INIT ("_NET_WM_STATE_ABOVE", Xatom_net_wm_state_above) + ATOM_REFS_INIT ("_NET_WM_STATE_BELOW", Xatom_net_wm_state_below) }; int i; diff --git a/src/xterm.h b/src/xterm.h index 32c879bcdc..a75257006f 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -447,9 +447,9 @@ struct x_display_info /* Atoms dealing with EWMH (i.e. _NET_...) */ Atom Xatom_net_wm_state, Xatom_net_wm_state_fullscreen, Xatom_net_wm_state_maximized_horz, Xatom_net_wm_state_maximized_vert, - Xatom_net_wm_state_sticky, Xatom_net_wm_state_hidden, - Xatom_net_frame_extents, - Xatom_net_current_desktop, Xatom_net_workarea; + Xatom_net_wm_state_sticky, Xatom_net_wm_state_above, Xatom_net_wm_state_below, + Xatom_net_wm_state_hidden, Xatom_net_wm_state_skip_taskbar, + Xatom_net_frame_extents, Xatom_net_current_desktop, Xatom_net_workarea; /* XSettings atoms and windows. */ Atom Xatom_xsettings_sel, Xatom_xsettings_prop, Xatom_xsettings_mgr; @@ -1168,6 +1168,8 @@ x_mutable_colormap (Visual *visual) } extern void x_set_sticky (struct frame *, Lisp_Object, Lisp_Object); +extern void x_set_skip_taskbar (struct frame *, Lisp_Object, Lisp_Object); +extern void x_set_z_group (struct frame *, Lisp_Object, Lisp_Object); extern bool x_wm_supports (struct frame *, Atom); extern void x_wait_for_event (struct frame *, int); extern void x_clear_under_internal_border (struct frame *f);