diff options
Diffstat (limited to 'third_party/emacs/exwm/exwm.el')
-rw-r--r-- | third_party/emacs/exwm/exwm.el | 1019 |
1 files changed, 0 insertions, 1019 deletions
diff --git a/third_party/emacs/exwm/exwm.el b/third_party/emacs/exwm/exwm.el deleted file mode 100644 index fc96ac75098c..000000000000 --- a/third_party/emacs/exwm/exwm.el +++ /dev/null @@ -1,1019 +0,0 @@ -;;; exwm.el --- Emacs X Window Manager -*- lexical-binding: t -*- - -;; Copyright (C) 2015-2020 Free Software Foundation, Inc. - -;; Author: Chris Feng <chris.w.feng@gmail.com> -;; Maintainer: Chris Feng <chris.w.feng@gmail.com> -;; Version: 0.24 -;; Package-Requires: ((xelb "0.18")) -;; Keywords: unix -;; URL: https://github.com/ch11ng/exwm - -;; 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: - -;; Overview -;; -------- -;; EXWM (Emacs X Window Manager) is a full-featured tiling X window manager -;; for Emacs built on top of [XELB](https://github.com/ch11ng/xelb). -;; It features: -;; + Fully keyboard-driven operations -;; + Hybrid layout modes (tiling & stacking) -;; + Dynamic workspace support -;; + ICCCM/EWMH compliance -;; + (Optional) RandR (multi-monitor) support -;; + (Optional) Built-in system tray - -;; Installation & configuration -;; ---------------------------- -;; Here are the minimal steps to get EXWM working: -;; 1. Install XELB and EXWM, and make sure they are in `load-path'. -;; 2. In '~/.emacs', add following lines (please modify accordingly): -;; -;; (require 'exwm) -;; (require 'exwm-config) -;; (exwm-config-example) -;; -;; 3. Link or copy the file 'xinitrc' to '~/.xinitrc'. -;; 4. Launch EXWM in a console (e.g. tty1) with -;; -;; xinit -- vt01 -;; -;; You should additionally hide the menu-bar, tool-bar, etc to increase the -;; usable space. Please check the wiki (https://github.com/ch11ng/exwm/wiki) -;; for more detailed instructions on installation, configuration, usage, etc. - -;; References: -;; + dwm (http://dwm.suckless.org/) -;; + i3 wm (https://i3wm.org/) -;; + Also see references within each required library. - -;;; Code: - -(require 'server) -(require 'exwm-core) -(require 'exwm-workspace) -(require 'exwm-layout) -(require 'exwm-floating) -(require 'exwm-manage) -(require 'exwm-input) - -(defgroup exwm nil - "Emacs X Window Manager." - :tag "EXWM" - :version "25.3" - :group 'applications - :prefix "exwm-") - -(defcustom exwm-init-hook nil - "Normal hook run when EXWM has just finished initialization." - :type 'hook) - -(defcustom exwm-exit-hook nil - "Normal hook run just before EXWM exits." - :type 'hook) - -(defcustom exwm-update-class-hook nil - "Normal hook run when window class is updated." - :type 'hook) - -(defcustom exwm-update-title-hook nil - "Normal hook run when window title is updated." - :type 'hook) - -(defcustom exwm-blocking-subrs '(x-file-dialog x-popup-dialog x-select-font) - "Subrs (primitives) that would normally block EXWM." - :type '(repeat function)) - -(defcustom exwm-replace 'ask - "Whether to replace existing window manager." - :type '(radio (const :tag "Ask" ask) - (const :tag "Replace by default" t) - (const :tag "Do not replace" nil))) - -(defconst exwm--server-name "server-exwm" - "Name of the subordinate Emacs server.") - -(defvar exwm--server-process nil "Process of the subordinate Emacs server.") - -(defun exwm-reset () - "Reset the state of the selected window (non-fullscreen, line-mode, etc)." - (interactive) - (exwm--log) - (with-current-buffer (window-buffer) - (when (derived-mode-p 'exwm-mode) - (when (exwm-layout--fullscreen-p) - (exwm-layout-unset-fullscreen)) - ;; Force refresh - (exwm-layout--refresh) - (call-interactively #'exwm-input-grab-keyboard)))) - -;;;###autoload -(defun exwm-restart () - "Restart EXWM." - (interactive) - (exwm--log) - (when (exwm--confirm-kill-emacs "[EXWM] Restart? " 'no-check) - (let* ((attr (process-attributes (emacs-pid))) - (args (cdr (assq 'args attr))) - (ppid (cdr (assq 'ppid attr))) - (pargs (cdr (assq 'args (process-attributes ppid))))) - (cond - ((= ppid 1) - ;; The parent is the init process. This probably means this - ;; instance is an emacsclient. Anyway, start a control instance - ;; to manage the subsequent ones. - (call-process (car command-line-args)) - (kill-emacs)) - ((string= args pargs) - ;; This is a subordinate instance. Return a magic number to - ;; inform the parent (control instance) to start another one. - (kill-emacs ?R)) - (t - ;; This is the control instance. Keep starting subordinate - ;; instances until told to exit. - ;; Run `server-force-stop' if it exists. - (run-hooks 'kill-emacs-hook) - (with-temp-buffer - (while (= ?R (shell-command-on-region (point) (point) args)))) - (kill-emacs)))))) - -(defun exwm--update-desktop (xwin) - "Update _NET_WM_DESKTOP." - (exwm--log "#x%x" xwin) - (with-current-buffer (exwm--id->buffer xwin) - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:ewmh:get-_NET_WM_DESKTOP - :window xwin))) - desktop) - (when reply - (setq desktop (slot-value reply 'value)) - (cond - ((eq desktop 4294967295.) - (unless (or (not exwm--floating-frame) - (eq exwm--frame exwm-workspace--current) - (and exwm--desktop - (= desktop exwm--desktop))) - (exwm-layout--show xwin (frame-root-window exwm--floating-frame))) - (setq exwm--desktop desktop)) - ((and desktop - (< desktop (exwm-workspace--count)) - (if exwm--desktop - (/= desktop exwm--desktop) - (/= desktop (exwm-workspace--position exwm--frame)))) - (exwm-workspace-move-window desktop xwin)) - (t - (exwm-workspace--set-desktop xwin))))))) - -(defun exwm--update-window-type (id &optional force) - "Update _NET_WM_WINDOW_TYPE." - (exwm--log "#x%x" id) - (with-current-buffer (exwm--id->buffer id) - (unless (and exwm-window-type (not force)) - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:ewmh:get-_NET_WM_WINDOW_TYPE - :window id)))) - (when reply ;nil when destroyed - (setq exwm-window-type (append (slot-value reply 'value) nil))))))) - -(defun exwm--update-class (id &optional force) - "Update WM_CLASS." - (exwm--log "#x%x" id) - (with-current-buffer (exwm--id->buffer id) - (unless (and exwm-instance-name exwm-class-name (not force)) - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:icccm:get-WM_CLASS :window id)))) - (when reply ;nil when destroyed - (setq exwm-instance-name (slot-value reply 'instance-name) - exwm-class-name (slot-value reply 'class-name)) - (when (and exwm-instance-name exwm-class-name) - (run-hooks 'exwm-update-class-hook))))))) - -(defun exwm--update-utf8-title (id &optional force) - "Update _NET_WM_NAME." - (exwm--log "#x%x" id) - (with-current-buffer (exwm--id->buffer id) - (when (or force (not exwm-title)) - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:ewmh:get-_NET_WM_NAME :window id)))) - (when reply ;nil when destroyed - (setq exwm-title (slot-value reply 'value)) - (when exwm-title - (setq exwm--title-is-utf8 t) - (run-hooks 'exwm-update-title-hook))))))) - -(defun exwm--update-ctext-title (id &optional force) - "Update WM_NAME." - (exwm--log "#x%x" id) - (with-current-buffer (exwm--id->buffer id) - (unless (or exwm--title-is-utf8 - (and exwm-title (not force))) - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:icccm:get-WM_NAME :window id)))) - (when reply ;nil when destroyed - (setq exwm-title (slot-value reply 'value)) - (when exwm-title - (run-hooks 'exwm-update-title-hook))))))) - -(defun exwm--update-title (id) - "Update _NET_WM_NAME or WM_NAME." - (exwm--log "#x%x" id) - (exwm--update-utf8-title id) - (exwm--update-ctext-title id)) - -(defun exwm--update-transient-for (id &optional force) - "Update WM_TRANSIENT_FOR." - (exwm--log "#x%x" id) - (with-current-buffer (exwm--id->buffer id) - (unless (and exwm-transient-for (not force)) - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:icccm:get-WM_TRANSIENT_FOR - :window id)))) - (when reply ;nil when destroyed - (setq exwm-transient-for (slot-value reply 'value))))))) - -(defun exwm--update-normal-hints (id &optional force) - "Update WM_NORMAL_HINTS." - (exwm--log "#x%x" id) - (with-current-buffer (exwm--id->buffer id) - (unless (and (not force) - (or exwm--normal-hints-x exwm--normal-hints-y - exwm--normal-hints-width exwm--normal-hints-height - exwm--normal-hints-min-width exwm--normal-hints-min-height - exwm--normal-hints-max-width exwm--normal-hints-max-height - ;; FIXME: other fields - )) - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:icccm:get-WM_NORMAL_HINTS - :window id)))) - (when (and reply (slot-value reply 'flags)) ;nil when destroyed - (with-slots (flags x y width height min-width min-height max-width - max-height base-width base-height ;; win-gravity - ) - reply - (unless (= 0 (logand flags xcb:icccm:WM_SIZE_HINTS:USPosition)) - (setq exwm--normal-hints-x x exwm--normal-hints-y y)) - (unless (= 0 (logand flags xcb:icccm:WM_SIZE_HINTS:USSize)) - (setq exwm--normal-hints-width width - exwm--normal-hints-height height)) - (unless (= 0 (logand flags xcb:icccm:WM_SIZE_HINTS:PMinSize)) - (setq exwm--normal-hints-min-width min-width - exwm--normal-hints-min-height min-height)) - (unless (= 0 (logand flags xcb:icccm:WM_SIZE_HINTS:PMaxSize)) - (setq exwm--normal-hints-max-width max-width - exwm--normal-hints-max-height max-height)) - (unless (or exwm--normal-hints-min-width - (= 0 (logand flags xcb:icccm:WM_SIZE_HINTS:PBaseSize))) - (setq exwm--normal-hints-min-width base-width - exwm--normal-hints-min-height base-height)) - ;; (unless (= 0 (logand flags xcb:icccm:WM_SIZE_HINTS:PWinGravity)) - ;; (setq exwm--normal-hints-win-gravity win-gravity)) - (setq exwm--fixed-size - (and exwm--normal-hints-min-width - exwm--normal-hints-min-height - exwm--normal-hints-max-width - exwm--normal-hints-max-height - (/= 0 exwm--normal-hints-min-width) - (/= 0 exwm--normal-hints-min-height) - (= exwm--normal-hints-min-width - exwm--normal-hints-max-width) - (= exwm--normal-hints-min-height - exwm--normal-hints-max-height))))))))) - -(defun exwm--update-hints (id &optional force) - "Update WM_HINTS." - (exwm--log "#x%x" id) - (with-current-buffer (exwm--id->buffer id) - (unless (and (not force) exwm--hints-input exwm--hints-urgency) - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:icccm:get-WM_HINTS :window id)))) - (when (and reply (slot-value reply 'flags)) ;nil when destroyed - (with-slots (flags input initial-state) reply - (when flags - (unless (= 0 (logand flags xcb:icccm:WM_HINTS:InputHint)) - (setq exwm--hints-input (when input (= 1 input)))) - (unless (= 0 (logand flags xcb:icccm:WM_HINTS:StateHint)) - (setq exwm-state initial-state)) - (unless (= 0 (logand flags xcb:icccm:WM_HINTS:UrgencyHint)) - (setq exwm--hints-urgency t)))) - (when (and exwm--hints-urgency - (not (eq exwm--frame exwm-workspace--current))) - (unless (frame-parameter exwm--frame 'exwm-urgency) - (set-frame-parameter exwm--frame 'exwm-urgency t) - (setq exwm-workspace--switch-history-outdated t)))))))) - -(defun exwm--update-protocols (id &optional force) - "Update WM_PROTOCOLS." - (exwm--log "#x%x" id) - (with-current-buffer (exwm--id->buffer id) - (unless (and exwm--protocols (not force)) - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:icccm:get-WM_PROTOCOLS - :window id)))) - (when reply ;nil when destroyed - (setq exwm--protocols (append (slot-value reply 'value) nil))))))) - -(defun exwm--update-struts-legacy (id) - "Update _NET_WM_STRUT." - (exwm--log "#x%x" id) - (let ((pair (assq id exwm-workspace--id-struts-alist)) - reply struts) - (unless (and pair (< 4 (length (cdr pair)))) - (setq reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:ewmh:get-_NET_WM_STRUT - :window id))) - (when reply - (setq struts (slot-value reply 'value)) - (if pair - (setcdr pair struts) - (push (cons id struts) exwm-workspace--id-struts-alist)) - (exwm-workspace--update-struts)) - ;; Update workareas. - (exwm-workspace--update-workareas) - ;; Update workspaces. - (dolist (f exwm-workspace--list) - (exwm-workspace--set-fullscreen f))))) - -(defun exwm--update-struts-partial (id) - "Update _NET_WM_STRUT_PARTIAL." - (exwm--log "#x%x" id) - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:ewmh:get-_NET_WM_STRUT_PARTIAL - :window id))) - struts pair) - (when reply - (setq struts (slot-value reply 'value) - pair (assq id exwm-workspace--id-struts-alist)) - (if pair - (setcdr pair struts) - (push (cons id struts) exwm-workspace--id-struts-alist)) - (exwm-workspace--update-struts)) - ;; Update workareas. - (exwm-workspace--update-workareas) - ;; Update workspaces. - (dolist (f exwm-workspace--list) - (exwm-workspace--set-fullscreen f)))) - -(defun exwm--update-struts (id) - "Update _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT." - (exwm--log "#x%x" id) - (exwm--update-struts-partial id) - (exwm--update-struts-legacy id)) - -(defun exwm--on-PropertyNotify (data _synthetic) - "Handle PropertyNotify event." - (let ((obj (make-instance 'xcb:PropertyNotify)) - atom id buffer) - (xcb:unmarshal obj data) - (setq id (slot-value obj 'window) - atom (slot-value obj 'atom)) - (exwm--log "atom=%s(%s)" (x-get-atom-name atom exwm-workspace--current) atom) - (setq buffer (exwm--id->buffer id)) - (if (not (buffer-live-p buffer)) - ;; Properties of unmanaged X windows. - (cond ((= atom xcb:Atom:_NET_WM_STRUT) - (exwm--update-struts-legacy id)) - ((= atom xcb:Atom:_NET_WM_STRUT_PARTIAL) - (exwm--update-struts-partial id))) - (with-current-buffer buffer - (cond ((= atom xcb:Atom:_NET_WM_WINDOW_TYPE) - (exwm--update-window-type id t)) - ((= atom xcb:Atom:WM_CLASS) - (exwm--update-class id t)) - ((= atom xcb:Atom:_NET_WM_NAME) - (exwm--update-utf8-title id t)) - ((= atom xcb:Atom:WM_NAME) - (exwm--update-ctext-title id t)) - ((= atom xcb:Atom:WM_TRANSIENT_FOR) - (exwm--update-transient-for id t)) - ((= atom xcb:Atom:WM_NORMAL_HINTS) - (exwm--update-normal-hints id t)) - ((= atom xcb:Atom:WM_HINTS) - (exwm--update-hints id t)) - ((= atom xcb:Atom:WM_PROTOCOLS) - (exwm--update-protocols id t)) - ((= atom xcb:Atom:_NET_WM_USER_TIME)) ;ignored - (t - (exwm--log "Unhandled: %s(%d)" - (x-get-atom-name atom exwm-workspace--current) - atom))))))) - -(defun exwm--on-ClientMessage (raw-data _synthetic) - "Handle ClientMessage event." - (let ((obj (make-instance 'xcb:ClientMessage)) - type id data) - (xcb:unmarshal obj raw-data) - (setq type (slot-value obj 'type) - id (slot-value obj 'window) - data (slot-value (slot-value obj 'data) 'data32)) - (exwm--log "atom=%s(%s)" (x-get-atom-name type exwm-workspace--current) - type) - (cond - ;; _NET_NUMBER_OF_DESKTOPS. - ((= type xcb:Atom:_NET_NUMBER_OF_DESKTOPS) - (let ((current (exwm-workspace--count)) - (requested (elt data 0))) - ;; Only allow increasing/decreasing the workspace number by 1. - (cond - ((< current requested) - (make-frame)) - ((and (> current requested) - (> current 1)) - (let ((frame (car (last exwm-workspace--list)))) - (exwm-workspace--get-remove-frame-next-workspace frame) - (delete-frame frame)))))) - ;; _NET_CURRENT_DESKTOP. - ((= type xcb:Atom:_NET_CURRENT_DESKTOP) - (exwm-workspace-switch (elt data 0))) - ;; _NET_ACTIVE_WINDOW. - ((= type xcb:Atom:_NET_ACTIVE_WINDOW) - (let ((buffer (exwm--id->buffer id)) - iconic window) - (when (buffer-live-p buffer) - (with-current-buffer buffer - (when (eq exwm--frame exwm-workspace--current) - (if exwm--floating-frame - (select-frame exwm--floating-frame) - (setq iconic (exwm-layout--iconic-state-p)) - (when iconic - ;; State change: iconic => normal. - (set-window-buffer (frame-selected-window exwm--frame) - (current-buffer))) - ;; Focus transfer. - (setq window (get-buffer-window nil t)) - (when (or iconic - (not (eq window (selected-window)))) - (select-window window)))))))) - ;; _NET_CLOSE_WINDOW. - ((= type xcb:Atom:_NET_CLOSE_WINDOW) - (let ((buffer (exwm--id->buffer id))) - (when (buffer-live-p buffer) - (exwm--defer 0 #'kill-buffer buffer)))) - ;; _NET_WM_MOVERESIZE - ((= type xcb:Atom:_NET_WM_MOVERESIZE) - (let ((direction (elt data 2)) - (buffer (exwm--id->buffer id))) - (unless (and buffer - (not (buffer-local-value 'exwm--floating-frame buffer))) - (cond ((= direction - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_KEYBOARD) - ;; FIXME - ) - ((= direction - xcb:ewmh:_NET_WM_MOVERESIZE_MOVE_KEYBOARD) - ;; FIXME - ) - ((= direction xcb:ewmh:_NET_WM_MOVERESIZE_CANCEL) - (exwm-floating--stop-moveresize)) - ;; In case it's a workspace frame. - ((and (not buffer) - (catch 'break - (dolist (f exwm-workspace--list) - (when (or (eq id (frame-parameter f 'exwm-outer-id)) - (eq id (frame-parameter f 'exwm-id))) - (throw 'break t))) - nil))) - (t - ;; In case it's a floating frame, - ;; move the corresponding X window instead. - (unless buffer - (catch 'break - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (when - (and exwm--floating-frame - (or (eq id - (frame-parameter exwm--floating-frame - 'exwm-outer-id)) - (eq id - (frame-parameter exwm--floating-frame - 'exwm-id)))) - (setq id exwm--id) - (throw 'break nil)))))) - ;; Start to move it. - (exwm-floating--start-moveresize id direction)))))) - ;; _NET_REQUEST_FRAME_EXTENTS - ((= type xcb:Atom:_NET_REQUEST_FRAME_EXTENTS) - (let ((buffer (exwm--id->buffer id)) - top btm) - (if (or (not buffer) - (not (buffer-local-value 'exwm--floating-frame buffer))) - (setq top 0 - btm 0) - (setq top (window-header-line-height) - btm (window-mode-line-height))) - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_FRAME_EXTENTS - :window id - :left 0 - :right 0 - :top top - :bottom btm))) - (xcb:flush exwm--connection)) - ;; _NET_WM_DESKTOP. - ((= type xcb:Atom:_NET_WM_DESKTOP) - (let ((buffer (exwm--id->buffer id))) - (when (buffer-live-p buffer) - (exwm-workspace-move-window (elt data 0) id)))) - ;; _NET_WM_STATE - ((= type xcb:Atom:_NET_WM_STATE) - (let ((action (elt data 0)) - (props (list (elt data 1) (elt data 2))) - (buffer (exwm--id->buffer id)) - props-new) - ;; only support _NET_WM_STATE_FULLSCREEN / _NET_WM_STATE_ADD for frames - (when (and (not buffer) - (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN props) - (= action xcb:ewmh:_NET_WM_STATE_ADD)) - (xcb:+request - exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_STATE - :window id - :data (vector xcb:Atom:_NET_WM_STATE_FULLSCREEN))) - (xcb:flush exwm--connection)) - (when buffer ;ensure it's managed - (with-current-buffer buffer - ;; _NET_WM_STATE_FULLSCREEN - (when (or (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN props) - (memq xcb:Atom:_NET_WM_STATE_ABOVE props)) - (cond ((= action xcb:ewmh:_NET_WM_STATE_ADD) - (unless (exwm-layout--fullscreen-p) - (exwm-layout-set-fullscreen id)) - (push xcb:Atom:_NET_WM_STATE_FULLSCREEN props-new)) - ((= action xcb:ewmh:_NET_WM_STATE_REMOVE) - (when (exwm-layout--fullscreen-p) - (exwm-layout-unset-fullscreen id))) - ((= action xcb:ewmh:_NET_WM_STATE_TOGGLE) - (if (exwm-layout--fullscreen-p) - (exwm-layout-unset-fullscreen id) - (exwm-layout-set-fullscreen id) - (push xcb:Atom:_NET_WM_STATE_FULLSCREEN props-new))))) - ;; _NET_WM_STATE_DEMANDS_ATTENTION - ;; FIXME: check (may require other properties set) - (when (memq xcb:Atom:_NET_WM_STATE_DEMANDS_ATTENTION props) - (when (= action xcb:ewmh:_NET_WM_STATE_ADD) - (unless (eq exwm--frame exwm-workspace--current) - (set-frame-parameter exwm--frame 'exwm-urgency t) - (setq exwm-workspace--switch-history-outdated t))) - ;; xcb:ewmh:_NET_WM_STATE_REMOVE? - ;; xcb:ewmh:_NET_WM_STATE_TOGGLE? - ) - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_STATE - :window id :data (vconcat props-new))) - (xcb:flush exwm--connection))))) - ((= type xcb:Atom:WM_PROTOCOLS) - (let ((type (elt data 0))) - (cond ((= type xcb:Atom:_NET_WM_PING) - (setq exwm-manage--ping-lock nil)) - (t (exwm--log "Unhandled WM_PROTOCOLS of type: %d" type))))) - ((= type xcb:Atom:WM_CHANGE_STATE) - (let ((buffer (exwm--id->buffer id))) - (when (and (buffer-live-p buffer) - (= (elt data 0) xcb:icccm:WM_STATE:IconicState)) - (with-current-buffer buffer - (if exwm--floating-frame - (call-interactively #'exwm-floating-hide) - (bury-buffer)))))) - (t - (exwm--log "Unhandled: %s(%d)" - (x-get-atom-name type exwm-workspace--current) type))))) - -(defun exwm--on-SelectionClear (data _synthetic) - "Handle SelectionClear events." - (exwm--log) - (let ((obj (make-instance 'xcb:SelectionClear)) - owner selection) - (xcb:unmarshal obj data) - (setq owner (slot-value obj 'owner) - selection (slot-value obj 'selection)) - (when (and (eq owner exwm--wmsn-window) - (eq selection xcb:Atom:WM_S0)) - (exwm-exit)))) - -(defun exwm--init-icccm-ewmh () - "Initialize ICCCM/EWMH support." - (exwm--log) - ;; Handle PropertyNotify event - (xcb:+event exwm--connection 'xcb:PropertyNotify #'exwm--on-PropertyNotify) - ;; Handle relevant client messages - (xcb:+event exwm--connection 'xcb:ClientMessage #'exwm--on-ClientMessage) - ;; Handle SelectionClear - (xcb:+event exwm--connection 'xcb:SelectionClear #'exwm--on-SelectionClear) - ;; Set _NET_SUPPORTED - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_SUPPORTED - :window exwm--root - :data (vector - ;; Root windows properties. - xcb:Atom:_NET_SUPPORTED - xcb:Atom:_NET_CLIENT_LIST - xcb:Atom:_NET_CLIENT_LIST_STACKING - xcb:Atom:_NET_NUMBER_OF_DESKTOPS - xcb:Atom:_NET_DESKTOP_GEOMETRY - xcb:Atom:_NET_DESKTOP_VIEWPORT - xcb:Atom:_NET_CURRENT_DESKTOP - ;; xcb:Atom:_NET_DESKTOP_NAMES - xcb:Atom:_NET_ACTIVE_WINDOW - ;; xcb:Atom:_NET_WORKAREA - xcb:Atom:_NET_SUPPORTING_WM_CHECK - ;; xcb:Atom:_NET_VIRTUAL_ROOTS - ;; xcb:Atom:_NET_DESKTOP_LAYOUT - ;; xcb:Atom:_NET_SHOWING_DESKTOP - - ;; Other root window messages. - xcb:Atom:_NET_CLOSE_WINDOW - ;; xcb:Atom:_NET_MOVERESIZE_WINDOW - xcb:Atom:_NET_WM_MOVERESIZE - ;; xcb:Atom:_NET_RESTACK_WINDOW - xcb:Atom:_NET_REQUEST_FRAME_EXTENTS - - ;; Application window properties. - xcb:Atom:_NET_WM_NAME - ;; xcb:Atom:_NET_WM_VISIBLE_NAME - ;; xcb:Atom:_NET_WM_ICON_NAME - ;; xcb:Atom:_NET_WM_VISIBLE_ICON_NAME - xcb:Atom:_NET_WM_DESKTOP - ;; - xcb:Atom:_NET_WM_WINDOW_TYPE - ;; xcb:Atom:_NET_WM_WINDOW_TYPE_DESKTOP - xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK - xcb:Atom:_NET_WM_WINDOW_TYPE_TOOLBAR - xcb:Atom:_NET_WM_WINDOW_TYPE_MENU - xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY - xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH - xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG - xcb:Atom:_NET_WM_WINDOW_TYPE_DROPDOWN_MENU - xcb:Atom:_NET_WM_WINDOW_TYPE_POPUP_MENU - xcb:Atom:_NET_WM_WINDOW_TYPE_TOOLTIP - xcb:Atom:_NET_WM_WINDOW_TYPE_NOTIFICATION - xcb:Atom:_NET_WM_WINDOW_TYPE_COMBO - xcb:Atom:_NET_WM_WINDOW_TYPE_DND - xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL - ;; - xcb:Atom:_NET_WM_STATE - ;; xcb:Atom:_NET_WM_STATE_MODAL - ;; xcb:Atom:_NET_WM_STATE_STICKY - ;; xcb:Atom:_NET_WM_STATE_MAXIMIZED_VERT - ;; xcb:Atom:_NET_WM_STATE_MAXIMIZED_HORZ - ;; xcb:Atom:_NET_WM_STATE_SHADED - ;; xcb:Atom:_NET_WM_STATE_SKIP_TASKBAR - ;; xcb:Atom:_NET_WM_STATE_SKIP_PAGER - xcb:Atom:_NET_WM_STATE_HIDDEN - xcb:Atom:_NET_WM_STATE_FULLSCREEN - ;; xcb:Atom:_NET_WM_STATE_ABOVE - ;; xcb:Atom:_NET_WM_STATE_BELOW - xcb:Atom:_NET_WM_STATE_DEMANDS_ATTENTION - ;; xcb:Atom:_NET_WM_STATE_FOCUSED - ;; - xcb:Atom:_NET_WM_ALLOWED_ACTIONS - xcb:Atom:_NET_WM_ACTION_MOVE - xcb:Atom:_NET_WM_ACTION_RESIZE - xcb:Atom:_NET_WM_ACTION_MINIMIZE - ;; xcb:Atom:_NET_WM_ACTION_SHADE - ;; xcb:Atom:_NET_WM_ACTION_STICK - ;; xcb:Atom:_NET_WM_ACTION_MAXIMIZE_HORZ - ;; xcb:Atom:_NET_WM_ACTION_MAXIMIZE_VERT - xcb:Atom:_NET_WM_ACTION_FULLSCREEN - xcb:Atom:_NET_WM_ACTION_CHANGE_DESKTOP - xcb:Atom:_NET_WM_ACTION_CLOSE - ;; xcb:Atom:_NET_WM_ACTION_ABOVE - ;; xcb:Atom:_NET_WM_ACTION_BELOW - ;; - xcb:Atom:_NET_WM_STRUT - xcb:Atom:_NET_WM_STRUT_PARTIAL - ;; xcb:Atom:_NET_WM_ICON_GEOMETRY - ;; xcb:Atom:_NET_WM_ICON - xcb:Atom:_NET_WM_PID - ;; xcb:Atom:_NET_WM_HANDLED_ICONS - ;; xcb:Atom:_NET_WM_USER_TIME - ;; xcb:Atom:_NET_WM_USER_TIME_WINDOW - xcb:Atom:_NET_FRAME_EXTENTS - ;; xcb:Atom:_NET_WM_OPAQUE_REGION - ;; xcb:Atom:_NET_WM_BYPASS_COMPOSITOR - - ;; Window manager protocols. - xcb:Atom:_NET_WM_PING - ;; xcb:Atom:_NET_WM_SYNC_REQUEST - ;; xcb:Atom:_NET_WM_FULLSCREEN_MONITORS - - ;; Other properties. - xcb:Atom:_NET_WM_FULL_PLACEMENT))) - ;; Create a child window for setting _NET_SUPPORTING_WM_CHECK - (let ((new-id (xcb:generate-id exwm--connection))) - (setq exwm--guide-window new-id) - (xcb:+request exwm--connection - (make-instance 'xcb:CreateWindow - :depth 0 - :wid new-id - :parent exwm--root - :x -1 - :y -1 - :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. Must be set to the name of the window manager, as - ;; required by wm-spec. - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window new-id :data "EXWM")) - (dolist (i (list exwm--root new-id)) - ;; Set _NET_SUPPORTING_WM_CHECK - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_SUPPORTING_WM_CHECK - :window i :data new-id)))) - ;; Set _NET_DESKTOP_VIEWPORT (we don't support large desktop). - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT - :window exwm--root - :data [0 0])) - (xcb:flush exwm--connection)) - -(defun exwm--wmsn-acquire (replace) - "Acquire the WM_Sn selection. - -REPLACE specifies what to do in case there already is a window -manager. If t, replace it, if nil, abort and ask the user if `ask'." - (exwm--log "%s" replace) - (with-slots (owner) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetSelectionOwner - :selection xcb:Atom:WM_S0)) - (when (/= owner xcb:Window:None) - (when (eq replace 'ask) - (setq replace (yes-or-no-p "Replace existing window manager? "))) - (when (not replace) - (user-error "Other window manager detected"))) - (let ((new-owner (xcb:generate-id exwm--connection))) - (xcb:+request exwm--connection - (make-instance 'xcb:CreateWindow - :depth 0 - :wid new-owner - :parent exwm--root - :x -1 - :y -1 - :width 1 - :height 1 - :border-width 0 - :class xcb:WindowClass:CopyFromParent - :visual 0 - :value-mask 0 - :override-redirect 0)) - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window new-owner :data "EXWM: exwm--wmsn-window")) - (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:SetSelectionOwner - :selection xcb:Atom:WM_S0 - :owner new-owner - :time xcb:Time:CurrentTime)) - (with-slots (owner) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetSelectionOwner - :selection xcb:Atom:WM_S0)) - (unless (eq owner new-owner) - (error "Could not acquire ownership of WM selection"))) - ;; Wait for the other window manager to terminate. - (when (/= owner xcb:Window:None) - (let (reply) - (cl-dotimes (i exwm--wmsn-acquire-timeout) - (setq reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetGeometry :drawable owner))) - (when (not reply) - (cl-return)) - (message "Waiting for other window manager to quit... %ds" i) - (sleep-for 1)) - (when reply - (error "Other window manager did not release selection in time")))) - ;; announce - (let* ((cmd (make-instance 'xcb:ClientMessageData - :data32 (vector xcb:Time:CurrentTime - xcb:Atom:WM_S0 - new-owner - 0 - 0))) - (cm (make-instance 'xcb:ClientMessage - :window exwm--root - :format 32 - :type xcb:Atom:MANAGER - :data cmd)) - (se (make-instance 'xcb:SendEvent - :propagate 0 - :destination exwm--root - :event-mask xcb:EventMask:NoEvent - :event (xcb:marshal cm exwm--connection)))) - (xcb:+request exwm--connection se)) - (setq exwm--wmsn-window new-owner)))) - -;;;###autoload -(cl-defun exwm-init (&optional frame) - "Initialize EXWM." - (interactive) - (exwm--log "%s" frame) - (if frame - ;; The frame might not be selected if it's created by emacslicnet. - (select-frame-set-input-focus frame) - (setq frame (selected-frame))) - (when (not (eq 'x (framep frame))) - (message "[EXWM] Not running under X environment") - (cl-return-from exwm-init)) - (when exwm--connection - (exwm--log "EXWM already running") - (cl-return-from exwm-init)) - (condition-case err - (progn - (exwm-enable 'undo) ;never initialize again - (setq exwm--connection (xcb:connect)) - (set-process-query-on-exit-flag (slot-value exwm--connection 'process) - nil) ;prevent query message on exit - (setq exwm--root - (slot-value (car (slot-value - (xcb:get-setup exwm--connection) 'roots)) - 'root)) - ;; Initialize ICCCM/EWMH support - (xcb:icccm:init exwm--connection t) - (xcb:ewmh:init exwm--connection t) - ;; Try to register window manager selection. - (exwm--wmsn-acquire exwm-replace) - (when (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window exwm--root - :value-mask xcb:CW:EventMask - :event-mask - xcb:EventMask:SubstructureRedirect)) - (error "Other window manager is running")) - ;; Disable some features not working well with EXWM - (setq use-dialog-box nil - confirm-kill-emacs #'exwm--confirm-kill-emacs) - (exwm--lock) - (exwm--init-icccm-ewmh) - (exwm-layout--init) - (exwm-floating--init) - (exwm-manage--init) - (exwm-workspace--init) - (exwm-input--init) - (exwm--unlock) - (exwm-workspace--post-init) - (exwm-input--post-init) - (run-hooks 'exwm-init-hook) - ;; Manage existing windows - (exwm-manage--scan)) - (user-error) - ((quit error) - (exwm-exit) - ;; Rethrow error - (warn "[EXWM] EXWM fails to start (%s: %s)" (car err) (cdr err))))) - - -;;;###autoload -(defun exwm-exit () - "Exit EXWM." - (interactive) - (exwm--log) - (run-hooks 'exwm-exit-hook) - (setq confirm-kill-emacs nil) - ;; Exit modules. - (exwm-input--exit) - (exwm-manage--exit) - (exwm-workspace--exit) - (exwm-floating--exit) - (exwm-layout--exit) - (when exwm--connection - (xcb:flush exwm--connection) - (xcb:disconnect exwm--connection)) - (setq exwm--connection nil)) - -;;;###autoload -(defun exwm-enable (&optional undo) - "Enable/Disable EXWM." - (exwm--log "%s" undo) - (pcase undo - (`undo ;prevent reinitialization - (remove-hook 'window-setup-hook #'exwm-init) - (remove-hook 'after-make-frame-functions #'exwm-init)) - (`undo-all ;attempt to revert everything - (remove-hook 'window-setup-hook #'exwm-init) - (remove-hook 'after-make-frame-functions #'exwm-init) - (remove-hook 'kill-emacs-hook #'exwm--server-stop) - (dolist (i exwm-blocking-subrs) - (advice-remove i #'exwm--server-eval-at))) - (_ ;enable EXWM - (setq frame-resize-pixelwise t ;mandatory; before init - window-resize-pixelwise t) - ;; Ignore unrecognized command line arguments. This can be helpful - ;; when EXWM is launched by some session manager. - (push #'vector command-line-functions) - ;; In case EXWM is to be started from a graphical Emacs instance. - (add-hook 'window-setup-hook #'exwm-init t) - ;; In case EXWM is to be started with emacsclient. - (add-hook 'after-make-frame-functions #'exwm-init t) - ;; Manage the subordinate Emacs server. - (add-hook 'kill-emacs-hook #'exwm--server-stop) - (dolist (i exwm-blocking-subrs) - (advice-add i :around #'exwm--server-eval-at))))) - -(defun exwm--server-stop () - "Stop the subordinate Emacs server." - (exwm--log) - (server-force-delete exwm--server-name) - (when exwm--server-process - (delete-process exwm--server-process) - (setq exwm--server-process nil))) - -(defun exwm--server-eval-at (&rest args) - "Wrapper of `server-eval-at' used to advice subrs." - ;; Start the subordinate Emacs server if it's not alive - (exwm--log "%s" args) - (unless (server-running-p exwm--server-name) - (when exwm--server-process (delete-process exwm--server-process)) - (setq exwm--server-process - (start-process exwm--server-name - nil - (car command-line-args) ;The executable file - "-d" (frame-parameter nil 'display) - "-Q" - (concat "--daemon=" exwm--server-name) - "--eval" - ;; Create an invisible frame - "(make-frame '((window-system . x) (visibility)))")) - (while (not (server-running-p exwm--server-name)) - (sit-for 0.001))) - (server-eval-at - exwm--server-name - `(progn (select-frame (car (frame-list))) - (let ((result ,(nconc (list (make-symbol (subr-name (car args)))) - (cdr args)))) - (pcase (type-of result) - ;; Return the name of a buffer - (`buffer (buffer-name result)) - ;; We blindly convert all font objects to their XLFD names. This - ;; might cause problems of course, but it still has a chance to - ;; work (whereas directly passing font objects would merely - ;; raise errors). - ((or `font-entity `font-object `font-spec) - (font-xlfd-name result)) - ;; Passing following types makes little sense - ((or `compiled-function `finalizer `frame `hash-table `marker - `overlay `process `window `window-configuration)) - ;; Passing the name of a subr - (`subr (make-symbol (subr-name result))) - ;; For other types, return the value as-is. - (t result)))))) - -(defun exwm--confirm-kill-emacs (prompt &optional force) - "Confirm before exiting Emacs." - (exwm--log) - (when (cond - ((and force (not (eq force 'no-check))) - ;; Force killing Emacs. - t) - ((or (eq force 'no-check) (not exwm--id-buffer-alist)) - ;; Check if there's any unsaved file. - (pcase (catch 'break - (let ((kill-emacs-query-functions - (append kill-emacs-query-functions - (list (lambda () - (throw 'break 'break)))))) - (save-buffers-kill-emacs))) - (`break (y-or-n-p prompt)) - (x x))) - (t - (yes-or-no-p (format "[EXWM] %d window(s) will be destroyed. %s" - (length exwm--id-buffer-alist) prompt)))) - ;; Run `kill-emacs-hook' (`server-force-stop' excluded) before Emacs - ;; frames are unmapped so that errors (if any) can be visible. - (if (memq #'server-force-stop kill-emacs-hook) - (progn - (setq kill-emacs-hook (delq #'server-force-stop kill-emacs-hook)) - (run-hooks 'kill-emacs-hook) - (setq kill-emacs-hook (list #'server-force-stop))) - (run-hooks 'kill-emacs-hook) - (setq kill-emacs-hook nil)) - ;; Exit each module, destroying all resources created by this connection. - (exwm-exit) - ;; Set the return value. - t)) - - - -(provide 'exwm) - -;;; exwm.el ends here |