1778 lines
		
	
	
	
		
			74 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
	
	
			
		
		
	
	
			1778 lines
		
	
	
	
		
			74 KiB
		
	
	
	
		
			EmacsLisp
		
	
	
	
	
	
;;; exwm-cm.el --- Compositing Manager for EXWM  -*- lexical-binding: t -*-
 | 
						||
 | 
						||
;; Copyright (C) 2016 Free Software Foundation, Inc.
 | 
						||
 | 
						||
;; Author: Chris Feng <chris.w.feng@gmail.com>
 | 
						||
 | 
						||
;; This file is part of GNU Emacs.
 | 
						||
 | 
						||
;; GNU Emacs is free software: you can redistribute it and/or modify
 | 
						||
;; it under the terms of the GNU General Public License as published by
 | 
						||
;; the Free Software Foundation, either version 3 of the License, or
 | 
						||
;; (at your option) any later version.
 | 
						||
 | 
						||
;; GNU Emacs is distributed in the hope that it will be useful,
 | 
						||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						||
;; GNU General Public License for more details.
 | 
						||
 | 
						||
;; You should have received a copy of the GNU General Public License
 | 
						||
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 | 
						||
 | 
						||
;;; Commentary:
 | 
						||
 | 
						||
;; This module provides a compositing manager (CM) for EXWM, mainly to
 | 
						||
;; enable transparency support.
 | 
						||
 | 
						||
;; Usage:
 | 
						||
;; Add following lines to .emacs and modify accordingly:
 | 
						||
;;
 | 
						||
;; (require 'exwm-cm)
 | 
						||
;; ;; Make all Emacs frames opaque.
 | 
						||
;; (setq window-system-default-frame-alist '((x . ((alpha . 100)))))
 | 
						||
;; ;; Assign everything else a 80% opacity.
 | 
						||
;; (setq exwm-cm-opacity 80)
 | 
						||
;; (exwm-cm-enable)
 | 
						||
;;
 | 
						||
;; With the last line this CM would be started with EXWM.  You can also
 | 
						||
;; start and stop this CM with `exwm-cm-start' and `exwm-cm-stop' at any
 | 
						||
;; time.
 | 
						||
 | 
						||
;; Theory:
 | 
						||
;; Due to its unique way of managing X windows, EXWM can not work with
 | 
						||
;; any existing CMs.  And this CM, designed specifically for EXWM,
 | 
						||
;; probably won't work well with other WMs, too.  The theories behind
 | 
						||
;; all CMs are basically the same, some peculiarities of this CM are
 | 
						||
;; summarized as the following sections.
 | 
						||
 | 
						||
;; + Data structures:
 | 
						||
;;   This CM organizes all X windows concerned with compositing in a
 | 
						||
;;   tree hierarchy.  Below is a stripped-down version of such tree with
 | 
						||
;;   each node representing an X window (except the root placeholder),
 | 
						||
;;
 | 
						||
;;   (nil
 | 
						||
;;    (root-xwin
 | 
						||
;;     (unmanaged-xwin)
 | 
						||
;;     (workspace-container
 | 
						||
;;      (unmanaged-xwin)
 | 
						||
;;      (xwin-container
 | 
						||
;;       (xwin)
 | 
						||
;;       (floating-frame-container
 | 
						||
;;        (floating-frame)))
 | 
						||
;;      (xwin-container
 | 
						||
;;       (xwin))
 | 
						||
;;      (workspace-frame-container
 | 
						||
;;       (workspace-frame)))
 | 
						||
;;     (minibuffer-frame-container
 | 
						||
;;      (minibuffer-frame))))
 | 
						||
;;
 | 
						||
;;   where
 | 
						||
;;   - nodes with non-nil CDRs are containers,
 | 
						||
;;   - siblings are arranged in stacking order (top to bottom),
 | 
						||
;;   - and "managed" and "unmanaged" are in WM's sense.
 | 
						||
;;
 | 
						||
;;   During a painting process, the tree is traversed starting from the
 | 
						||
;;   root node, with each leaf visited and painted.  The attributes of
 | 
						||
;;   each X window (position, size, etc) are recorded as an instance of
 | 
						||
;;   class `exwm-cm--attr'.  Such instance is associated with the
 | 
						||
;;   corresponding X window ID through a hash table.  The instance also
 | 
						||
;;   contains a slot pointing to a subtree of the aforementioned tree,
 | 
						||
;;   with the root node being the parent of the X window.  This makes it
 | 
						||
;;   convenient to carry out operations such as insertion, deletion,
 | 
						||
;;   restacking and reparenting.
 | 
						||
 | 
						||
;; + Compositing strategies:
 | 
						||
;;   - Only leaves are painted, since branches (containers) are always
 | 
						||
;;     invisible.
 | 
						||
;;   - The root X window is painted separately.
 | 
						||
;;   - Siblings below a workspace frame container are not painted; they
 | 
						||
;;     are considered hidden.
 | 
						||
;;   - Only the top workspace in one (RandR) output is painted.
 | 
						||
;;   - Workspace frames and floating frames are always clipped by its
 | 
						||
;;     Emacs windows displaying `exwm-mode' buffers, therefore they
 | 
						||
;;     don't block X windows.
 | 
						||
 | 
						||
;; Reference:
 | 
						||
;; + xcompmgr (http://cgit.freedesktop.org/xorg/app/xcompmgr/)
 | 
						||
 | 
						||
;;; Code:
 | 
						||
 | 
						||
(require 'xcb-composite)
 | 
						||
(require 'xcb-damage)
 | 
						||
(require 'xcb-ewmh)
 | 
						||
(require 'xcb-icccm)
 | 
						||
(require 'xcb-renderutil)
 | 
						||
(require 'xcb-shape)
 | 
						||
 | 
						||
(require 'exwm-core)
 | 
						||
(require 'exwm-workspace)
 | 
						||
(require 'exwm-manage)
 | 
						||
 | 
						||
(defconst exwm-cm--OPAQUE (float #xFFFFFFFF)
 | 
						||
  "The opacity value of the _NET_WM_WINDOW_OPACITY property.")
 | 
						||
(defvar exwm-cm--_NET_WM_WINDOW_OPACITY nil "The _NET_WM_WINDOW_OPACITY atom.")
 | 
						||
(defvar exwm-cm-opacity nil
 | 
						||
  "The default value of opacity when it's not explicitly specified.
 | 
						||
 | 
						||
The value should be a floating number between 0 (transparent) and 100
 | 
						||
(opaque).  A value of nil also means opaque.")
 | 
						||
 | 
						||
(defvar exwm-cm--hash nil
 | 
						||
  "The hash table associating X window IDs to their attributes.")
 | 
						||
 | 
						||
(defvar exwm-cm--conn nil "The X connection used by the CM.")
 | 
						||
(defvar exwm-cm--buffer nil "The rendering buffer.")
 | 
						||
(defvar exwm-cm--depth nil "Default depth.")
 | 
						||
(defvar exwm-cm--clip-changed t "Whether clip has changed.")
 | 
						||
(defvar exwm-cm--damages nil "All damaged regions.")
 | 
						||
(defvar exwm-cm--expose-rectangles nil
 | 
						||
  "Used by Expose event handler to collect exposed regions.")
 | 
						||
 | 
						||
(defvar exwm-cm--background nil "The background (render) picture.")
 | 
						||
(defvar exwm-cm--background-atom-names '("_XROOTPMAP_ID" "_XSETROOT_ID")
 | 
						||
  "Property names for background pixmap.")
 | 
						||
(defvar exwm-cm--background-atoms nil "Interned atoms of the property names.")
 | 
						||
 | 
						||
(defun exwm-cm--get-opacity (xwin)
 | 
						||
  "Get the opacity of X window XWIN.
 | 
						||
 | 
						||
The value is between 0 (fully transparent) to #xFFFFFFFF (opaque)."
 | 
						||
  (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                   (make-instance 'xcb:icccm:-GetProperty-single
 | 
						||
                                  :window xwin
 | 
						||
                                  :property exwm-cm--_NET_WM_WINDOW_OPACITY
 | 
						||
                                  :type xcb:Atom:CARDINAL))))
 | 
						||
    ;; The X window might have already been destroyed.
 | 
						||
    (when reply
 | 
						||
      (slot-value reply 'value))))
 | 
						||
 | 
						||
(defun exwm-cm-set-opacity (xwin opacity)
 | 
						||
  "Set the opacity of X window XWIN to OPACITY.
 | 
						||
 | 
						||
The value is between 0 (fully transparent) to 100 (opaque).
 | 
						||
 | 
						||
If called interactively, XWIN would be the selected X window."
 | 
						||
  (interactive
 | 
						||
   (list (exwm--buffer->id (window-buffer))
 | 
						||
         (read-number "Opacity (0 ~ 100): " 100)))
 | 
						||
  (when (and xwin
 | 
						||
             (<= 0 opacity 100))
 | 
						||
    (setq opacity (round (* exwm-cm--OPAQUE (/ opacity 100.0))))
 | 
						||
    (xcb:+request exwm-cm--conn
 | 
						||
        (make-instance 'xcb:icccm:-ChangeProperty-single
 | 
						||
                       :window xwin
 | 
						||
                       :property exwm-cm--_NET_WM_WINDOW_OPACITY
 | 
						||
                       :type xcb:Atom:CARDINAL
 | 
						||
                       :data opacity))
 | 
						||
    (xcb:flush exwm-cm--conn)))
 | 
						||
 | 
						||
(defclass exwm-cm--attr ()
 | 
						||
  (
 | 
						||
   ;; The entity associated with this X window; can be a frame, a buffer
 | 
						||
   ;; or nil.
 | 
						||
   (entity :initform nil)
 | 
						||
   ;; The subtree of which the root node is the parent of this X window.
 | 
						||
   (tree :initarg :tree)
 | 
						||
   ;; Geometry.
 | 
						||
   (x :initarg :x)
 | 
						||
   (y :initarg :y)
 | 
						||
   (width :initarg :width)
 | 
						||
   (height :initarg :height)
 | 
						||
   ;; X window attributes.
 | 
						||
   (visual :initarg :visual)
 | 
						||
   (class :initarg :class)
 | 
						||
   ;; The opacity of this X window; can be 0 ~ #xFFFE or nil.
 | 
						||
   (opacity :initform nil)
 | 
						||
   ;; Determine whether this X window should be treated as opaque or
 | 
						||
   ;; transparent; can be nil (opaque), 'argb or 'transparent (both
 | 
						||
   ;; should be treated as transparent).
 | 
						||
   (mode :initform nil)
 | 
						||
   ;; The (render) picture of this X window.
 | 
						||
   (picture :initform nil)
 | 
						||
   ;; The 1x1 (render) picture with only alpha channel.
 | 
						||
   (alpha-picture :initform nil)
 | 
						||
   ;; Whether this X window is ever damaged.
 | 
						||
   (damaged :initform nil)
 | 
						||
   ;; The damage object monitoring this X window.
 | 
						||
   (damage :initarg :damage)
 | 
						||
   ;; The bounding region of this X window (can be irregular).
 | 
						||
   (border-size :initform nil)
 | 
						||
   ;; The rectangular bounding region of this X window.
 | 
						||
   (extents :initform nil)
 | 
						||
   ;; The region require repainting (used for transparent X windows).
 | 
						||
   (border-clip :initform nil)
 | 
						||
   ;; Shape-related parameters.
 | 
						||
   (shaped :initform nil)
 | 
						||
   (shape-x :initarg :shape-x)
 | 
						||
   (shape-y :initarg :shape-y)
 | 
						||
   (shape-width :initarg :shape-width)
 | 
						||
   (shape-height :initarg :shape-height))
 | 
						||
  :documentation "Attributes of an X window.")
 | 
						||
 | 
						||
(defsubst exwm-cm--xwin->attr (xwin)
 | 
						||
  "Get the attributes of X window XWIN."
 | 
						||
  (gethash xwin exwm-cm--hash))
 | 
						||
 | 
						||
(defsubst exwm-cm--get-tree (xwin)
 | 
						||
  "Get the subtree of the parent of X window XWIN."
 | 
						||
  (slot-value (exwm-cm--xwin->attr xwin) 'tree))
 | 
						||
 | 
						||
(defsubst exwm-cm--set-tree (xwin tree)
 | 
						||
  "Reparent X window XWIN to another tree TREE."
 | 
						||
  (setf (slot-value (exwm-cm--xwin->attr xwin) 'tree) tree))
 | 
						||
 | 
						||
(defsubst exwm-cm--get-parent (xwin)
 | 
						||
  "Get the parent of X window XWIN."
 | 
						||
  (car (exwm-cm--get-tree xwin)))
 | 
						||
 | 
						||
(defsubst exwm-cm--get-siblings (xwin)
 | 
						||
  "Get a list of subtrees of the siblings of X window XWIN"
 | 
						||
  (cdr (exwm-cm--get-tree xwin)))
 | 
						||
 | 
						||
(defsubst exwm-cm--get-subtree (xwin)
 | 
						||
  "Get the subtree of which the X window XWIN is the root node."
 | 
						||
  (assq xwin (exwm-cm--get-siblings xwin)))
 | 
						||
 | 
						||
(defun exwm-cm--create-attr (xwin tree x y width height)
 | 
						||
  "Create attributes for X window XWIN.
 | 
						||
 | 
						||
TREE is the subtree and the parent of this X window is the tree's root.
 | 
						||
X and Y specify the position with regard to the root X window.  WIDTH
 | 
						||
and HEIGHT specify the size of the X window."
 | 
						||
  (let (visual class map-state damage attr)
 | 
						||
    (cond
 | 
						||
     ((= xwin exwm--root)
 | 
						||
      ;; Redirect all subwindows to off-screen storage.
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:composite:RedirectSubwindows
 | 
						||
                         :window exwm--root
 | 
						||
                         :update xcb:composite:Redirect:Manual))
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:ChangeWindowAttributes
 | 
						||
                         :window xwin
 | 
						||
                         :value-mask xcb:CW:EventMask
 | 
						||
                         :event-mask (logior xcb:EventMask:StructureNotify
 | 
						||
                                             xcb:EventMask:PropertyChange
 | 
						||
                                             xcb:EventMask:SubstructureNotify
 | 
						||
                                             xcb:EventMask:Exposure)))
 | 
						||
      (setq visual (slot-value (car (slot-value (xcb:get-setup exwm-cm--conn)
 | 
						||
                                                'roots))
 | 
						||
                               'root-visual)
 | 
						||
            class xcb:WindowClass:InputOutput))
 | 
						||
     ((eq xwin exwm-manage--desktop)
 | 
						||
      ;; Ignore any desktop; paint the background ourselves.
 | 
						||
      (setq visual 0
 | 
						||
            class xcb:WindowClass:InputOnly
 | 
						||
            map-state xcb:MapState:Unmapped))
 | 
						||
     (t
 | 
						||
      ;; Redirect this window to off-screen storage, or the content
 | 
						||
      ;; would be mirrored to its parent.
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:composite:RedirectWindow
 | 
						||
                         :window xwin
 | 
						||
                         :update xcb:composite:Redirect:Manual))
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:ChangeWindowAttributes
 | 
						||
                         :window xwin
 | 
						||
                         :value-mask xcb:CW:EventMask
 | 
						||
                         :event-mask (logior xcb:EventMask:StructureNotify
 | 
						||
                                             xcb:EventMask:PropertyChange)))
 | 
						||
      (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                       (make-instance 'xcb:GetWindowAttributes
 | 
						||
                                      :window xwin))))
 | 
						||
        (if reply
 | 
						||
            (with-slots ((visual* visual)
 | 
						||
                         (class* class)
 | 
						||
                         (map-state* map-state))
 | 
						||
                reply
 | 
						||
              (setq visual visual*
 | 
						||
                    class class*
 | 
						||
                    map-state map-state*))
 | 
						||
          ;; The X window has been destroyed actually.  It'll get
 | 
						||
          ;; removed by a DestroyNotify event.
 | 
						||
          (setq visual 0
 | 
						||
                class xcb:WindowClass:InputOnly
 | 
						||
                map-state xcb:MapState:Unmapped)))
 | 
						||
      (when (/= class xcb:WindowClass:InputOnly)
 | 
						||
        (setq damage (xcb:generate-id exwm-cm--conn))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:damage:Create
 | 
						||
                           :damage damage
 | 
						||
                           :drawable xwin
 | 
						||
                           :level xcb:damage:ReportLevel:NonEmpty))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:shape:SelectInput
 | 
						||
                           :destination-window xwin
 | 
						||
                           :enable 1)))))
 | 
						||
    (setq attr (make-instance 'exwm-cm--attr
 | 
						||
                              :tree tree
 | 
						||
                              :x x
 | 
						||
                              :y y
 | 
						||
                              :width width
 | 
						||
                              :height height
 | 
						||
                              :visual visual
 | 
						||
                              :class class
 | 
						||
                              :damage damage
 | 
						||
                              :shape-x x
 | 
						||
                              :shape-y y
 | 
						||
                              :shape-width width
 | 
						||
                              :shape-height height))
 | 
						||
    (puthash xwin attr exwm-cm--hash)
 | 
						||
    (unless (or (= xwin exwm--root)
 | 
						||
                (= class xcb:WindowClass:InputOnly))
 | 
						||
      (exwm-cm--update-opacity xwin)
 | 
						||
      (when (= map-state xcb:MapState:Viewable)
 | 
						||
        (exwm-cm--map-xwin xwin t)))))
 | 
						||
 | 
						||
(defun exwm-cm--update-geometry (xwin x y width height &optional above-sibling)
 | 
						||
  "Update the geometry of X window XWIN.
 | 
						||
 | 
						||
X, Y, WIDTH and HEIGHT have the same meaning with the arguments used in
 | 
						||
`exwm-cm--create-attr'.  If ABOVE-SIBLING is non-nil, restack XWIN with
 | 
						||
`exwm-cm--restack.'"
 | 
						||
  (with-slots ((x* x)
 | 
						||
               (y* y)
 | 
						||
               (width* width)
 | 
						||
               (height* height)
 | 
						||
               extents shaped shape-x shape-y shape-width shape-height)
 | 
						||
      (exwm-cm--xwin->attr xwin)
 | 
						||
    (let ((stack-changed (and above-sibling
 | 
						||
                              (exwm-cm--restack xwin above-sibling)))
 | 
						||
          (position-changed (or (and x (/= x x*))
 | 
						||
                                (and y (/= y y*))))
 | 
						||
          (size-changed (or (and width (/= width width*))
 | 
						||
                            (and height (/= height height*))))
 | 
						||
          subtree dx dy damage new-extents)
 | 
						||
      (when position-changed
 | 
						||
        (setq subtree (exwm-cm--get-subtree xwin)
 | 
						||
              dx (- x x*)
 | 
						||
              dy (- y y*))
 | 
						||
        (dolist (node (cdr subtree))
 | 
						||
          (with-slots (x y) (exwm-cm--xwin->attr (car node))
 | 
						||
            (exwm--log "(CM) #x%X(*): @%+d%+d => @%+d%+d"
 | 
						||
                       (car node) x y (+ x dx) (+ y dy))
 | 
						||
            (exwm-cm--update-geometry (car node) (+ x dx) (+ y dy) nil nil)))
 | 
						||
        (exwm--log "(CM) #x%X: @%+d%+d => @%+d%+d" xwin x* y* x y)
 | 
						||
        (setf x* x
 | 
						||
              y* y)
 | 
						||
        (cl-incf shape-x dx)
 | 
						||
        (cl-incf shape-y dy))
 | 
						||
      (when size-changed
 | 
						||
        (setf width* width
 | 
						||
              height* height)
 | 
						||
        (unless shaped
 | 
						||
          (setf shape-width width
 | 
						||
                shape-height height)))
 | 
						||
      (when (or stack-changed position-changed size-changed)
 | 
						||
        (setq damage (xcb:generate-id exwm-cm--conn)
 | 
						||
              new-extents (xcb:generate-id exwm-cm--conn))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:CreateRegion
 | 
						||
                           :region damage
 | 
						||
                           :rectangles nil))
 | 
						||
        (when extents
 | 
						||
          (xcb:+request exwm-cm--conn
 | 
						||
              (make-instance 'xcb:xfixes:CopyRegion
 | 
						||
                             :source extents
 | 
						||
                             :destination damage)))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:CreateRegion
 | 
						||
                           :region new-extents
 | 
						||
                           :rectangles (list (make-instance 'xcb:RECTANGLE
 | 
						||
                                                            :x x*
 | 
						||
                                                            :y y*
 | 
						||
                                                            :width width*
 | 
						||
                                                            :height height*))))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:UnionRegion
 | 
						||
                           :source1 damage
 | 
						||
                           :source2 new-extents
 | 
						||
                           :destination damage))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:DestroyRegion
 | 
						||
                           :region new-extents))
 | 
						||
        (exwm-cm--add-damage damage)))))
 | 
						||
 | 
						||
