diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 51c43c7..a507e30 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -286,7 +286,11 @@ minibuffer-prompt-properties--setter ;; 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/faces.el b/lisp/faces.el index d4f2f08..54eb294 100644 --- a/lisp/faces.el +++ b/lisp/faces.el @@ -2626,6 +2626,13 @@ window-divider-last-pixel :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/frame.el b/lisp/frame.el index 0a35b71..202a8d1 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -115,15 +115,19 @@ minibuffer-frame-alist (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 (cdr (frame-parameter frame-1 'parent-frame))) + (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)))) @@ -144,6 +148,13 @@ handle-focus-out This function runs the hook `focus-out-hook'." (interactive "e") (run-hooks 'focus-out-hook)) + +(defun handle-move-frame (event) + "Handle a move-frame event. +This function runs the abnormal hook `move-frame-functions'." + (interactive "e") + (let ((frame (posn-window (event-start event)))) + (run-hook-with-args 'move-frame-functions frame))) ;;;; Arrangement of frames at startup @@ -827,21 +838,24 @@ other-frame 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))) @@ -1483,6 +1497,56 @@ frame-monitor-attributes 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 bottommost (first) to topmost (last). +Child frames appear right after their parent frame. 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"))) ;;;; Frame/display capabilities. @@ -1877,8 +1941,8 @@ frame-geom-spec-cons (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. +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))) @@ -1887,14 +1951,16 @@ delete-other-frames ;; 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) + (eq (frame-parameter this 'parent-frame) 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) + (eq (frame-parameter this 'parent-frame) frame)) (delete-frame this)) (setq this next)))) diff --git a/lisp/mwheel.el b/lisp/mwheel.el index eaeb831..ee894b0 100644 --- a/lisp/mwheel.el +++ b/lisp/mwheel.el @@ -190,17 +190,39 @@ mwheel-scroll 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))) + (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)) @@ -232,18 +254,18 @@ mwheel-scroll ;; 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/scroll-bar.el b/lisp/scroll-bar.el index 5290a7b..5835274 100644 --- a/lisp/scroll-bar.el +++ b/lisp/scroll-bar.el @@ -281,7 +281,7 @@ scroll-bar-drag (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 @@ scroll-bar-horizontal-drag (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 @@ scroll-bar-scroll-down (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 @@ scroll-bar-scroll-up (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 @@ scroll-bar-toolkit-scroll (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 @@ scroll-bar-toolkit-horizontal-scroll (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 358d7bc..9b1ff3a 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -1533,7 +1533,7 @@ window-min-size (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))))) @@ -8272,6 +8272,165 @@ shrink-window-if-larger-than-buffer (when (and (window-combined-p window) (pos-visible-in-window-p (point-min) window)) (fit-window-to-buffer window (window-total-height window)))) + +(defun window-largest-empty-rectangle--maximums-1 (quad maximums) + (cond + ((null maximums) + (list quad)) + ((> (car quad) (caar maximums)) + (cons quad maximums)) + (t + (cons (car maximums) + (window-largest-empty-rectangle--maximums-1 quad (cdr maximums)))))) + +(defun window-largest-empty-rectangle--maximums (quad maximums count) + (setq maximums (window-largest-empty-rectangle--maximums-1 quad maximums)) + (if (> (length maximums) count) + (nbutlast maximums) + maximums)) + +(defun window-largest-empty-rectangle--disjoint-maximums (maximums count) + (setq maximums (sort maximums (lambda (x y) (> (car x) (car y))))) + (let ((new-length 0) + new-maximums) + (while (and maximums (< new-length count)) + (let* ((maximum (car maximums)) + (at (nth 2 maximum)) + (to (nth 3 maximum))) + (catch 'drop + (dolist (new-maximum new-maximums) + (let ((new-at (nth 2 new-maximum)) + (new-to (nth 3 new-maximum))) + (when (if (< at new-at) (> to new-at) (< at new-to)) + ;; Intersection -> drop. + (throw 'drop nil)))) + (setq new-maximums (cons maximum new-maximums)) + (setq new-length (1+ new-length))) + (setq maximums (cdr maximums)))) + + (nreverse new-maximums))) + +(defun window-largest-empty-rectangle (&optional window count min-width min-height positions left) + "Return largest empty rectangle in WINDOW. +WINDOW must be live window and defaults to the selected one. + +The return value is a triple of the width and the start and end +Y-coordinates of the largest rectangle that can be inscribed into +the empty space (the space not displaying any text) of WINDOW. +The return value is nil if the current glyph matrix of WINDOW is +not up-to-date. + +Optional argument COUNT, if non-nil, specifies the maximum number +of rectangles to return. This means that the return value is a +list of triples specifying rectangles with the largest rectangle +first. COUNT can be also a cons cell whose car specifies the +number of rectangles to return and whose cdr, if non-nil, states +that all rectangles returned must be disjoint. + +Note that the right edge of any rectangle returned by this +function is the right edge of WINDOW (the left edge if its buffer +displays RTL text). + +Optional arguments MIN-WIDTH and MIN-HEIGHT, if non-nil, specify +the minimum width and height of any rectangle returned. + +Optional argument POSITIONS, if non-nil, is a cons cell whose car +specifies the uppermost and whose cdr specifies the lowermost +pixel position that must be covered by any rectangle returned. +Note that positions are counted from the start of the text area +of WINDOW. + +Optional argument LEFT, if non-nil, means to return values suitable for +buffers displaying right to left text." + ;; Process lines as returned by ‘window-lines-pixel-dimensions’. + ;; STACK is a stack that contains rows that have to be processed yet. + (let* ((window (window-normalize-window window t)) + (disjoint (and (consp count) (cdr count))) + (count (or (and (numberp count) count) + (and (consp count) (numberp (car count)) (car count)))) + (rows (window-lines-pixel-dimensions window nil nil t t left)) + (rows-at 0) + (max-size 0) + row stack stack-at stack-to + top top-width top-at top-to top-size + max-width max-at max-to maximums) + ;; ROWS-AT is the position where the first element of ROWS starts. + ;; STACK-AT is the position where the first element of STACK starts. + (while rows + (setq row (car rows)) + (if (or (not stack) (>= (car row) (caar stack))) + (progn + (unless stack + (setq stack-at rows-at)) + (setq stack (cons row stack)) + ;; Set ROWS-AT to where the first element of ROWS ends + ;; which, after popping ROW, makes it the start position of + ;; the next ROW. + (setq rows-at (cdr row)) + (setq rows (cdr rows))) + (setq top (car stack)) + (setq stack (cdr stack)) + (setq top-width (car top)) + (setq top-at (if stack (cdar stack) stack-at)) + (setq top-to (cdr top)) + (setq top-size (* top-width (- top-to top-at))) + (unless (or (and min-width (< top-width min-width)) + (and min-height (< (- top-to top-at) min-height)) + (and positions + (or (> top-at (car positions)) + (< top-to (cdr positions))))) + (if count + (if disjoint + (setq maximums (cons (list top-size top-width top-at top-to) + maximums)) + (setq maximums (window-largest-empty-rectangle--maximums + (list top-size top-width top-at top-to) + maximums count))) + (when (> top-size max-size) + (setq max-size top-size) + (setq max-width top-width) + (setq max-at top-at) + (setq max-to top-to)))) + (if (and stack (> (caar stack) (car row))) + ;; Have new top element of stack include old top. + (setq stack (cons (cons (caar stack) (cdr top)) (cdr stack))) + ;; Move rows-at backwards to top-at. + (setq rows-at top-at)))) + + (when stack + ;; STACK-TO is the position where the stack ends. + (setq stack-to (cdar stack)) + (while stack + (setq top (car stack)) + (setq stack (cdr stack)) + (setq top-width (car top)) + (setq top-at (if stack (cdar stack) stack-at)) + (setq top-size (* top-width (- stack-to top-at))) + (unless (or (and min-width (< top-width min-width)) + (and min-height (< (- stack-to top-at) min-height)) + (and positions + (or (> top-at (car positions)) + (< stack-to (cdr positions))))) + (if count + (if disjoint + (setq maximums (cons (list top-size top-width top-at stack-to) + maximums)) + (setq maximums (window-largest-empty-rectangle--maximums + (list top-size top-width top-at stack-to) + maximums count))) + (when (> top-size max-size) + (setq max-size top-size) + (setq max-width top-width) + (setq max-at top-at) + (setq max-to stack-to)))))) + + (cond + (maximums + (if disjoint + (window-largest-empty-rectangle--disjoint-maximums maximums count) + maximums)) + ((> max-size 0) + (list max-width max-at max-to max-size))))) (defun kill-buffer-and-window () "Kill the current buffer and delete the selected window." @@ -8764,8 +8923,11 @@ mouse-autoselect-window-select (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 window + (or (and (not (numberp mouse-autoselect-window)) + ;; Moved here to allow autoselection of window in + ;; child frames. + (not (eq window (selected-window)))) (and (>= mouse-autoselect-window 0) ;; If `mouse-autoselect-window' is non-negative, ;; select window if it's the same as before. @@ -8793,7 +8955,11 @@ mouse-autoselect-window-select (setq unread-command-events (cons (list 'select-window (list window)) unread-command-events)))) - ((or (and window (eq window (selected-window))) + ((or (not window) + ;; The following was commented out to allow autoselection of + ;; child windows. + + ;; (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 @@ -8807,9 +8973,13 @@ mouse-autoselect-window-select (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 (window-frame window))) (unless (or (not (window-live-p window)) - ;; Don't switch if we're currently in the minibuffer. + ;; Don't switch to a `no-accept-focus' frame. + (and (not (eq frame (selected-frame))) + (frame-parameter frame 'no-accept-focus)) + ;; 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 @@ -8838,6 +9008,11 @@ handle-select-window (run-hooks 'mouse-leave-buffer-hook)) ;; Clear echo area. (message nil) + (when (eq focus-follows-mouse 'auto-raise) + (raise-frame frame)) + ;; Ensure, if possible, that FRAME gets input focus. + (when (memq (window-system frame) '(x w32 ns)) + (x-focus-frame frame)) (select-window window)))) (defun truncated-partial-width-window-p (&optional window) diff --git a/src/dispextern.h b/src/dispextern.h index e030618..3f3166d 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 d0f653f..67fc1ff 100644 --- a/src/frame.c +++ b/src/frame.c @@ -324,11 +324,51 @@ struct frame * 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,13 @@ struct frame * 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; + 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 @@ -1253,12 +1300,15 @@ of them (the selected terminal frame) is actually displayed. to that frame. */) (Lisp_Object event) { + Lisp_Object value; + /* Preserve prefix arg that the command loop just cleared. */ kset_prefix_arg (current_kboard, Vcurrent_prefix_arg); run_hook (Qmouse_leave_buffer_hook); /* `switch-frame' implies a focus in. */ + value = do_switch_frame (event, 0, 0, Qnil); call1 (intern ("handle-focus-in"), event); - return do_switch_frame (event, 0, 0, Qnil); + return value; } DEFUN ("selected-frame", Fselected_frame, Sselected_frame, 0, 0, 0, @@ -1270,16 +1320,48 @@ of them (the selected terminal frame) is actually displayed. DEFUN ("frame-list", Fframe_list, Sframe_list, 0, 0, 0, - doc: /* Return a list of all live frames. */) + doc: /* Return a list of all live frames. +Child frames are not included. */) (void) { - Lisp_Object frames; - frames = Fcopy_sequence (Vframe_list); #ifdef HAVE_WINDOW_SYSTEM - if (FRAMEP (tip_frame)) - frames = Fdelq (tip_frame, frames); + Lisp_Object list = Qnil, frames, frame; + struct frame *f; + + FOR_EACH_FRAME (frames, frame) + { + f = XFRAME (frame); + if (!FRAME_PARENT_FRAME (f) + && NILP (Fframe_parameter (frame, Qtooltip))) + list = Fcons (frame, list); + } + /* frame-list always returned frames in reverse chronological order, + i.e., with the most recently created frame first. Keep that. */ + return Fnreverse (list); +#else /* !HAVE_WINDOW_SYSTEM */ + return Fcopy_sequence (Vframe_list); #endif - return frames; +} + +DEFUN ("frame-child-frames", Fframe_child_frames, Sframe_child_frames, + 0, 1, 0, + doc: /* Return a list of all child frames of the specified FRAME. +FRAME nil or omitted means the selected frame. */) + (Lisp_Object frame) +{ +#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_NS) + struct frame *f = decode_any_frame (frame); + + XSETFRAME (frame, f); + Lisp_Object list = Qnil, frames, frame1; + + FOR_EACH_FRAME (frames, frame1) + if (FRAME_PARENT_FRAME (XFRAME (frame1)) == f) + list = Fcons (frame1, list); + return Fnreverse (list); +#else /* !HAVE_WINDOW_SYSTEM or HAVE_NS */ + return Qnil; +#endif /* HAVE_WINDOW_SYSTEM and not HAVE_NS */ } /* Return CANDIDATE if it can be used as 'other-than-FRAME' frame on the @@ -1302,7 +1384,9 @@ of them (the selected terminal frame) is actually displayed. || (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; @@ -1440,35 +1524,65 @@ of them (the selected terminal frame) is actually displayed. 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 @@ -1517,53 +1631,65 @@ of them (the selected terminal frame) is actually displayed. } -/* 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"); } @@ -1592,20 +1718,26 @@ of them (the selected terminal frame) is actually displayed. 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. @@ -1709,8 +1841,6 @@ of them (the selected terminal frame) is actually displayed. promise that the terminal of the frame must be valid until we have called the window-system-dependent frame destruction routine. */ - - { struct terminal *terminal; block_input (); @@ -1747,16 +1877,15 @@ of them (the selected terminal frame) is actually displayed. 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; } } @@ -1766,13 +1895,12 @@ of them (the selected terminal frame) is actually displayed. 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); @@ -1784,29 +1912,27 @@ of them (the selected terminal frame) is actually displayed. 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)) @@ -2119,7 +2245,7 @@ of them (the selected terminal frame) is actually displayed. { 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. */ @@ -2457,9 +2583,39 @@ of them (the selected terminal frame) is actually displayed. } } + /* 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)) @@ -2468,7 +2624,7 @@ of them (the selected terminal frame) is actually displayed. 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)) @@ -3099,6 +3255,12 @@ struct frame_parm_table { {"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)}, }; #ifdef HAVE_WINDOW_SYSTEM @@ -4886,6 +5048,13 @@ or a list (- N) meaning -N pixels relative to bottom/right corner. 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 (Qdelete_before, "delete-before"); DEFSYM (Qmodeline, "modeline"); DEFSYM (Qonly, "only"); DEFSYM (Qnone, "none"); @@ -5017,6 +5186,11 @@ or a list (- N) meaning -N pixels relative to bottom/right corner. 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 (Qmin_width, "min-width"); + DEFSYM (Qmin_height, "min-height"); + DEFSYM (Qmouse_wheel_frame, "mouse-wheel-frame"); { int i; @@ -5133,6 +5307,11 @@ even if the end of the buffer is shown (i.e. overscrolling). doc: /* Normal hook run when a frame loses input focus. */); Vfocus_out_hook = Qnil; + DEFVAR_LISP ("move-frame-functions", Vmove_frame_functions, + doc: /* Functions run after a frame was moved. +The functions are run with one arg, the frame that moved. */); + Vmove_frame_functions = Qnil; + DEFVAR_LISP ("delete-frame-functions", Vdelete_frame_functions, doc: /* Functions run before deleting a frame. The functions are run with one arg, the frame to be deleted. @@ -5179,12 +5358,51 @@ either customize it (see the info node `Easy Customization') 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 +5504,7 @@ This variable is effective only with the X toolkit (and there only when defsubr (&Sselect_frame); defsubr (&Sselected_frame); defsubr (&Sframe_list); + defsubr (&Sframe_child_frames); defsubr (&Snext_frame); defsubr (&Sprevious_frame); defsubr (&Slast_nonminibuf_frame); diff --git a/src/frame.h b/src/frame.h index 7331352..a5198bc 100644 --- a/src/frame.h +++ b/src/frame.h @@ -45,6 +45,12 @@ enum fullscreen_type #endif }; +enum z_group +{ + z_group_none, + z_group_above, + z_group_below, +}; #endif /* HAVE_WINDOW_SYSTEM */ /* The structure representing a frame. */ @@ -68,6 +74,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 +331,25 @@ 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; + + /* 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 +564,13 @@ struct frame { 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) { @@ -850,7 +887,6 @@ struct frame #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) @@ -860,17 +896,39 @@ struct frame ((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) +#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_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_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) \ @@ -1037,7 +1095,8 @@ struct frame 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 @@ -1217,7 +1276,7 @@ extern void frame_size_history_add (struct frame *f, Lisp_Object fun_symbol, 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) { diff --git a/src/gtkutil.c b/src/gtkutil.c index b028254..9b545b3 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -813,30 +813,6 @@ struct xg_frame_tb_info } } -/* 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) { @@ -883,7 +859,7 @@ struct xg_frame_tb_info || 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); @@ -911,7 +887,7 @@ struct xg_frame_tb_info &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)) { @@ -1178,7 +1154,14 @@ struct xg_frame_tb_info 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; @@ -1334,7 +1317,9 @@ struct xg_frame_tb_info /* 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); @@ -1460,6 +1445,86 @@ struct xg_frame_tb_info } } +/* 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 (); +} + /* Set the frame icon to ICON_PIXMAP/MASK. This must be done with GTK functions so GTK does not overwrite the icon. */ @@ -3740,6 +3805,7 @@ struct xg_dialog_data GtkWidget *wparent = gtk_widget_get_parent (wscroll); gint msl; int scale = xg_get_gdk_scale (); + bool hidden; top /= scale; left /= scale; @@ -3764,6 +3830,7 @@ struct xg_dialog_data the height is less than the min size. */ gtk_widget_hide (wparent); gtk_widget_hide (wscroll); + hidden = true; } else { @@ -3778,6 +3845,15 @@ struct xg_dialog_data 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. */ @@ -3843,6 +3919,15 @@ struct xg_dialog_data 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 d67a7bc..12c8d68 100644 --- a/src/gtkutil.h +++ b/src/gtkutil.h @@ -150,7 +150,6 @@ extern void xg_set_toolkit_horizontal_scroll_bar_thumb (struct scroll_bar *bar, 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); @@ -172,6 +171,12 @@ 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 bool xg_prepare_tooltip (struct frame *f, Lisp_Object string, int *width, diff --git a/src/keyboard.c b/src/keyboard.c index ed8e71f..6f3eb09 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -4056,6 +4056,14 @@ static Lisp_Object kbd_buffer_get_event (KBOARD **kbp, bool *used_mouse_menu, kbd_fetch_ptr = event + 1; } #endif +#if defined (HAVE_X11) || defined (HAVE_NTGUI) + else if (event->kind == MOVE_FRAME_EVENT) + { + /* Make an event (move-frame (FRAME)). */ + obj = list2 (Qmove_frame, list1 (event->ie.frame_or_window)); + kbd_fetch_ptr = event + 1; + } +#endif #ifdef HAVE_XWIDGETS else if (event->kind == XWIDGET_EVENT) { @@ -10972,6 +10980,7 @@ struct event_head {SYMBOL_INDEX (Qfocus_in), SYMBOL_INDEX (Qfocus_in)}, {SYMBOL_INDEX (Qfocus_out), SYMBOL_INDEX (Qfocus_out)}, + {SYMBOL_INDEX (Qmove_frame), SYMBOL_INDEX (Qmove_frame)}, {SYMBOL_INDEX (Qdelete_frame), SYMBOL_INDEX (Qdelete_frame)}, {SYMBOL_INDEX (Qiconify_frame), SYMBOL_INDEX (Qiconify_frame)}, {SYMBOL_INDEX (Qmake_frame_visible), SYMBOL_INDEX (Qmake_frame_visible)}, @@ -11144,6 +11153,7 @@ struct event_head DEFSYM (Qswitch_frame, "switch-frame"); DEFSYM (Qfocus_in, "focus-in"); DEFSYM (Qfocus_out, "focus-out"); + DEFSYM (Qmove_frame, "move-frame"); DEFSYM (Qdelete_frame, "delete-frame"); DEFSYM (Qiconify_frame, "iconify-frame"); DEFSYM (Qmake_frame_visible, "make-frame-visible"); @@ -11890,6 +11900,8 @@ shutdown when Emacs receives a fatal signal (e.g., a crash). "handle-focus-in"); initial_define_lispy_key (Vspecial_event_map, "focus-out", "handle-focus-out"); + initial_define_lispy_key (Vspecial_event_map, "move-frame", + "handle-move-frame"); } /* Mark the pointers in the kboard objects. diff --git a/src/nsfns.m b/src/nsfns.m index a709935..912493e 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -972,6 +972,12 @@ 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 */ }; @@ -1248,6 +1254,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 63f1b15..3cd569f 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -6307,7 +6307,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/termhooks.h b/src/termhooks.h index 3b1b495..14ec397 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -202,6 +202,9 @@ enum event_kind FOCUS_OUT_EVENT, + /* Generated when a frame is moved. */ + MOVE_FRAME_EVENT, + /* Generated when mouse moves over window not currently selected. */ SELECT_WINDOW_EVENT, diff --git a/src/w32fns.c b/src/w32fns.c index 1b628b0..7256925 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -256,6 +256,10 @@ typedef BOOL (WINAPI * GetTitleBarInfo_Proc) # 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 @@ struct frame * 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; @@ -1627,7 +1634,13 @@ struct frame * #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); @@ -1638,12 +1651,26 @@ struct frame * 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 (); } @@ -1682,7 +1709,7 @@ struct frame * 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 +1982,227 @@ struct frame * 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) +{ + if (!EQ (new_value, 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, 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 +2261,12 @@ struct frame * 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 +2275,12 @@ struct frame * 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 +2289,55 @@ struct frame * 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 +2352,16 @@ struct frame * } 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 +2377,12 @@ struct frame * /* 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 +4678,22 @@ struct frame * } } + 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 +4799,10 @@ struct frame * 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 +5029,34 @@ struct frame * } 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. */ @@ -4899,6 +5231,10 @@ struct frame * 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; } @@ -5130,7 +5466,8 @@ struct frame * 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) @@ -5318,7 +5655,7 @@ struct frame * 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; @@ -5355,10 +5692,11 @@ struct frame * 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. */ @@ -5381,6 +5719,31 @@ struct frame * 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); @@ -5408,7 +5771,9 @@ struct frame * 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 @@ -5492,23 +5857,42 @@ struct frame * "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); @@ -5516,10 +5900,17 @@ struct frame * /* 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), @@ -5530,9 +5921,7 @@ struct frame * 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); @@ -5597,29 +5986,36 @@ struct frame * 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) @@ -6568,8 +6964,6 @@ static void compute_tip_xy (struct frame *, Lisp_Object, Lisp_Object, 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. */ @@ -6635,6 +7029,7 @@ static void compute_tip_xy (struct frame *, Lisp_Object, Lisp_Object, 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); @@ -8555,6 +8950,140 @@ value is a list of the form (LEFT, TOP, RIGHT, BOTTOM). All values are } } +/** + * 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 frames) +{ + while (window) + { + HWND child; + struct frame *f = x_window_to_frame (dpyinfo, window); + Lisp_Object frame; + + /* Process child windows first - they precede their parent in the + z-order. */ + block_input (); + child = GetWindow (window, GW_CHILD); + unblock_input (); + if (child) + frames = w32_frame_list_z_order (dpyinfo, child, frames); + + if (f) + { + XSETFRAME (frame, f); + frames = Fcons (frame, frames); + } + + block_input (); + window = GetNextWindow (window, GW_HWNDNEXT); + unblock_input (); + } + + return 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. + +Frames are listed from bottommost (first) to topmost (last). Child +frames appear right after their parent frame. Return nil if DISPLAY +contains no Emacs frame. */) + (Lisp_Object display) +{ + struct w32_display_info *dpyinfo = check_x_display_info (display); + HWND window; + + block_input (); + window = GetTopWindow (NULL); + unblock_input (); + + return w32_frame_list_z_order (dpyinfo, window, Qnil); +} + +/** + * 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. @@ -9749,6 +10278,12 @@ enum NI_Severity { 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, }; void @@ -10128,6 +10663,8 @@ successive mouse move (or scroll bar drag) events before they are 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/w32term.c b/src/w32term.c index d6b78fd..b88a855 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -780,9 +780,23 @@ struct record 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 (); @@ -3082,7 +3096,8 @@ static void w32_draw_box_rect (struct glyph_string *, int, int, int, int, 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); @@ -3433,8 +3448,22 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object /* 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) @@ -3877,11 +3906,15 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object 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. */ 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; @@ -3975,11 +4008,15 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object 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. */ 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); @@ -4516,6 +4553,7 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object 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); @@ -4633,7 +4671,7 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object 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); @@ -4645,6 +4683,7 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object msg.rect.top, msg.rect.right - msg.rect.left, msg.rect.bottom - msg.rect.top); + x_clear_under_internal_border (f); } } break; @@ -4819,8 +4858,15 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object 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 @@ -4832,20 +4878,16 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object 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; } @@ -4948,21 +4990,40 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object 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; @@ -5015,6 +5076,7 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object w32fullscreen_hook (f); } } + check_visibility = 1; break; @@ -5022,7 +5084,11 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object f = x_window_to_frame (dpyinfo, msg.msg.hwnd); if (f && !FRAME_ICONIFIED_P (f)) - x_real_positions (f, &f->left_pos, &f->top_pos); + { + x_real_positions (f, &f->left_pos, &f->top_pos); + inev.kind = MOVE_FRAME_EVENT; + XSETFRAME (inev.frame_or_window, f); + } check_visibility = 1; break; @@ -5051,6 +5117,9 @@ static void x_horizontal_scroll_bar_report_motion (struct frame **, Lisp_Object } #endif + if (f = x_window_to_frame (dpyinfo, msg.msg.hwnd)) + x_clear_under_internal_border (f); + check_visibility = 1; break; @@ -6026,11 +6095,16 @@ struct xim_inst_t 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 (); } @@ -6234,11 +6308,18 @@ struct xim_inst_t 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) @@ -6453,7 +6534,11 @@ struct xim_inst_t 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 990d379..eada7cc 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -706,7 +706,7 @@ struct scroll_bar { 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 9569044..a7b9afc 100644 --- a/src/window.c +++ b/src/window.c @@ -492,7 +492,7 @@ struct window * 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 @@ struct 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 @@ -1897,6 +1898,128 @@ of the (first) text line, YPOS is negative. return list4i (row->height + min (0, row->y) - crop, i, row->y, crop); } +DEFUN ("window-lines-pixel-dimensions", Fwindow_lines_pixel_dimensions, Swindow_lines_pixel_dimensions, 0, 6, 0, + doc: /* Return pixel dimensions of WINDOW's lines. +The return value is a list of the x- and y-coordinates of the lower +right corner of the last character of each line. Return nil if the +current glyph matrix of WINDOW is not up-to-date. + +Optional argument WINDOW specifies the window whose lines' dimensions +shall be returned. Nil or omitted means to return the dimensions for +the selected window. + +FIRST, if non-nil, specifies the first line whose dimensions shall be +returned. If FIRST is nil and BODY is non-nil, start with the first +text line of WINDOW. Otherwise, start with the first line of WINDOW. + +LAST, if non-nil, specifies the last line whose dimensions shall be +returned. If LAST is nil and BODY is non-nil, the last line is the last +line of the body (text area) of WINDOW. Otherwise, last is the last +line of WINDOW. + +INVERSE, if nil, means that the y-pixel value returned for a specific +line specifies the distance in pixels from the left edge (body edge if +BODY is non-nil) of WINDOW to the right edge of the last glyph of that +line. INVERSE non-nil means that the y-pixel value returned for a +specific line specifies the distance in pixels from the right edge of +the last glyph of that line to the right edge (body edge if BODY is +non-nil) of WINDOW. + +LEFT non-nil means to return the x- and y-coordinates of the lower left +corner of the leftmost character on each line. This is the value that +should be used for buffers that mostly display text from right to left. + +If LEFT is non-nil and INVERSE is nil, this means that the y-pixel value +returned for a specific line specifies the distance in pixels from the +left edge of the last (leftmost) glyph of that line to the right edge +(body edge if BODY is non-nil) of WINDOW. If LEFT and INVERSE are both +non-nil, the y-pixel value returned for a specific line specifies the +distance in pixels from the left edge (body edge if BODY is non-nil) of +WINDOW to the left edge of the last (leftmost) glyph of that line. + +Normally, the value of this function is not available while Emacs is +busy, for example, when processing a command. It should be retrievable +though when run from an idle timer with a delay of zero seconds. */) + (Lisp_Object window, Lisp_Object first, Lisp_Object last, Lisp_Object body, Lisp_Object inverse, Lisp_Object left) +{ + struct window *w = decode_live_window (window); + struct buffer *b; + struct glyph_row *row, *end_row; + int max_y = NILP (body) ? WINDOW_PIXEL_HEIGHT (w) : window_text_bottom_y (w); + Lisp_Object rows = Qnil; + int window_width = NILP (body) ? w->pixel_width : window_body_width (w, true); + int header_line_height = WINDOW_HEADER_LINE_HEIGHT (w); + int subtract = NILP (body) ? 0 : header_line_height; + bool invert = !NILP (inverse); + bool left_flag = !NILP (left); + + if (noninteractive || w->pseudo_window_p) + return Qnil; + + CHECK_BUFFER (w->contents); + b = XBUFFER (w->contents); + + /* Fail if current matrix is not up-to-date. */ + if (!w->window_end_valid + || windows_or_buffers_changed + || b->clip_changed + || b->prevent_redisplay_optimizations_p + || window_outdated (w)) + return Qnil; + + if (NILP (first)) + row = MATRIX_ROW (w->current_matrix, 0); + else if (NUMBERP (first)) + { + CHECK_RANGED_INTEGER (first, 0, w->current_matrix->nrows); + row = MATRIX_ROW (w->current_matrix, XINT (first)); + } + else if (!NILP (body)) + row = MATRIX_FIRST_TEXT_ROW (w->current_matrix); + else + error ("Invalid specification of first line"); + + if (NILP (last)) + end_row = MATRIX_ROW (w->current_matrix, w->current_matrix->nrows); + else if (NUMBERP (first)) + { + CHECK_RANGED_INTEGER (first, 0, w->current_matrix->nrows); + end_row = MATRIX_ROW (w->current_matrix, XINT (last)); + } + else if (!NILP (body)) + end_row = MATRIX_BOTTOM_TEXT_ROW (w->current_matrix, w); + else + error ("Invalid specification of last line"); + + while (row <= end_row && row->enabled_p + && row->y + row->height < max_y) + { + + if (left_flag) + { + struct glyph *glyph = row->glyphs[TEXT_AREA]; + + rows = Fcons (Fcons (make_number + (invert + ? glyph->pixel_width + : window_width - glyph->pixel_width), + make_number (row->y + row->height - subtract)), + rows); + } + else + rows = Fcons (Fcons (make_number + (invert + ? window_width - row->pixel_width + : row->pixel_width), /* maybe add left + margin here */ + make_number (row->y + row->height - subtract)), + rows); + row++; + } + + return Fnreverse (rows); +} + DEFUN ("window-dedicated-p", Fwindow_dedicated_p, Swindow_dedicated_p, 0, 1, 0, doc: /* Return non-nil when WINDOW is dedicated to its buffer. @@ -7341,6 +7464,7 @@ Value is a list of the form (WIDTH COLUMNS VERTICAL-TYPE HEIGHT LINES DEFSYM (Qclone_of, "clone-of"); DEFSYM (Qfloor, "floor"); DEFSYM (Qceiling, "ceiling"); + DEFSYM (Qmark_for_redisplay, "mark-for-redisplay"); staticpro (&Vwindow_list); @@ -7584,6 +7708,7 @@ this value for parameters without read syntax (like windows or frames). defsubr (&Sset_window_point); defsubr (&Sset_window_start); defsubr (&Swindow_dedicated_p); + defsubr (&Swindow_lines_pixel_dimensions); defsubr (&Sset_window_dedicated_p); defsubr (&Swindow_display_table); defsubr (&Sset_window_display_table); diff --git a/src/xdisp.c b/src/xdisp.c index e59934d..4a754e4 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -11365,6 +11365,11 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0) 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; @@ -11427,7 +11432,14 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0) 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 @@ -11753,6 +11765,7 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0) && 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; } @@ -11869,6 +11882,7 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0) 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 @@ -11915,6 +11929,10 @@ static void ATTRIBUTE_FORMAT_PRINTF (1, 0) 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); @@ -14119,6 +14137,10 @@ static void debug_method_add (struct window *, char const *, ...) if (FRAME_GARBAGED_P (f) && FRAME_WINDOW_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 b5dbb53..ffe9cec 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -4474,6 +4474,10 @@ or lists of the form (RED GREEN BLUE). 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 @@ ALIST is an alist of (REGISTRY ALTERNATIVE1 ALTERNATIVE2 ...) entries. 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) @@ -6389,11 +6394,12 @@ ALIST is an alist of (REGISTRY ALTERNATIVE1 ALTERNATIVE2 ...) entries. 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 97aa923..2fa3ce2 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -90,6 +90,7 @@ #include #include #include +#include #endif #ifdef USE_LUCID @@ -117,6 +118,35 @@ 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 @@ struct x_display_info * 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 @@ struct x_display_info * 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 @@ struct x_display_info * #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,171 @@ struct x_display_info * 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); + } +} + #ifdef USE_GTK /* Set icon from FILE for frame F. By using GTK functions the icon @@ -1272,7 +1471,7 @@ struct mouse_cursor_data { 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)) @@ -1474,12 +1673,7 @@ struct mouse_cursor_data { if (FRAME_X_WINDOW (f) != 0) { 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 } } @@ -2803,6 +2997,25 @@ struct mouse_cursor_data { 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); @@ -2943,6 +3156,26 @@ struct mouse_cursor_data { 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 +3518,12 @@ struct mouse_cursor_data { Lisp_Object frame, tem; Lisp_Object name; bool minibuffer_only = false; + bool undecorated = 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 +3575,28 @@ struct mouse_cursor_data { 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); + XSETFRAME (frame, f); f->terminal = dpyinfo->terminal; @@ -3528,15 +3784,24 @@ struct mouse_cursor_data { 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 +3876,21 @@ struct mouse_cursor_data { 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)) @@ -3652,27 +3932,29 @@ struct mouse_cursor_data { adjust_frame_size call. */ x_default_parameter (f, parms, Qfullscreen, Qnil, "fullscreen", "Fullscreen", RES_TYPE_SYMBOL); + x_default_parameter (f, parms, 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.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 +3967,18 @@ struct mouse_cursor_data { 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); + /* Initialize `default-minibuffer-frame' in case this is the first frame on this terminal. */ if (FRAME_HAS_MINIBUF_P (f) @@ -3710,7 +3996,7 @@ struct mouse_cursor_data { and similar functions. */ Vwindow_list = Qnil; - return unbind_to (count, frame); + return unbind_to (count, frame); } @@ -4845,6 +5131,141 @@ value is a list of the form (LEFT, TOP, RIGHT, BOTTOM). All values are : Qnative_edges)); } +/** + * 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 +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 = nchildren - 1; i >= 0; 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. + +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 bottommost (first) to topmost (last). Child +frames appear right after their parent frame. Return nil if TERMINAL +contains no Emacs frame. */) + (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. @@ -6979,6 +7400,12 @@ FRAMES should be nil (the selected frame), a frame, or a list of 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, }; void @@ -7183,6 +7610,8 @@ FRAMES should be nil (the selected frame), a frame, or a list of 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/xterm.c b/src/xterm.c index 38229a5..bcf7107 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -714,7 +714,7 @@ struct record #endif } -static void +void x_fill_rectangle (struct frame *f, GC gc, int x, int y, int width, int height) { #ifdef USE_CAIRO @@ -945,11 +945,14 @@ struct x_display_info * 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 */ { @@ -1292,8 +1295,12 @@ struct x_display_info * 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) { @@ -1302,17 +1309,39 @@ struct x_display_info * 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 @@ -1348,10 +1377,25 @@ struct x_display_info * 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 (); } } @@ -3835,11 +3879,11 @@ Status x_parse_color (struct frame *f, const char *color_name, 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); @@ -4953,6 +4997,9 @@ struct frame * 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; @@ -4999,20 +5046,37 @@ struct frame * &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), @@ -5273,20 +5337,22 @@ static void x_send_scroll_bar_event (Lisp_Object, enum scroll_bar_part, 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 */ @@ -6485,10 +6551,14 @@ static void x_send_scroll_bar_event (Lisp_Object, enum scroll_bar_part, 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 (); @@ -7056,10 +7126,10 @@ static void x_send_scroll_bar_event (Lisp_Object, enum scroll_bar_part, /* 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 (); @@ -7828,8 +7898,21 @@ static void xembed_send_message (struct frame *f, Time, 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; @@ -7869,6 +7952,7 @@ static void xembed_send_message (struct frame *f, Time, event->xexpose.x, event->xexpose.y, event->xexpose.width, event->xexpose.height, 0); + x_clear_under_internal_border (f); #endif } @@ -7884,6 +7968,9 @@ static void xembed_send_message (struct frame *f, Time, #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)) @@ -7932,7 +8019,10 @@ static void xembed_send_message (struct frame *f, Time, 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 @@ -7989,7 +8079,16 @@ static void xembed_send_message (struct frame *f, Time, /* 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 */ + } SET_FRAME_VISIBLE (f, 1); SET_FRAME_ICONIFIED (f, false); @@ -8433,10 +8532,17 @@ static void xembed_send_message (struct frame *f, Time, #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 SELECT_WINDOW_EVENTs for + `mouse-autoselect-window' but don't let popup menus + interfere with this (Bug#1261). */ + if (!NILP (Vmouse_autoselect_window) + && !popup_activated () + && (f == dpyinfo->x_highlight_frame + /* Switch to f from another frame iff + focus_follows_mouse is set and f's + no_accept_focus parameter is nil. */ + || (!NILP (focus_follows_mouse) + && !FRAME_NO_FOCUS_ON_MAP (f)))) { static Lisp_Object last_mouse_window; Lisp_Object window = window_from_coordinates @@ -8447,13 +8553,7 @@ static void xembed_send_message (struct frame *f, Time, will be selected 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.ie.kind = SELECT_WINDOW_EVENT; inev.ie.frame_or_window = window; @@ -8601,7 +8701,34 @@ static void xembed_send_message (struct frame *f, Time, if (FRAME_GTK_OUTER_WIDGET (f) && gtk_widget_get_mapped (FRAME_GTK_OUTER_WIDGET (f))) #endif - x_real_positions (f, &f->left_pos, &f->top_pos); + { + int old_left = f->left_pos; + int old_top = f->top_pos; + Lisp_Object frame = Qnil; + + XSETFRAME (frame, f); + + 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) + { + inev.ie.kind = MOVE_FRAME_EVENT; + XSETFRAME (inev.ie.frame_or_window, f); + } + } + #ifdef HAVE_X_I18N if (FRAME_XIC (f) && (FRAME_XIC_STYLE (f) & XIMStatusArea)) @@ -8622,8 +8749,37 @@ static void xembed_send_message (struct frame *f, Time, 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_FOCUS_ON_MAP (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) + || (hf && FRAME_LIVE_P (hf) + && f == FRAME_PARENT_FRAME (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)) @@ -10061,7 +10217,7 @@ struct x_error_message_stack { } XMoveWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), - modified_left, modified_top); + modified_left, modified_top); x_sync_with_move (f, f->left_pos, f->top_pos, FRAME_DISPLAY_INFO (f)->wm_type == X_WMTYPE_UNKNOWN); @@ -10077,6 +10233,7 @@ struct x_error_message_stack { 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 @@ -10207,6 +10364,91 @@ struct x_error_message_stack { 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) +{ + if (!EQ (new_value, old_value)) + { + Lisp_Object frame; + struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); + + XSETFRAME (frame, f); + + if (NILP (new_value)) + { + if (FRAME_Z_GROUP_ABOVE (f)) + set_wm_state (frame, false, + dpyinfo->Xatom_net_wm_state_above, None); + else if (FRAME_Z_GROUP_BELOW (f)) + 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) && !FRAME_Z_GROUP_ABOVE (f)) + { + if (FRAME_Z_GROUP_BELOW (f)) + set_wm_state (frame, false, + dpyinfo->Xatom_net_wm_state_below, None); + set_wm_state (frame, true, + dpyinfo->Xatom_net_wm_state_above, None); + FRAME_Z_GROUP (f) = z_group_above; + } + else if (EQ (new_value, Qbelow) && !FRAME_Z_GROUP_BELOW (f)) + { + if (FRAME_Z_GROUP_ABOVE (f)) + 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 + 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. @@ -10999,6 +11241,26 @@ struct x_error_message_stack { 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); @@ -11067,9 +11329,10 @@ struct x_error_message_stack { 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) { @@ -11132,15 +11395,15 @@ struct x_error_message_stack { 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. @@ -11150,8 +11413,6 @@ struct x_error_message_stack { SET_FRAME_VISIBLE (f, 0); SET_FRAME_ICONIFIED (f, false); - x_sync (f); - unblock_input (); } @@ -12306,6 +12567,9 @@ struct x_display_info * 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 32c879b..3122a2b 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; @@ -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 @@ -1168,6 +1169,8 @@ extern bool x_alloc_lighter_color_for_widget (Widget, Display *, Colormap, } 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);