(defun exwm-cm--update-opacity (xwin)
 | 
						||
  "Update the opacity of X window XWIN."
 | 
						||
  (with-slots (visual opacity mode alpha-picture extents)
 | 
						||
      (exwm-cm--xwin->attr xwin)
 | 
						||
    (let (format forminfo)
 | 
						||
      ;; Get the opacity.
 | 
						||
      (setf opacity (exwm-cm--get-opacity xwin))
 | 
						||
      (if opacity
 | 
						||
          (setf opacity (round (* #xFFFF (/ opacity exwm-cm--OPAQUE))))
 | 
						||
        (when (numberp exwm-cm-opacity)
 | 
						||
          (setf opacity (round (* #xFFFF (/ exwm-cm-opacity 100.0))))))
 | 
						||
      (when (and opacity
 | 
						||
                 (>= opacity #xFFFF))
 | 
						||
        (setf opacity nil))
 | 
						||
      ;; Determine the mode of the X window.
 | 
						||
      (setq format (xcb:renderutil:find-visual-format
 | 
						||
                    (xcb:renderutil:query-formats exwm-cm--conn) visual))
 | 
						||
      (when format
 | 
						||
        (catch 'break
 | 
						||
          (dolist (f (slot-value (xcb:renderutil:query-formats exwm-cm--conn)
 | 
						||
                                 'formats))
 | 
						||
            (when (eq format (slot-value f 'id))
 | 
						||
              (setq forminfo f)
 | 
						||
              (throw 'break nil)))))
 | 
						||
      (if (and forminfo
 | 
						||
               (eq xcb:render:PictType:Direct (slot-value forminfo 'type))
 | 
						||
               (/= 0 (slot-value (slot-value forminfo 'direct) 'alpha-mask)))
 | 
						||
          (setf mode 'argb)
 | 
						||
        (if opacity
 | 
						||
            (setf mode 'transparent)
 | 
						||
          (setf mode nil)))
 | 
						||
      ;; Clear resources.
 | 
						||
      (when alpha-picture
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:render:FreePicture
 | 
						||
                           :picture alpha-picture))
 | 
						||
        (setf alpha-picture nil))
 | 
						||
      (when extents
 | 
						||
        (let ((damage (xcb:generate-id exwm-cm--conn)))
 | 
						||
          (xcb:+request exwm-cm--conn
 | 
						||
              (make-instance 'xcb:xfixes:CreateRegion
 | 
						||
                             :region damage
 | 
						||
                             :rectangles nil))
 | 
						||
          (xcb:+request exwm-cm--conn
 | 
						||
              (make-instance 'xcb:xfixes:CopyRegion
 | 
						||
                             :source extents
 | 
						||
                             :destination damage))
 | 
						||
          (exwm-cm--add-damage damage))))))
 | 
						||
 | 
						||
(defsubst exwm-cm--push (newelt place)
 | 
						||
  "Similar to `push' but preserve the reference."
 | 
						||
  (let ((oldelt (car place)))
 | 
						||
    (setf (car place) newelt
 | 
						||
          (cdr place) (cons oldelt (cdr place)))))
 | 
						||
 | 
						||
(defsubst exwm-cm--delq (elt list)
 | 
						||
  "Similar to `delq' but preserve the reference."
 | 
						||
  (if (eq elt (car list))
 | 
						||
      (setf (car list) (cadr list)
 | 
						||
            (cdr list) (cddr list))
 | 
						||
    (delq elt list)))
 | 
						||
 | 
						||
(defsubst exwm-cm--assq-delete-all (key alist)
 | 
						||
  "Similar to `assq-delete-all' but preserve the reference."
 | 
						||
  (when (eq key (caar alist))
 | 
						||
    (setf (car alist) (cadr alist)
 | 
						||
          (cdr alist) (cddr alist)))
 | 
						||
  (assq-delete-all key alist))
 | 
						||
 | 
						||
(defun exwm-cm--create-tree (&optional xwin)
 | 
						||
  "Create a tree with XWIN being the root node."
 | 
						||
  (let (tree0 x0 y0 children containers)
 | 
						||
    ;; Get the position of this branch.
 | 
						||
    (if xwin
 | 
						||
        (with-slots (tree x y) (exwm-cm--xwin->attr xwin)
 | 
						||
          (setq tree0 (assq xwin (cdr tree))
 | 
						||
                x0 x
 | 
						||
                y0 y))
 | 
						||
      (setq tree0 (list nil)
 | 
						||
            x0 0
 | 
						||
            y0 0))
 | 
						||
    ;; Get children nodes.
 | 
						||
    (if (null xwin)
 | 
						||
        (setq children (list exwm--root))
 | 
						||
      (setq children
 | 
						||
            (reverse (slot-value (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                                     (make-instance 'xcb:QueryTree
 | 
						||
                                                    :window xwin))
 | 
						||
                                 'children))))
 | 
						||
    ;; Get container nodes.
 | 
						||
    ;; Floating frame containers are determined dynamically.
 | 
						||
    (cond
 | 
						||
     ((null xwin)
 | 
						||
      (setq containers `((,exwm--root))))
 | 
						||
     ((= xwin exwm--root)
 | 
						||
      ;; Workspace containers and the minibuffer frame container.
 | 
						||
      (setq containers (mapcar (lambda (f)
 | 
						||
                                 (cons (frame-parameter f 'exwm-workspace) f))
 | 
						||
                               exwm-workspace--list))
 | 
						||
      (when (exwm-workspace--minibuffer-own-frame-p)
 | 
						||
        (push (cons
 | 
						||
               (frame-parameter exwm-workspace--minibuffer 'exwm-container)
 | 
						||
               exwm-workspace--minibuffer)
 | 
						||
              containers)))
 | 
						||
     ;; No containers in the minibuffer container.
 | 
						||
     ((and (exwm-workspace--minibuffer-own-frame-p)
 | 
						||
           (= xwin
 | 
						||
              (frame-parameter exwm-workspace--minibuffer 'exwm-container))))
 | 
						||
     ((= exwm--root
 | 
						||
         (slot-value (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                         (make-instance 'xcb:QueryTree
 | 
						||
                                        :window xwin))
 | 
						||
                     'parent))
 | 
						||
      ;; Managed X window containers and the workspace frame container.
 | 
						||
      (let (frame)
 | 
						||
        (catch 'break
 | 
						||
          (dolist (f exwm-workspace--list)
 | 
						||
            (when (= xwin (frame-parameter f 'exwm-workspace))
 | 
						||
              (setq frame f)
 | 
						||
              (throw 'break nil))))
 | 
						||
        (cl-assert frame)
 | 
						||
        (dolist (pair exwm--id-buffer-alist)
 | 
						||
          (with-current-buffer (cdr pair)
 | 
						||
            (when (eq frame exwm--frame)
 | 
						||
              (push (cons exwm--container (cdr pair)) containers))))
 | 
						||
        (push (cons (frame-parameter frame 'exwm-container) frame)
 | 
						||
              containers))))
 | 
						||
    ;; Create subnodes.
 | 
						||
    (dolist (xwin children)
 | 
						||
      ;; Create attributes.
 | 
						||
      (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                       (make-instance 'xcb:GetGeometry
 | 
						||
                                      :drawable xwin))))
 | 
						||
        ;; It's possible the X window has been destroyed.
 | 
						||
        (if (null reply)
 | 
						||
            (setq xwin nil)
 | 
						||
          (when reply
 | 
						||
            (with-slots (x y width height) reply
 | 
						||
              (exwm-cm--create-attr xwin tree0
 | 
						||
                                    (+ x x0) (+ y y0) width height))
 | 
						||
            ;; Insert the node.
 | 
						||
            (setcdr (or (last (cdr tree0)) tree0) `((,xwin))))))
 | 
						||
      (cond
 | 
						||
       ((null xwin))
 | 
						||
       ((assq xwin containers)
 | 
						||
        ;; A branch.  Repeat the process.
 | 
						||
        (exwm-cm--create-tree xwin)
 | 
						||
        (let ((entity (cdr (assq xwin containers)))
 | 
						||
              entity-xwin)
 | 
						||
          (when entity
 | 
						||
            (setq entity-xwin (if (framep entity)
 | 
						||
                                  (frame-parameter entity 'exwm-outer-id)
 | 
						||
                                (buffer-local-value 'exwm--id entity)))
 | 
						||
            (setf (slot-value (exwm-cm--xwin->attr entity-xwin) 'entity) entity
 | 
						||
                  (slot-value (exwm-cm--xwin->attr xwin) 'entity) entity)
 | 
						||
            (let ((tmp (exwm-cm--get-parent entity-xwin)))
 | 
						||
              (when (/= xwin tmp)
 | 
						||
                ;; Workspace frame container.
 | 
						||
                (setf (slot-value (exwm-cm--xwin->attr tmp) 'entity)
 | 
						||
                      entity))))))
 | 
						||
       ((and (null containers)
 | 
						||
             (exwm--id->buffer xwin))
 | 
						||
        ;; A leaf but a floating frame container might follow.
 | 
						||
        (with-current-buffer (exwm--id->buffer xwin)
 | 
						||
          (when exwm--floating-frame
 | 
						||
            (push (cons (frame-parameter exwm--floating-frame 'exwm-container)
 | 
						||
                        exwm--floating-frame)
 | 
						||
                  containers))))))))
 | 
						||
 | 
						||
(defun exwm-cm--restack (xwin above-sibling)
 | 
						||
  "Restack X window XWIN so as to it's exactly on top of ABOVE-SIBLING."
 | 
						||
  (let ((siblings (exwm-cm--get-siblings xwin))
 | 
						||
        node tmp)
 | 
						||
    (unless (= 1 (length siblings))
 | 
						||
      (setq node (assq xwin siblings))
 | 
						||
      (if (= above-sibling xcb:Window:None)
 | 
						||
          ;; Put at bottom.
 | 
						||
          (unless (eq node (cdr (last siblings)))
 | 
						||
            (exwm-cm--delq node siblings)
 | 
						||
            (setcdr (last siblings) (list node))
 | 
						||
            ;; Set the return value.
 | 
						||
            t)
 | 
						||
        ;; Insert before the sibling.
 | 
						||
        (setq tmp siblings)
 | 
						||
        (while (and tmp
 | 
						||
                    (/= above-sibling (caar tmp)))
 | 
						||
          (setq tmp (cdr tmp)))
 | 
						||
        (cl-assert tmp)
 | 
						||
        ;; Check if it's already at the requested position.
 | 
						||
        (unless (eq tmp (cdr siblings))
 | 
						||
          (exwm-cm--delq node siblings)
 | 
						||
          (exwm-cm--push node tmp)
 | 
						||
          ;; Set the return value.
 | 
						||
          t)))))
 | 
						||
 | 
						||
(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
 | 
						||
 | 
						||
(defun exwm-cm--paint-tree (tree region &optional force-opaque frame-clip)
 | 
						||
  "Paint the tree TREE, with REGION specifying the clipping region.
 | 
						||
 | 
						||
If FORCE-OPAQUE is non-nil, all X windows painted in this tree is
 | 
						||
assumed opaque.  FRAME-CLIP specifies the region should be clipped when
 | 
						||
painting a frame."
 | 
						||
  (unless tree
 | 
						||
    (setq tree (exwm-cm--get-tree exwm--root)))
 | 
						||
  (let ((root (car tree))
 | 
						||
        xwin attr entity current output outputs queue rectangles)
 | 
						||
    ;; Paint subtrees.
 | 
						||
    (catch 'break
 | 
						||
      (dolist (subtree (cdr tree))
 | 
						||
        (setq xwin (car subtree)
 | 
						||
              attr (exwm-cm--xwin->attr xwin))
 | 
						||
        (cond
 | 
						||
         ;; Skip destroyed X windows.
 | 
						||
         ((null attr))
 | 
						||
         ;; Skip InputOnly X windows.
 | 
						||
         ((= xcb:WindowClass:InputOnly
 | 
						||
             (slot-value attr 'class)))
 | 
						||
         ((and (eq root exwm--root)
 | 
						||
               (frame-live-p (setq entity (slot-value attr 'entity)))
 | 
						||
               (if (eq entity exwm-workspace--minibuffer)
 | 
						||
                   ;; Skip the minibuffer if the current workspace is
 | 
						||
                   ;; already painted.
 | 
						||
                   (unless (exwm-workspace--minibuffer-attached-p)
 | 
						||
                     current)
 | 
						||
                 ;; Skip lower workspaces on visited RandR output.
 | 
						||
                 ;; If RandR is not enabled, it'll just paint the first.
 | 
						||
                 (memq (setq output (frame-parameter entity
 | 
						||
                                                     'exwm-randr-output))
 | 
						||
                       outputs))))
 | 
						||
         ((cdr subtree)
 | 
						||
          ;; Paint the subtree.
 | 
						||
          (setq entity (slot-value attr 'entity))
 | 
						||
          (let (fullscreen clip)
 | 
						||
            (cond
 | 
						||
             ((buffer-live-p entity)
 | 
						||
              (with-current-buffer entity
 | 
						||
                ;; Collect frame clip but exclude fullscreen and
 | 
						||
                ;; floating X windows.
 | 
						||
                (setq fullscreen (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN
 | 
						||
                                       exwm--ewmh-state))
 | 
						||
                (when (and (null fullscreen)
 | 
						||
                           ;; In case it's hidden.
 | 
						||
                           (null (exwm-layout--iconic-state-p))
 | 
						||
                           ;; The buffer of a floating X windows is not
 | 
						||
                           ;; displayed on a workspace frame.
 | 
						||
                           (null exwm--floating-frame)
 | 
						||
                           ;; Opaque regions are always clipped.
 | 
						||
                           (slot-value (exwm-cm--xwin->attr xwin) 'mode))
 | 
						||
                  ;; Prepare rectangles to clip the workspace frame.
 | 
						||
                  (with-slots (x y width height) (exwm-cm--xwin->attr xwin)
 | 
						||
                    (push (make-instance 'xcb:RECTANGLE
 | 
						||
                                         :x x
 | 
						||
                                         :y y
 | 
						||
                                         :width width
 | 
						||
                                         :height height)
 | 
						||
                          rectangles)))))
 | 
						||
             ((and rectangles
 | 
						||
                   (frame-live-p entity))
 | 
						||
              ;; Prepare region to clip the frame.
 | 
						||
              (setq clip (xcb:generate-id exwm-cm--conn))
 | 
						||
              (xcb:+request exwm-cm--conn
 | 
						||
                  (make-instance 'xcb:xfixes:CreateRegion
 | 
						||
                                 :region clip
 | 
						||
                                 :rectangles rectangles))))
 | 
						||
            (setq queue
 | 
						||
                  (nconc (exwm-cm--paint-tree subtree region fullscreen clip)
 | 
						||
                         queue))
 | 
						||
            (when fullscreen
 | 
						||
              ;; Fullscreen X windows are always opaque thus occludes
 | 
						||
              ;; anything in this workspace.
 | 
						||
              (throw 'break 'fullscreen))
 | 
						||
            (when clip
 | 
						||
              (xcb:+request exwm-cm--conn
 | 
						||
                  (make-instance 'xcb:xfixes:DestroyRegion
 | 
						||
                                 :region clip))))
 | 
						||
          (if (not (eq root exwm--root))
 | 
						||
              ;; Avoid painting any siblings below the workspace frame
 | 
						||
              ;; container.
 | 
						||
              (when (exwm-workspace--workspace-p (slot-value attr 'entity))
 | 
						||
                (throw 'break nil))
 | 
						||
            ;; Save some status.
 | 
						||
            (when (and (frame-live-p entity)
 | 
						||
                       (not (eq entity exwm-workspace--minibuffer)))
 | 
						||
              (push output outputs)
 | 
						||
              (when (eq entity exwm-workspace--current)
 | 
						||
                (setq current t)))))
 | 
						||
         ((and force-opaque
 | 
						||
               (slot-value attr 'damaged))
 | 
						||
          (exwm-cm--paint-opaque xwin region t))
 | 
						||
         ((slot-value attr 'damaged)
 | 
						||
          ;; Paint damaged leaf.
 | 
						||
          (setq entity (slot-value attr 'entity))
 | 
						||
          (when (slot-value attr 'mode)
 | 
						||
            (push xwin queue))
 | 
						||
          (cond
 | 
						||
           ((buffer-live-p entity)
 | 
						||
            (with-current-buffer entity
 | 
						||
              (cl-assert (= xwin exwm--id))
 | 
						||
              (when (and exwm--floating-frame
 | 
						||
                         ;; Opaque regions are always clipped.
 | 
						||
                         (slot-value (exwm-cm--xwin->attr xwin) 'mode))
 | 
						||
                ;; Prepare rectangles to clip the floating frame.
 | 
						||
                (with-slots (x y width height) (exwm-cm--xwin->attr xwin)
 | 
						||
                  (push (make-instance 'xcb:RECTANGLE
 | 
						||
                                       :x x
 | 
						||
                                       :y y
 | 
						||
                                       :width width
 | 
						||
                                       :height height)
 | 
						||
                        rectangles)))))
 | 
						||
           ((and frame-clip
 | 
						||
                 (frame-live-p entity))
 | 
						||
            ;; Apply frame clip.
 | 
						||
            (xcb:+request exwm-cm--conn
 | 
						||
                (make-instance 'xcb:xfixes:IntersectRegion
 | 
						||
                               :source1 region
 | 
						||
                               :source2 frame-clip
 | 
						||
                               :destination frame-clip))
 | 
						||
            (xcb:+request exwm-cm--conn
 | 
						||
                (make-instance 'xcb:xfixes:SubtractRegion
 | 
						||
                               :source1 region
 | 
						||
                               :source2 frame-clip
 | 
						||
                               :destination region))))
 | 
						||
          (exwm-cm--paint-opaque xwin region)
 | 
						||
          (when (and frame-clip
 | 
						||
                     (frame-live-p entity))
 | 
						||
            ;; Restore frame clip.
 | 
						||
            (xcb:+request exwm-cm--conn
 | 
						||
                (make-instance 'xcb:xfixes:UnionRegion
 | 
						||
                               :source1 region
 | 
						||
                               :source2 frame-clip
 | 
						||
                               :destination region)))))))
 | 
						||
    ;; Return the queue.
 | 
						||
    queue))
 | 
						||
 | 
						||
(defun exwm-cm--paint-opaque (xwin region &optional force-opaque)
 | 
						||
  "Paint an X window XWIN clipped by region REGION if XWIN is opaque.
 | 
						||
 | 
						||
Also update the attributes of XWIN and clip the region."
 | 
						||
  (with-slots (x y width height visual mode picture
 | 
						||
                 border-size extents border-clip)
 | 
						||
      (exwm-cm--xwin->attr xwin)
 | 
						||
    ;; Prepare the X window picture.
 | 
						||
    (unless picture
 | 
						||
      (setf picture (xcb:generate-id exwm-cm--conn))
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:render:CreatePicture
 | 
						||
                         :pid picture
 | 
						||
                         :drawable xwin
 | 
						||
                         :format (xcb:renderutil:find-visual-format
 | 
						||
                                  (xcb:renderutil:query-formats exwm-cm--conn)
 | 
						||
                                  visual)
 | 
						||
                         :value-mask 0)))
 | 
						||
    ;; Clear cached resources if clip changed.
 | 
						||
    (when exwm-cm--clip-changed
 | 
						||
      (when border-size
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:DestroyRegion
 | 
						||
                           :region border-size))
 | 
						||
        (setf border-size nil))
 | 
						||
      (when extents
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:DestroyRegion
 | 
						||
                           :region extents))
 | 
						||
        (setf extents nil))
 | 
						||
      (when border-clip
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:DestroyRegion
 | 
						||
                           :region border-clip))
 | 
						||
        (setf border-clip nil)))
 | 
						||
    ;; Retrieve the border.
 | 
						||
    (unless border-size
 | 
						||
      (setf border-size (xcb:generate-id exwm-cm--conn))
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:xfixes:CreateRegionFromWindow
 | 
						||
                         :region border-size
 | 
						||
                         :window xwin
 | 
						||
                         :kind xcb:shape:SK:Bounding))
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:xfixes:TranslateRegion
 | 
						||
                         :region border-size
 | 
						||
                         :dx x
 | 
						||
                         :dy y)))
 | 
						||
    ;; Retrieve the extents.
 | 
						||
    (unless extents
 | 
						||
      (setf extents (xcb:generate-id exwm-cm--conn))
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:xfixes:CreateRegion
 | 
						||
                         :region extents
 | 
						||
                         :rectangles (list (make-instance 'xcb:RECTANGLE
 | 
						||
                                                          :x x
 | 
						||
                                                          :y y
 | 
						||
                                                          :width width
 | 
						||
                                                          :height height)))))
 | 
						||
    (cond
 | 
						||
     ((and mode
 | 
						||
           (null force-opaque))
 | 
						||
      ;; Calculate clipped border for the transparent X window.
 | 
						||
      (unless border-clip
 | 
						||
        (setf border-clip (xcb:generate-id exwm-cm--conn))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:CreateRegion
 | 
						||
                           :region border-clip
 | 
						||
                           :rectangles nil))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:CopyRegion
 | 
						||
                           :source region
 | 
						||
                           :destination border-clip))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:IntersectRegion
 | 
						||
                           :source1 border-clip
 | 
						||
                           :source2 border-size
 | 
						||
                           :destination border-clip))))
 | 
						||
     (t
 | 
						||
      ;; Clip & render for the opaque X window.
 | 
						||
      ;; Set the clip region for the rendering buffer.
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:xfixes:SetPictureClipRegion
 | 
						||
                         :picture exwm-cm--buffer
 | 
						||
                         :region region
 | 
						||
                         :x-origin 0
 | 
						||
                         :y-origin 0))
 | 
						||
      ;; Clip the region with border.
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:xfixes:SubtractRegion
 | 
						||
                         :source1 region
 | 
						||
                         :source2 border-size
 | 
						||
                         :destination region))
 | 
						||
      ;; Render the picture to the buffer.
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:render:Composite
 | 
						||
                         :op xcb:render:PictOp:Src
 | 
						||
                         :src picture
 | 
						||
                         :mask xcb:render:Picture:None
 | 
						||
                         :dst exwm-cm--buffer
 | 
						||
                         :src-x 0
 | 
						||
                         :src-y 0
 | 
						||
                         :mask-x 0
 | 
						||
                         :mask-y 0
 | 
						||
                         :dst-x x
 | 
						||
                         :dst-y y
 | 
						||
                         :width width
 | 
						||
                         :height height))))))
 | 
						||
 | 
						||
(defun exwm-cm--paint-transparent (xwin)
 | 
						||
  "Paint a transparent X window XWIN."
 | 
						||
  (with-slots (x y width height opacity picture alpha-picture border-clip)
 | 
						||
      (exwm-cm--xwin->attr xwin)
 | 
						||
    ;; Prepare the alpha picture for transparent X windows.
 | 
						||
    (when (and opacity (null alpha-picture))
 | 
						||
      (setf alpha-picture (xcb:generate-id exwm-cm--conn))
 | 
						||
      (let ((pixmap (xcb:generate-id exwm-cm--conn)))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:CreatePixmap
 | 
						||
                           :depth 8
 | 
						||
                           :pid pixmap
 | 
						||
                           :drawable exwm--root
 | 
						||
                           :width 1
 | 
						||
                           :height 1))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:render:CreatePicture
 | 
						||
                           :pid alpha-picture
 | 
						||
                           :drawable pixmap
 | 
						||
                           :format (xcb:renderutil:find-standard
 | 
						||
                                    (xcb:renderutil:query-formats
 | 
						||
                                     exwm-cm--conn)
 | 
						||
                                    xcb:renderutil:PICT_STANDARD:A_8)
 | 
						||
                           :value-mask xcb:render:CP:Repeat
 | 
						||
                           :repeat 1))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:render:FillRectangles
 | 
						||
                           :op xcb:render:PictOp:Src
 | 
						||
                           :dst alpha-picture
 | 
						||
                           :color (make-instance 'xcb:render:COLOR
 | 
						||
                                                 :red 0
 | 
						||
                                                 :green 0
 | 
						||
                                                 :blue 0
 | 
						||
                                                 :alpha opacity)
 | 
						||
                           :rects (list (make-instance 'xcb:RECTANGLE
 | 
						||
                                                       :x 0
 | 
						||
                                                       :y 0
 | 
						||
                                                       :width 1
 | 
						||
                                                       :height 1))))))
 | 
						||
    ;; Set the clip region for the rendering buffer.
 | 
						||
    (xcb:+request exwm-cm--conn
 | 
						||
        (make-instance 'xcb:xfixes:SetPictureClipRegion
 | 
						||
                       :picture exwm-cm--buffer
 | 
						||
                       :region border-clip
 | 
						||
                       :x-origin 0
 | 
						||
                       :y-origin 0))
 | 
						||
    ;; Render the picture to the buffer.
 | 
						||
    (xcb:+request exwm-cm--conn
 | 
						||
        (make-instance 'xcb:render:Composite
 | 
						||
                       :op xcb:render:PictOp:Over
 | 
						||
                       :src picture
 | 
						||
                       :mask (or alpha-picture xcb:render:Picture:None)
 | 
						||
                       :dst exwm-cm--buffer
 | 
						||
                       :src-x 0
 | 
						||
                       :src-y 0
 | 
						||
                       :mask-x 0
 | 
						||
                       :mask-y 0
 | 
						||
                       :dst-x x
 | 
						||
                       :dst-y y
 | 
						||
                       :width width
 | 
						||
                       :height height))
 | 
						||
    (xcb:+request exwm-cm--conn
 | 
						||
        (make-instance 'xcb:xfixes:DestroyRegion
 | 
						||
                       :region border-clip))
 | 
						||
    (setf border-clip nil)))
 | 
						||
 | 
						||
(defun exwm-cm--paint (&optional region)
 | 
						||
  "Paint the whole tree within clipping region REGION.
 | 
						||
 | 
						||
If REGION is omitted, `exwm-cm--damages' is assumed.  If it's t, paint
 | 
						||
the whole screen."
 | 
						||
  ;; Prepare the clipping region.
 | 
						||
  (cond
 | 
						||
   ((null region)
 | 
						||
    (when exwm-cm--damages
 | 
						||
      (setq region exwm-cm--damages)))
 | 
						||
   ((eq region t)
 | 
						||
    (with-slots (width height) (exwm-cm--xwin->attr exwm--root)
 | 
						||
      (let ((rect (make-instance 'xcb:RECTANGLE
 | 
						||
                                 :x 0
 | 
						||
                                 :y 0
 | 
						||
                                 :width width
 | 
						||
                                 :height height)))
 | 
						||
        (setq region (xcb:generate-id exwm-cm--conn))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:CreateRegion
 | 
						||
                           :region region
 | 
						||
                           :rectangles (list rect)))))))
 | 
						||
  (when region
 | 
						||
    ;; Prepare the rendering buffer.
 | 
						||
    (unless exwm-cm--buffer
 | 
						||
      (let ((pixmap (xcb:generate-id exwm-cm--conn))
 | 
						||
            (picture (xcb:generate-id exwm-cm--conn)))
 | 
						||
        (setq exwm-cm--buffer picture)
 | 
						||
        (with-slots (width height visual) (exwm-cm--xwin->attr exwm--root)
 | 
						||
          (xcb:+request exwm-cm--conn
 | 
						||
              (make-instance 'xcb:CreatePixmap
 | 
						||
                             :depth exwm-cm--depth
 | 
						||
                             :pid pixmap
 | 
						||
                             :drawable exwm--root
 | 
						||
                             :width width
 | 
						||
                             :height height))
 | 
						||
          (xcb:+request exwm-cm--conn
 | 
						||
              (make-instance 'xcb:render:CreatePicture
 | 
						||
                             :pid picture
 | 
						||
                             :drawable pixmap
 | 
						||
                             :format (xcb:renderutil:find-visual-format
 | 
						||
                                      (xcb:renderutil:query-formats
 | 
						||
                                       exwm-cm--conn)
 | 
						||
                                      visual)
 | 
						||
                             :value-mask 0)))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:FreePixmap
 | 
						||
                           :pixmap pixmap))))
 | 
						||
    (let (queue)
 | 
						||
      ;; Paint opaque X windows and update clipping region.
 | 
						||
      (setq queue (exwm-cm--paint-tree nil region))
 | 
						||
      ;; Paint the background.
 | 
						||
      (exwm-cm--paint-background region)
 | 
						||
      ;; Paint transparent X windows.
 | 
						||
      (while queue
 | 
						||
        (exwm-cm--paint-transparent (pop queue))))
 | 
						||
    ;; Submit changes.
 | 
						||
    (with-slots (width height picture) (exwm-cm--xwin->attr exwm--root)
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:xfixes:SetPictureClipRegion
 | 
						||
                         :picture exwm-cm--buffer
 | 
						||
                         :region xcb:xfixes:Region:None
 | 
						||
                         :x-origin 0
 | 
						||
                         :y-origin 0))
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:render:Composite
 | 
						||
                         :op xcb:render:PictOp:Src
 | 
						||
                         :src exwm-cm--buffer
 | 
						||
                         :mask xcb:render:Picture:None
 | 
						||
                         :dst picture
 | 
						||
                         :src-x 0
 | 
						||
                         :src-y 0
 | 
						||
                         :mask-x 0
 | 
						||
                         :mask-y 0
 | 
						||
                         :dst-x 0
 | 
						||
                         :dst-y 0
 | 
						||
                         :width width
 | 
						||
                         :height height)))
 | 
						||
    ;; Cleanup.
 | 
						||
    (xcb:+request exwm-cm--conn
 | 
						||
        (make-instance 'xcb:xfixes:DestroyRegion
 | 
						||
                       :region region))
 | 
						||
    (when (eq region exwm-cm--damages)
 | 
						||
      (setq exwm-cm--damages nil))
 | 
						||
    (setq exwm-cm--clip-changed nil)
 | 
						||
    (xcb:flush exwm-cm--conn)))
 | 
						||
 | 
						||
(defun exwm-cm--paint-background (region)
 | 
						||
  "Paint the background."
 | 
						||
  (unless exwm-cm--background
 | 
						||
    (setq exwm-cm--background (xcb:generate-id exwm-cm--conn))
 | 
						||
    (let (pixmap exist)
 | 
						||
      (catch 'break
 | 
						||
        (dolist (atom exwm-cm--background-atoms)
 | 
						||
          (with-slots (~lsb format value-len value)
 | 
						||
              (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                  (make-instance 'xcb:GetProperty
 | 
						||
                                 :delete 0
 | 
						||
                                 :window exwm--root
 | 
						||
                                 :property atom
 | 
						||
                                 :type xcb:Atom:PIXMAP
 | 
						||
                                 :long-offset 0
 | 
						||
                                 :long-length 4))
 | 
						||
            (when (and (= format 32)
 | 
						||
                       (= 1 value-len))
 | 
						||
              (setq pixmap (if ~lsb
 | 
						||
                               (xcb:-unpack-u4-lsb value 0)
 | 
						||
                             (xcb:-unpack-u4 value 0)))
 | 
						||
              (setq exist t)
 | 
						||
              (throw 'break nil)))))
 | 
						||
      (unless pixmap
 | 
						||
        (setq pixmap (xcb:generate-id exwm-cm--conn))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:CreatePixmap
 | 
						||
                           :depth exwm-cm--depth
 | 
						||
                           :pid pixmap
 | 
						||
                           :drawable exwm--root
 | 
						||
                           :width 1
 | 
						||
                           :height 1)))
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:render:CreatePicture
 | 
						||
                         :pid exwm-cm--background
 | 
						||
                         :drawable pixmap
 | 
						||
                         :format (xcb:renderutil:find-visual-format
 | 
						||
                                  (xcb:renderutil:query-formats exwm-cm--conn)
 | 
						||
                                  (slot-value (exwm-cm--xwin->attr exwm--root)
 | 
						||
                                              'visual))
 | 
						||
                         :value-mask xcb:render:CP:Repeat
 | 
						||
                         :repeat 1))
 | 
						||
      (unless exist
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:render:FillRectangles
 | 
						||
                           :op xcb:render:PictOp:Src
 | 
						||
                           :dst exwm-cm--background
 | 
						||
                           :color (make-instance 'xcb:render:COLOR
 | 
						||
                                                 :red #x8080
 | 
						||
                                                 :green #x8080
 | 
						||
                                                 :blue #x8080
 | 
						||
                                                 :alpha #xFFFF)
 | 
						||
                           :rects (list (make-instance 'xcb:RECTANGLE
 | 
						||
                                                       :x 0
 | 
						||
                                                       :y 0
 | 
						||
                                                       :width 1
 | 
						||
                                                       :height 1)))))))
 | 
						||
  (xcb:+request exwm-cm--conn
 | 
						||
      (make-instance 'xcb:xfixes:SetPictureClipRegion
 | 
						||
                     :picture exwm-cm--buffer
 | 
						||
                     :region region
 | 
						||
                     :x-origin 0
 | 
						||
                     :y-origin 0))
 | 
						||
  (with-slots (width height) (exwm-cm--xwin->attr exwm--root)
 | 
						||
    (xcb:+request exwm-cm--conn
 | 
						||
        (make-instance 'xcb:render:Composite
 | 
						||
                       :op xcb:render:PictOp:Src
 | 
						||
                       :src exwm-cm--background
 | 
						||
                       :mask xcb:render:Picture:None
 | 
						||
                       :dst exwm-cm--buffer
 | 
						||
                       :src-x 0
 | 
						||
                       :src-y 0
 | 
						||
                       :mask-x 0
 | 
						||
                       :mask-y 0
 | 
						||
                       :dst-x 0
 | 
						||
                       :dst-y 0
 | 
						||
                       :width width
 | 
						||
                       :height height))))
 | 
						||
 | 
						||
(defun exwm-cm--map-xwin (xwin &optional silent)
 | 
						||
  "Prepare to map X window XWIN."
 | 
						||
  (let ((attr (exwm-cm--xwin->attr xwin)))
 | 
						||
    (setf (slot-value attr 'damaged) nil)
 | 
						||
    ;; Add to damage.
 | 
						||
    (when (slot-value attr 'extents)
 | 
						||
      (let ((damage (xcb:generate-id exwm-cm--conn)))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:CreateRegion
 | 
						||
                           :region damage
 | 
						||
                           :rectangles nil))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:CopyRegion
 | 
						||
                           :source (slot-value attr 'extents)
 | 
						||
                           :destination damage))
 | 
						||
        (exwm-cm--add-damage damage))
 | 
						||
      (unless silent
 | 
						||
        (exwm-cm--paint)))))
 | 
						||
 | 
						||
(defun exwm-cm--on-MapNotify (data _synthetic)
 | 
						||
  "Handle MapNotify events."
 | 
						||
  (let ((obj (make-instance 'xcb:MapNotify))
 | 
						||
        attr)
 | 
						||
    (xcb:unmarshal obj data)
 | 
						||
    (with-slots (event window) obj
 | 
						||
      (exwm--log "(CM) MapNotify: Try to map #x%X" window)
 | 
						||
      (setq attr (exwm-cm--xwin->attr window))
 | 
						||
      (when (and attr
 | 
						||
                 (/= (slot-value attr 'class) xcb:WindowClass:InputOnly)
 | 
						||
                 (or (= event exwm--root)
 | 
						||
                     ;; Filter out duplicated events.
 | 
						||
                     (/= exwm--root (exwm-cm--get-parent window))))
 | 
						||
        (exwm--log "(CM) MapNotify: Map")
 | 
						||
        (exwm-cm--map-xwin window)))))
 | 
						||
 | 
						||
(defun exwm-cm--on-UnmapNotify (data _synthetic)
 | 
						||
  "Handle UnmapNotify events."
 | 
						||
  (let ((obj (make-instance 'xcb:UnmapNotify))
 | 
						||
        attr)
 | 
						||
    (xcb:unmarshal obj data)
 | 
						||
    (with-slots (event window) obj
 | 
						||
      (exwm--log "(CM) UnmapNotify: Try to unmap #x%X" window)
 | 
						||
      (setq attr (exwm-cm--xwin->attr window))
 | 
						||
      (when (and attr
 | 
						||
                 (or (= event exwm--root)
 | 
						||
                     ;; Filter out duplicated events.
 | 
						||
                     (/= exwm--root (exwm-cm--get-parent window))))
 | 
						||
        (exwm--log "(CM) UnmapNotify: Unmap")
 | 
						||
        (with-slots (picture damaged border-size extents border-clip) attr
 | 
						||
          (setf damaged nil)
 | 
						||
          (when picture
 | 
						||
            (xcb:+request exwm-cm--conn
 | 
						||
                (make-instance 'xcb:render:FreePicture
 | 
						||
                               :picture picture))
 | 
						||
            (setf picture nil))
 | 
						||
          (when border-size
 | 
						||
            (xcb:+request exwm-cm--conn
 | 
						||
                (make-instance 'xcb:xfixes:DestroyRegion
 | 
						||
                               :region border-size))
 | 
						||
            (setf border-size nil))
 | 
						||
          (when extents
 | 
						||
            (exwm-cm--add-damage extents)
 | 
						||
            (setf extents nil))
 | 
						||
          (when border-clip
 | 
						||
            (xcb:+request exwm-cm--conn
 | 
						||
                (make-instance 'xcb:xfixes:DestroyRegion
 | 
						||
                               :region border-clip))
 | 
						||
            (setf border-clip nil)))
 | 
						||
        (setq exwm-cm--clip-changed t)
 | 
						||
        (exwm-cm--paint)))))
 | 
						||
 | 
						||
(defun exwm-cm--on-CreateNotify (data _synthetic)
 | 
						||
  "Handle CreateNotify events."
 | 
						||
  (let ((obj (make-instance 'xcb:CreateNotify))
 | 
						||
        tree0)
 | 
						||
    (xcb:unmarshal obj data)
 | 
						||
    (with-slots (window parent x y width height) obj
 | 
						||
      (exwm--log "(CM) CreateNotify: Create #x%X on #x%X @%sx%s%+d%+d"
 | 
						||
                 window parent width height x y)
 | 
						||
      (cl-assert (= parent exwm--root))
 | 
						||
      (cl-assert (null (exwm-cm--xwin->attr window)))
 | 
						||
      (setq tree0 (exwm-cm--get-subtree parent))
 | 
						||
      (exwm-cm--create-attr window tree0 x y width height)
 | 
						||
      (if (cdr tree0)
 | 
						||
          (exwm-cm--push (list window) (cdr tree0))
 | 
						||
        (setcdr tree0 `((,window)))))))
 | 
						||
 | 
						||
(defun exwm-cm--on-ConfigureNotify (data synthetic)
 | 
						||
  "Handle ConfigureNotify events."
 | 
						||
  ;; Ignore synthetic ConfigureNotify events sent by the WM.
 | 
						||
  (unless synthetic
 | 
						||
    (let ((obj (make-instance 'xcb:ConfigureNotify)))
 | 
						||
      (xcb:unmarshal obj data)
 | 
						||
      (with-slots (event window above-sibling x y width height) obj
 | 
						||
        (exwm--log
 | 
						||
         "(CM) ConfigureNotify: Try to configure #x%X @%sx%s%+d%+d, above #x%X"
 | 
						||
         window width height x y above-sibling)
 | 
						||
        (cond
 | 
						||
         ((= window exwm--root)
 | 
						||
          (exwm--log "(CM) ConfigureNotify: Configure the root X window")
 | 
						||
          (when exwm-cm--buffer
 | 
						||
            (xcb:+request exwm-cm--conn
 | 
						||
                (make-instance 'xcb:render:FreePicture
 | 
						||
                               :picture exwm-cm--buffer))
 | 
						||
            (setq exwm-cm--buffer nil))
 | 
						||
          (with-slots ((x* x)
 | 
						||
                       (y* y)
 | 
						||
                       (width* width)
 | 
						||
                       (height* height))
 | 
						||
              (exwm-cm--xwin->attr exwm--root)
 | 
						||
            (setf x* x
 | 
						||
                  y* y
 | 
						||
                  width* width
 | 
						||
                  height* height))
 | 
						||
          (exwm-cm--paint))
 | 
						||
         ((null (exwm-cm--xwin->attr window)))
 | 
						||
         ((or (= event exwm--root)
 | 
						||
              ;; Filter out duplicated events.
 | 
						||
              (/= exwm--root (exwm-cm--get-parent window)))
 | 
						||
          (exwm--log "(CM) ConfigureNotify: Configure")
 | 
						||
          (with-slots ((x0 x)
 | 
						||
                       (y0 y))
 | 
						||
              (exwm-cm--xwin->attr (exwm-cm--get-parent window))
 | 
						||
            (exwm-cm--update-geometry window (+ x x0) (+ y y0) width height
 | 
						||
                                      above-sibling))
 | 
						||
          (setq exwm-cm--clip-changed t)
 | 
						||
          (exwm-cm--paint))
 | 
						||
         (t
 | 
						||
          (exwm--log "(CM) ConfigureNotify: Skip event from #x%X" event)))))))
 | 
						||
 | 
						||
(defun exwm-cm--destroy (xwin)
 | 
						||
  "Prepare to destroy X window XWIN."
 | 
						||
  (with-slots (tree picture alpha-picture damage
 | 
						||
                    border-size extents border-clip)
 | 
						||
      (exwm-cm--xwin->attr xwin)
 | 
						||
    (cl-assert (assq xwin (cdr tree)))
 | 
						||
    (if (= 1 (length (cdr tree)))
 | 
						||
        (setcdr tree nil)
 | 
						||
      (exwm-cm--assq-delete-all xwin (cdr tree)))
 | 
						||
    (remhash xwin exwm-cm--hash)
 | 
						||
    ;; Release resources.
 | 
						||
    (when picture
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:render:FreePicture
 | 
						||
                         :picture picture))
 | 
						||
      (setf picture nil))
 | 
						||
    (when alpha-picture
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:render:FreePicture
 | 
						||
                         :picture alpha-picture))
 | 
						||
      (setf alpha-picture nil))
 | 
						||
    (when damage
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:damage:Destroy
 | 
						||
                         :damage damage))
 | 
						||
      (setf damage nil))
 | 
						||
    (when border-size
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:xfixes:DestroyRegion
 | 
						||
                         :region border-size))
 | 
						||
      (setf border-size nil))
 | 
						||
    (when extents
 | 
						||
      (exwm-cm--add-damage extents)
 | 
						||
      (setf extents nil))
 | 
						||
    (when border-clip
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:xfixes:DestroyRegion
 | 
						||
                         :region border-clip))
 | 
						||
      (setf border-clip nil))))
 | 
						||
 | 
						||
(defun exwm-cm--on-DestroyNotify (data _synthetic)
 | 
						||
  "Handle DestroyNotify events."
 | 
						||
  (let ((obj (make-instance 'xcb:DestroyNotify))
 | 
						||
        xwin)
 | 
						||
    (xcb:unmarshal obj data)
 | 
						||
    (setq xwin (slot-value obj 'window))
 | 
						||
    (exwm--log "(CM) DestroyNotify: Try to destroy #x%X" xwin)
 | 
						||
    (when (exwm-cm--xwin->attr xwin)
 | 
						||
      (exwm--log "(CM) DestroyNotify: Destroy")
 | 
						||
      (exwm-cm--destroy xwin))))
 | 
						||
 | 
						||
(defun exwm-cm--on-CirculateNotify (data _synthetic)
 | 
						||
  "Handle CirculateNotify events."
 | 
						||
  (let ((obj (make-instance 'xcb:CirculateNotify))
 | 
						||
        attr)
 | 
						||
    (xcb:unmarshal obj data)
 | 
						||
    (with-slots (event window place) obj
 | 
						||
      (setq attr (exwm-cm--xwin->attr window))
 | 
						||
      (exwm--log "(CM) CirculateNotify: Try to circulate #x%X to %s"
 | 
						||
                 window place)
 | 
						||
      (when (and attr
 | 
						||
                 (or (= event exwm--root)
 | 
						||
                     ;; Filter out duplicated events.
 | 
						||
                     (/= exwm--root (exwm-cm--get-parent window))))
 | 
						||
        (exwm--log "(CM) CirculateNotify: Circulate")
 | 
						||
        (exwm-cm--update-geometry window nil nil nil nil
 | 
						||
                                  (if (= place xcb:Circulate:LowerHighest)
 | 
						||
                                      xcb:Window:None
 | 
						||
                                    (caar (exwm-cm--get-siblings window))))
 | 
						||
        (setq exwm-cm--clip-changed t)
 | 
						||
        (exwm-cm--paint)))))
 | 
						||
 | 
						||
(defun exwm-cm--on-Expose (data _synthetic)
 | 
						||
  "Handle Expose events."
 | 
						||
  (let ((obj (make-instance 'xcb:Expose)))
 | 
						||
    (xcb:unmarshal obj data)
 | 
						||
    (with-slots (window x y width height count) obj
 | 
						||
      (when (eq window exwm--root)
 | 
						||
        (push (make-instance 'xcb:RECTANGLE
 | 
						||
                             :x x
 | 
						||
                             :y y
 | 
						||
                             :width width
 | 
						||
                             :height height)
 | 
						||
              exwm-cm--expose-rectangles))
 | 
						||
      (when (= count 0)
 | 
						||
        (let ((region (xcb:generate-id exwm-cm--conn)))
 | 
						||
          (xcb:+request exwm-cm--conn
 | 
						||
              (xcb:xfixes:CreateRegion
 | 
						||
               :region region
 | 
						||
               :rectangles exwm-cm--expose-rectangles))
 | 
						||
          (exwm-cm--add-damage region))
 | 
						||
        (setq exwm-cm--expose-rectangles nil)
 | 
						||
        (exwm-cm--paint)))))
 | 
						||
 | 
						||
(defun exwm-cm--on-PropertyNotify (data _synthetic)
 | 
						||
  "Handle PropertyNotify events."
 | 
						||
  (let ((obj (make-instance 'xcb:PropertyNotify)))
 | 
						||
    (xcb:unmarshal obj data)
 | 
						||
    (with-slots (window atom) obj
 | 
						||
      (cond
 | 
						||
       ((and (= window exwm--root)
 | 
						||
             (memq atom exwm-cm--background-atoms))
 | 
						||
        (exwm--log "(CM) PropertyNotify: Update background")
 | 
						||
        (when exwm-cm--background
 | 
						||
          (xcb:+request exwm-cm--conn
 | 
						||
              (make-instance 'xcb:render:FreePicture
 | 
						||
                             :picture exwm-cm--background))
 | 
						||
          (setq exwm-cm--background nil)
 | 
						||
          (xcb:+request exwm-cm--conn
 | 
						||
              (make-instance 'xcb:ClearArea
 | 
						||
                             :exposures 1
 | 
						||
                             :window exwm--root
 | 
						||
                             :x 0
 | 
						||
                             :y 0
 | 
						||
                             :width 0
 | 
						||
                             :height 0))
 | 
						||
          (xcb:flush exwm-cm--conn)))
 | 
						||
       ((and (= atom exwm-cm--_NET_WM_WINDOW_OPACITY)
 | 
						||
             ;; Some applications also set this property on their parents.
 | 
						||
             (null (cdr (exwm-cm--get-subtree window))))
 | 
						||
        (when (exwm-cm--xwin->attr window)
 | 
						||
          (exwm--log "(CM) PropertyNotify: Update opacity for #x%X" window)
 | 
						||
          (exwm-cm--update-opacity window)
 | 
						||
          (exwm-cm--paint)))))))
 | 
						||
 | 
						||
(defun exwm-cm--prepare-container (xwin)
 | 
						||
  "Make X window XWIN a container by deselecting unnecessary events."
 | 
						||
  (with-slots (damage) (exwm-cm--xwin->attr xwin)
 | 
						||
    (when damage
 | 
						||
      (xcb:+request exwm-cm--conn
 | 
						||
          (make-instance 'xcb:damage:Destroy
 | 
						||
                         :damage damage)))
 | 
						||
    (xcb:+request exwm-cm--conn
 | 
						||
        (make-instance 'xcb:shape:SelectInput
 | 
						||
                       :destination-window xwin
 | 
						||
                       :enable 0))))
 | 
						||
 | 
						||
(defun exwm-cm--on-ReparentNotify (data _synthetic)
 | 
						||
  "Handle ReparentNotify events."
 | 
						||
  (let ((obj (make-instance 'xcb:ReparentNotify))
 | 
						||
        tree tree0 grandparent great-grandparent entity)
 | 
						||
    (xcb:unmarshal obj data)
 | 
						||
    (with-slots (window parent x y) obj
 | 
						||
      (exwm--log "(CM) ReparentNotify: Try to reparent #x%X to #x%X @%+d%+d"
 | 
						||
                 window parent x y)
 | 
						||
      (cond
 | 
						||
       ((null (exwm-cm--xwin->attr window))
 | 
						||
        (when (eq parent exwm--root)
 | 
						||
          (exwm--log "(CM) ReparentNotify: Create on the root X window")
 | 
						||
          (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                           (make-instance 'xcb:GetGeometry
 | 
						||
                                          :drawable window))))
 | 
						||
            (when reply
 | 
						||
              (with-slots (width height) reply
 | 
						||
                (setq tree0 (exwm-cm--get-subtree exwm--root))
 | 
						||
                (exwm-cm--create-attr window tree0 x y width height)
 | 
						||
                (if (cdr tree0)
 | 
						||
                    (exwm-cm--push (list window) (cdr tree0))
 | 
						||
                  (setcdr tree0 `((,window)))))
 | 
						||
              (exwm-cm--paint)))))
 | 
						||
       ((= parent (exwm-cm--get-parent window)))
 | 
						||
       (t
 | 
						||
        (unless (exwm-cm--xwin->attr parent)
 | 
						||
          ;; Only allow workspace frame here.
 | 
						||
          (setq grandparent
 | 
						||
                (slot-value (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                                (make-instance 'xcb:QueryTree
 | 
						||
                                               :window parent))
 | 
						||
                            'parent))
 | 
						||
          (cond
 | 
						||
           ((null (exwm-cm--xwin->attr grandparent))
 | 
						||
            (exwm--log "(CM) ReparentNotify: Destroy (too deep)"))
 | 
						||
           ((and (= exwm--root
 | 
						||
                    (setq great-grandparent (exwm-cm--get-parent grandparent)))
 | 
						||
                 (setq tree0 (exwm-cm--get-subtree grandparent))
 | 
						||
                 (or (setq entity (exwm--id->buffer window))
 | 
						||
                     (null (cdr tree0))))
 | 
						||
            ;; Reparent a workspace frame or an X window into its
 | 
						||
            ;; container.
 | 
						||
            (exwm--debug
 | 
						||
             (if entity
 | 
						||
                 (exwm--log "(CM) ReparentNotify: \
 | 
						||
Create implicit X window container")
 | 
						||
               (exwm--log "(CM) ReparentNotify: \
 | 
						||
Create implicit workspace frame container")))
 | 
						||
            (unless entity
 | 
						||
              (setq entity 'workspace-frame))
 | 
						||
            (with-slots ((x0 x)
 | 
						||
                         (y0 y))
 | 
						||
                (exwm-cm--xwin->attr grandparent)
 | 
						||
              (with-slots (x y width height)
 | 
						||
                  (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                      (make-instance 'xcb:GetGeometry
 | 
						||
                                     :drawable parent))
 | 
						||
                (exwm-cm--create-attr parent tree0
 | 
						||
                                      (+ x x0) (+ y y0) width height)))
 | 
						||
            (if (null (cdr tree0))
 | 
						||
                (setcdr tree0 `((,parent)))
 | 
						||
              ;; The stacking order of the parent is unknown.
 | 
						||
              (let* ((siblings
 | 
						||
                      (slot-value (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                                      (make-instance 'xcb:QueryTree
 | 
						||
                                                     :window grandparent))
 | 
						||
                                  'children)))
 | 
						||
                (cl-assert (memq parent siblings))
 | 
						||
                (if (= parent (car siblings))
 | 
						||
                    ;; At the bottom.
 | 
						||
                    (setcdr (last (cdr tree0)) `((,parent)))
 | 
						||
                  ;; Insert it.
 | 
						||
                  (exwm-cm--push (list parent)
 | 
						||
                                 ;; The stacking order is reversed.
 | 
						||
                                 (nthcdr (- (length siblings) 1
 | 
						||
                                            (cl-position parent siblings))
 | 
						||
                                         (cdr tree0)))))))
 | 
						||
           ((and (= exwm--root
 | 
						||
                    (exwm-cm--get-parent great-grandparent))
 | 
						||
                 (setq tree0 (exwm-cm--get-subtree grandparent))
 | 
						||
                 (= 1 (length (cdr tree0)))
 | 
						||
                 (exwm--id->buffer (caar (cdr tree0))))
 | 
						||
            ;; Reparent a floating frame into its container.
 | 
						||
            (exwm--log "(CM) ReparentNotify: Create floating frame container")
 | 
						||
            (setq entity 'floating-frame)
 | 
						||
            (with-slots ((x0 x)
 | 
						||
                         (y0 y))
 | 
						||
                (exwm-cm--xwin->attr grandparent)
 | 
						||
              (with-slots (x y width height)
 | 
						||
                  (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                      (make-instance 'xcb:GetGeometry
 | 
						||
                                     :drawable parent))
 | 
						||
                (exwm-cm--create-attr parent tree0
 | 
						||
                                      (+ x x0) (+ y y0) width height)))
 | 
						||
            (nconc (cdr tree0) `((,parent))))
 | 
						||
           (t
 | 
						||
            (exwm--log "(CM) ReparentNotify: Destroy")
 | 
						||
            (exwm-cm--destroy window))))
 | 
						||
        ;; Ensure there's a valid parent.
 | 
						||
        (when (exwm-cm--xwin->attr parent)
 | 
						||
          (exwm--log "(CM) ReparentNotify: Reparent")
 | 
						||
          (when (null (cdr (exwm-cm--get-subtree parent)))
 | 
						||
            ;; The parent is a new container.
 | 
						||
            (exwm-cm--prepare-container parent))
 | 
						||
          (setq tree (exwm-cm--get-subtree window))
 | 
						||
          (let ((tree (exwm-cm--get-tree window)))
 | 
						||
            (if (= 1 (length (cdr tree)))
 | 
						||
                (setcdr tree nil)
 | 
						||
              (exwm-cm--assq-delete-all window (cdr tree))))
 | 
						||
          (setq tree0 (exwm-cm--get-subtree parent))
 | 
						||
          (exwm-cm--set-tree window tree0)
 | 
						||
          ;; The size might have already changed (e.g. when reparenting
 | 
						||
          ;; a workspace frame).
 | 
						||
          (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                           (make-instance 'xcb:GetGeometry
 | 
						||
                                          :drawable window))))
 | 
						||
            ;; The X window might have already been destroyed.
 | 
						||
            (when reply
 | 
						||
              (with-slots (width height) reply
 | 
						||
                (with-slots ((x0 x)
 | 
						||
                             (y0 y))
 | 
						||
                    (exwm-cm--xwin->attr parent)
 | 
						||
                  (exwm-cm--update-geometry window (+ x x0) (+ y y0)
 | 
						||
                                            width height)))))
 | 
						||
          (when entity
 | 
						||
            ;; Decide frame entity.
 | 
						||
            (when (symbolp entity)
 | 
						||
              (catch 'break
 | 
						||
                (dolist (f (if (eq entity 'workspace-frame)
 | 
						||
                               exwm-workspace--list
 | 
						||
                             (frame-list)))
 | 
						||
                  (when (eq window (frame-parameter f 'exwm-outer-id))
 | 
						||
                    (setq entity f)
 | 
						||
                    (throw 'break nil))))
 | 
						||
              (when (exwm-workspace--workspace-p entity)
 | 
						||
                ;; The grandparent is a new workspace container.
 | 
						||
                (exwm-cm--prepare-container grandparent)
 | 
						||
                (setf (slot-value (exwm-cm--xwin->attr grandparent) 'entity)
 | 
						||
                      entity)))
 | 
						||
            (setf (slot-value (exwm-cm--xwin->attr parent) 'entity) entity)
 | 
						||
            (setf (slot-value (exwm-cm--xwin->attr window) 'entity) entity))
 | 
						||
          (if (cdr tree0)
 | 
						||
              (exwm-cm--push tree (cdr tree0))
 | 
						||
            (setcdr tree0 `(,tree)))
 | 
						||
          (exwm-cm--paint)))))))
 | 
						||
 | 
						||
(defun exwm-cm--add-damage (damage)
 | 
						||
  "Add region DAMAGE to `exwm-cm--damages'."
 | 
						||
  (if (not exwm-cm--damages)
 | 
						||
      (setq exwm-cm--damages damage)
 | 
						||
    (xcb:+request exwm-cm--conn
 | 
						||
        (make-instance 'xcb:xfixes:UnionRegion
 | 
						||
                       :source1 exwm-cm--damages
 | 
						||
                       :source2 damage
 | 
						||
                       :destination exwm-cm--damages))
 | 
						||
    (xcb:+request exwm-cm--conn
 | 
						||
        (make-instance 'xcb:xfixes:DestroyRegion
 | 
						||
                       :region damage))))
 | 
						||
 | 
						||
(defun exwm-cm--on-DamageNotify (data _synthetic)
 | 
						||
  "Handle DamageNotify events."
 | 
						||
  (let ((obj (make-instance 'xcb:damage:Notify))
 | 
						||
        parts)
 | 
						||
    (xcb:unmarshal obj data)
 | 
						||
    (cl-assert (exwm-cm--xwin->attr (slot-value obj 'drawable)))
 | 
						||
    (with-slots (x y width height damaged damage)
 | 
						||
        (exwm-cm--xwin->attr (slot-value obj 'drawable))
 | 
						||
      (setq parts (xcb:generate-id exwm-cm--conn))
 | 
						||
      (cond
 | 
						||
       (damaged
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:CreateRegion
 | 
						||
                           :region parts
 | 
						||
                           :rectangles nil))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:damage:Subtract
 | 
						||
                           :damage damage
 | 
						||
                           :repair xcb:xfixes:Region:None
 | 
						||
                           :parts parts))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:TranslateRegion
 | 
						||
                           :region parts
 | 
						||
                           :dx x
 | 
						||
                           :dy y)))
 | 
						||
       (t
 | 
						||
        (setf damaged t)
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:xfixes:CreateRegion
 | 
						||
                           :region parts
 | 
						||
                           :rectangles (list (make-instance 'xcb:RECTANGLE
 | 
						||
                                                            :width width
 | 
						||
                                                            :height height
 | 
						||
                                                            :x x
 | 
						||
                                                            :y y))))
 | 
						||
        (xcb:+request exwm-cm--conn
 | 
						||
            (make-instance 'xcb:damage:Subtract
 | 
						||
                           :damage damage
 | 
						||
                           :repair xcb:xfixes:Region:None
 | 
						||
                           :parts xcb:xfixes:Region:None))))
 | 
						||
      (exwm-cm--add-damage parts))
 | 
						||
    ;; Check if there are more damages immediately followed.
 | 
						||
    (unless (/= 0 (logand #x80 (slot-value obj 'level)))
 | 
						||
      (exwm-cm--paint))))
 | 
						||
 | 
						||
(defun exwm-cm--on-ShapeNotify (data _synthetic)
 | 
						||
  "Handle ShapeNotify events."
 | 
						||
  (let ((obj (make-instance 'xcb:shape:Notify))
 | 
						||
        attr region1 region2)
 | 
						||
    (xcb:unmarshal obj data)
 | 
						||
    (with-slots (shape-kind affected-window shaped
 | 
						||
                            extents-x extents-y extents-width extents-height)
 | 
						||
        obj
 | 
						||
      (exwm--log "(CM) ShapeNotify: #x%X" affected-window)
 | 
						||
      (when (and (or (eq shape-kind xcb:shape:SK:Clip)
 | 
						||
                     (eq shape-kind xcb:shape:SK:Bounding))
 | 
						||
                 (setq attr (exwm-cm--xwin->attr affected-window)))
 | 
						||
        (with-slots ((shaped* shaped)
 | 
						||
                     x y width height
 | 
						||
                     shape-x shape-y shape-width shape-height)
 | 
						||
            attr
 | 
						||
          (setq region1 (xcb:generate-id exwm-cm--conn)
 | 
						||
                region2 (xcb:generate-id exwm-cm--conn))
 | 
						||
          (xcb:+request exwm-cm--conn
 | 
						||
              (make-instance 'xcb:xfixes:CreateRegion
 | 
						||
                             :region region1
 | 
						||
                             :rectangles `(,(make-instance 'xcb:RECTANGLE
 | 
						||
                                                           :width shape-width
 | 
						||
                                                           :height shape-height
 | 
						||
                                                           :x shape-x
 | 
						||
                                                           :y shape-y))))
 | 
						||
          (if shaped
 | 
						||
              (setf shaped* t
 | 
						||
                    shape-x (+ x extents-x)
 | 
						||
                    shape-y (+ y extents-y)
 | 
						||
                    shape-width (+ width extents-width)
 | 
						||
                    shape-height (+ height extents-height))
 | 
						||
            (setf shaped* nil
 | 
						||
                  shape-x x
 | 
						||
                  shape-y y
 | 
						||
                  shape-width width
 | 
						||
                  shape-height height))
 | 
						||
          (xcb:+request exwm-cm--conn
 | 
						||
              (make-instance 'xcb:xfixes:CreateRegion
 | 
						||
                             :region region2
 | 
						||
                             :rectangles `(,(make-instance 'xcb:RECTANGLE
 | 
						||
                                                           :width shape-width
 | 
						||
                                                           :height shape-height
 | 
						||
                                                           :x shape-x
 | 
						||
                                                           :y shape-y))))
 | 
						||
          (xcb:+request exwm-cm--conn
 | 
						||
              (make-instance 'xcb:xfixes:UnionRegion
 | 
						||
                             :source1 region1
 | 
						||
                             :source2 region2
 | 
						||
                             :destination region1))
 | 
						||
          (xcb:+request exwm-cm--conn
 | 
						||
              (make-instance 'xcb:xfixes:DestroyRegion
 | 
						||
                             :region region2))
 | 
						||
          (setq exwm-cm--clip-changed t)
 | 
						||
          (exwm-cm--paint region1))))))
 | 
						||
 | 
						||
(defun exwm-cm--init ()
 | 
						||
  "Initialize EXWM compositing manager."
 | 
						||
  ;; Create a new connection.
 | 
						||
  (setq exwm-cm--conn (xcb:connect))
 | 
						||
  (set-process-query-on-exit-flag (slot-value exwm-cm--conn 'process) nil)
 | 
						||
  ;; Initialize ICCCM/EWMH support.
 | 
						||
  (xcb:icccm:init exwm-cm--conn)
 | 
						||
  (xcb:ewmh:init exwm-cm--conn)
 | 
						||
  ;; Check for Render extension.
 | 
						||
  (let ((version (xcb:renderutil:query-version exwm-cm--conn)))
 | 
						||
    (unless (and version
 | 
						||
                 (= 0 (slot-value version 'major-version))
 | 
						||
                 (<= 2 (slot-value version 'minor-version)))
 | 
						||
      (error "[EXWM] The server does not support Render extension")))
 | 
						||
  ;; Check for Composite extension.
 | 
						||
  (when (or (= 0
 | 
						||
               (slot-value (xcb:get-extension-data exwm-cm--conn
 | 
						||
                                                   'xcb:composite)
 | 
						||
                           'present))
 | 
						||
            (with-slots (major-version minor-version)
 | 
						||
                (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                    (make-instance 'xcb:composite:QueryVersion
 | 
						||
                                   :client-major-version 0
 | 
						||
                                   :client-minor-version 1))
 | 
						||
              (or (/= major-version 0) (< minor-version 1))))
 | 
						||
    (error "[EXWM] The server does not support Composite extension"))
 | 
						||
  ;; Check for Damage extension.
 | 
						||
  (when (or (= 0 (slot-value (xcb:get-extension-data exwm-cm--conn 'xcb:damage)
 | 
						||
                             'present))
 | 
						||
            (with-slots (major-version minor-version)
 | 
						||
                (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                    (make-instance 'xcb:damage:QueryVersion
 | 
						||
                                   :client-major-version 1
 | 
						||
                                   :client-minor-version 1))
 | 
						||
              (or (/= major-version 1) (< minor-version 1))))
 | 
						||
    (error "[EXWM] The server does not support Damage extension"))
 | 
						||
  ;; Check for XFixes extension.
 | 
						||
  (when (or (= 0 (slot-value (xcb:get-extension-data exwm-cm--conn 'xcb:xfixes)
 | 
						||
                             'present))
 | 
						||
            (with-slots (major-version minor-version)
 | 
						||
                (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                    (make-instance 'xcb:xfixes:QueryVersion
 | 
						||
                                   :client-major-version 2
 | 
						||
                                   :client-minor-version 0))
 | 
						||
              (or (/= major-version 2) (/= minor-version 0))))
 | 
						||
    (error "[EXWM] The server does not support XFixes extension"))
 | 
						||
  ;; Check for Shape extension.
 | 
						||
  (when (or (= 0 (slot-value (xcb:get-extension-data exwm-cm--conn 'xcb:shape)
 | 
						||
                             'present))
 | 
						||
            (with-slots (major-version minor-version)
 | 
						||
                (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                    (make-instance 'xcb:shape:QueryVersion))
 | 
						||
              (or (/= major-version 1) (< minor-version 1))))
 | 
						||
    (error "[EXWM] The server does not support Shape extension"))
 | 
						||
  ;; Intern atoms.
 | 
						||
  (let ((atom-name "_NET_WM_WINDOW_OPACITY"))
 | 
						||
    (setq exwm-cm--_NET_WM_WINDOW_OPACITY
 | 
						||
          (slot-value (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                          (make-instance 'xcb:InternAtom
 | 
						||
                                         :only-if-exists 0
 | 
						||
                                         :name-len (length atom-name)
 | 
						||
                                         :name atom-name))
 | 
						||
                      'atom)))
 | 
						||
  (setq exwm-cm--background-atoms
 | 
						||
        (mapcar (lambda (atom-name)
 | 
						||
                  (slot-value (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
                                  (make-instance 'xcb:InternAtom
 | 
						||
                                                 :only-if-exists 0
 | 
						||
                                                 :name-len (length atom-name)
 | 
						||
                                                 :name atom-name))
 | 
						||
                              'atom))
 | 
						||
                exwm-cm--background-atom-names))
 | 
						||
  ;; Register CM.
 | 
						||
  (with-slots (owner)
 | 
						||
      (xcb:+request-unchecked+reply exwm-cm--conn
 | 
						||
          (make-instance 'xcb:GetSelectionOwner
 | 
						||
                         :selection xcb:Atom:_NET_WM_CM_S0))
 | 
						||
    (when (/= owner xcb:Window:None)
 | 
						||
      (error "[EXWM] Other compositing manager detected")))
 | 
						||
  (let ((id (xcb:generate-id exwm-cm--conn)))
 | 
						||
    (xcb:+request exwm-cm--conn
 | 
						||
        (make-instance 'xcb:CreateWindow
 | 
						||
                       :depth 0
 | 
						||
                       :wid id
 | 
						||
                       :parent exwm--root
 | 
						||
                       :x 0
 | 
						||
                       :y 0
 | 
						||
                       :width 1
 | 
						||
                       :height 1
 | 
						||
                       :border-width 0
 | 
						||
                       :class xcb:WindowClass:InputOnly
 | 
						||
                       :visual 0
 | 
						||
                       :value-mask xcb:CW:OverrideRedirect
 | 
						||
                       :override-redirect 1))
 | 
						||
    ;; Set _NET_WM_NAME.
 | 
						||
    (xcb:+request exwm-cm--conn
 | 
						||
        (make-instance 'xcb:ewmh:set-_NET_WM_NAME
 | 
						||
                       :window id
 | 
						||
                       :data "EXWM-CM"))
 | 
						||
    ;; Get the selection ownership.
 | 
						||
    (xcb:+request exwm-cm--conn
 | 
						||
        (make-instance 'xcb:SetSelectionOwner
 | 
						||
                       :owner id
 | 
						||
                       :selection xcb:Atom:_NET_WM_CM_S0
 | 
						||
                       :time xcb:Time:CurrentTime)))
 | 
						||
  ;; Attach event listeners.
 | 
						||
  (xcb:+event exwm-cm--conn 'xcb:MapNotify #'exwm-cm--on-MapNotify)
 | 
						||
  (xcb:+event exwm-cm--conn 'xcb:UnmapNotify #'exwm-cm--on-UnmapNotify)
 | 
						||
  (xcb:+event exwm-cm--conn 'xcb:CreateNotify #'exwm-cm--on-CreateNotify)
 | 
						||
  (xcb:+event exwm-cm--conn 'xcb:ConfigureNotify #'exwm-cm--on-ConfigureNotify)
 | 
						||
  (xcb:+event exwm-cm--conn 'xcb:DestroyNotify #'exwm-cm--on-DestroyNotify)
 | 
						||
  (xcb:+event exwm-cm--conn 'xcb:ReparentNotify #'exwm-cm--on-ReparentNotify)
 | 
						||
  (xcb:+event exwm-cm--conn 'xcb:CirculateNotify #'exwm-cm--on-CirculateNotify)
 | 
						||
  (xcb:+event exwm-cm--conn 'xcb:Expose #'exwm-cm--on-Expose)
 | 
						||
  (xcb:+event exwm-cm--conn 'xcb:PropertyNotify #'exwm-cm--on-PropertyNotify)
 | 
						||
  (xcb:+event exwm-cm--conn 'xcb:damage:Notify #'exwm-cm--on-DamageNotify)
 | 
						||
  (xcb:+event exwm-cm--conn 'xcb:shape:Notify #'exwm-cm--on-ShapeNotify)
 | 
						||
  ;; Scan the window tree.
 | 
						||
  (setq exwm-cm--hash (make-hash-table))
 | 
						||
  (exwm-cm--create-tree)
 | 
						||
  ;; Set up the root X window.
 | 
						||
  (setq exwm-cm--depth
 | 
						||
        (slot-value (car (slot-value (xcb:get-setup exwm-cm--conn) 'roots))
 | 
						||
                    'root-depth))
 | 
						||
  (with-slots (visual picture) (exwm-cm--xwin->attr exwm--root)
 | 
						||
    (setf picture (xcb:generate-id exwm-cm--conn))
 | 
						||
    (xcb:+request exwm-cm--conn
 | 
						||
        (make-instance 'xcb:render:CreatePicture
 | 
						||
                       :pid picture
 | 
						||
                       :drawable exwm--root
 | 
						||
                       :format (xcb:renderutil:find-visual-format
 | 
						||
                                (xcb:renderutil:query-formats exwm-cm--conn)
 | 
						||
                                visual)
 | 
						||
                       :value-mask xcb:render:CP:SubwindowMode
 | 
						||
                       :subwindowmode xcb:SubwindowMode:IncludeInferiors)))
 | 
						||
  (xcb:flush exwm-cm--conn)
 | 
						||
  ;; Paint once.
 | 
						||
  (exwm-cm--paint t))
 | 
						||
 | 
						||
(defun exwm-cm--exit ()
 | 
						||
  "Exit EXWM compositing manager."
 | 
						||
  (when exwm-cm--conn
 | 
						||
    (xcb:disconnect exwm-cm--conn)
 | 
						||
    (clrhash exwm-cm--hash)
 | 
						||
    (setq exwm-cm--hash nil
 | 
						||
          exwm-cm--conn nil
 | 
						||
          exwm-cm--buffer nil
 | 
						||
          exwm-cm--clip-changed t
 | 
						||
          exwm-cm--damages nil
 | 
						||
          exwm-cm--expose-rectangles nil
 | 
						||
          exwm-cm--background nil)))
 | 
						||
 | 
						||
(defun exwm-cm-enable ()
 | 
						||
  "Enable compositing support for EXWM."
 | 
						||
  (add-hook 'exwm-init-hook #'exwm-cm--init t)
 | 
						||
  (add-hook 'exwm-exit-hook #'exwm-cm--exit t))
 | 
						||
 | 
						||
(defun exwm-cm-start ()
 | 
						||
  "Start EXWM compositing manager."
 | 
						||
  (interactive)
 | 
						||
  (unless exwm-cm--conn
 | 
						||
    (exwm-cm--init)))
 | 
						||
 | 
						||
(defun exwm-cm-stop ()
 | 
						||
  "Stop EXWM compositing manager."
 | 
						||
  (interactive)
 | 
						||
  (exwm-cm--exit))
 | 
						||
 | 
						||
(defun exwm-cm-toggle ()
 | 
						||
  "Toggle the running state of EXWM compositing manager."
 | 
						||
  (interactive)
 | 
						||
  (if exwm-cm--conn
 | 
						||
      (exwm-cm-stop)
 | 
						||
    (exwm-cm-start)))
 | 
						||
 | 
						||
 | 
						||
 | 
						||
(provide 'exwm-cm)
 | 
						||
 | 
						||
;;; exwm-cm.el ends here
 |