2015-07-17 13:16:08 +02:00
|
|
|
|
;;; exwm.el --- Emacs X Window Manager -*- lexical-binding: t -*-
|
|
|
|
|
|
2016-02-02 15:33:58 +01:00
|
|
|
|
;; Copyright (C) 2015-2016 Free Software Foundation, Inc.
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
;; Author: Chris Feng <chris.w.feng@gmail.com>
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; Maintainer: Chris Feng <chris.w.feng@gmail.com>
|
2016-08-14 06:27:14 +02:00
|
|
|
|
;; Version: 0.9
|
|
|
|
|
;; Package-Requires: ((xelb "0.11"))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; Keywords: unix
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; URL: https://github.com/ch11ng/exwm
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; This file is part of GNU Emacs.
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; 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.
|
|
|
|
|
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; 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
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;; Overview
|
|
|
|
|
;; --------
|
2016-07-19 04:36:14 +02:00
|
|
|
|
;; 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:
|
2016-02-19 10:12:43 +01:00
|
|
|
|
;; + Fully keyboard-driven operations
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; + Hybrid layout modes (tiling & stacking)
|
2016-07-19 04:36:14 +02:00
|
|
|
|
;; + Dynamic workspace support
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; + ICCCM/EWMH compliance
|
2016-07-19 04:36:14 +02:00
|
|
|
|
;; + (Optional) RandR (multi-monitor) support
|
2016-08-15 16:59:48 +02:00
|
|
|
|
;; + (Optional) Built-in compositing manager
|
|
|
|
|
;; + (Optional) Built-in system tray
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
;; Installation & configuration
|
|
|
|
|
;; ----------------------------
|
|
|
|
|
;; Here are the minimal steps to get EXWM working:
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; 1. Install XELB and EXWM, and make sure they are in `load-path'.
|
|
|
|
|
;; 2. In '~/.emacs', add following lines (please modify accordingly):
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;;
|
|
|
|
|
;; (require 'exwm)
|
2015-10-28 11:55:49 +01:00
|
|
|
|
;; (require 'exwm-config)
|
|
|
|
|
;; (exwm-config-default)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;;
|
2015-10-28 11:55:49 +01:00
|
|
|
|
;; 3. Link or copy the file 'xinitrc' to '~/.xinitrc'.
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; 4. Launch EXWM in a console (e.g. tty1) with
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;;
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; xinit -- vt01
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;;
|
2015-09-04 03:09:59 +02:00
|
|
|
|
;; 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.
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
;; References:
|
|
|
|
|
;; + dwm (http://dwm.suckless.org/)
|
|
|
|
|
;; + i3 wm (https://i3wm.org/)
|
|
|
|
|
;; + Also see references within each required library.
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
2015-09-27 13:31:00 +02:00
|
|
|
|
(require 'server)
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(require 'exwm-core)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(require 'exwm-workspace)
|
|
|
|
|
(require 'exwm-layout)
|
|
|
|
|
(require 'exwm-floating)
|
|
|
|
|
(require 'exwm-manage)
|
|
|
|
|
(require 'exwm-input)
|
|
|
|
|
|
2016-02-19 10:12:43 +01:00
|
|
|
|
;;;###autoload
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(defun exwm-reset ()
|
|
|
|
|
"Reset window to standard state: non-fullscreen, line-mode."
|
|
|
|
|
(interactive)
|
2015-08-06 08:41:28 +02:00
|
|
|
|
(with-current-buffer (window-buffer)
|
2015-08-07 06:41:15 +02:00
|
|
|
|
(when (eq major-mode 'exwm-mode)
|
2016-08-12 13:27:26 +02:00
|
|
|
|
(when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)
|
|
|
|
|
(exwm-layout-unset-fullscreen))
|
2015-08-08 14:12:07 +02:00
|
|
|
|
;; Force refresh
|
|
|
|
|
(exwm-layout--refresh)
|
2016-04-03 06:24:50 +02:00
|
|
|
|
(call-interactively #'exwm-input-grab-keyboard))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2016-07-30 13:01:33 +02:00
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun exwm-restart ()
|
|
|
|
|
"Restart EXWM."
|
|
|
|
|
(interactive)
|
2016-07-31 07:14:43 +02:00
|
|
|
|
(when (exwm-workspace--confirm-kill-emacs "[EXWM] Restart? " 'no-check)
|
2016-08-01 13:49:43 +02:00
|
|
|
|
(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))))))
|
2016-07-30 13:01:33 +02:00
|
|
|
|
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(defun exwm--update-window-type (id &optional force)
|
|
|
|
|
"Update _NET_WM_WINDOW_TYPE."
|
2015-08-10 08:23:37 +02:00
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(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)))))))
|
|
|
|
|
|
|
|
|
|
(defvar exwm-update-class-hook nil
|
|
|
|
|
"Normal hook run when window class is updated.")
|
|
|
|
|
|
|
|
|
|
(defun exwm--update-class (id &optional force)
|
|
|
|
|
"Update WM_CLASS."
|
2015-08-10 08:23:37 +02:00
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(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)))))))
|
|
|
|
|
|
|
|
|
|
(defvar exwm-update-title-hook nil
|
|
|
|
|
"Normal hook run when window title is updated.")
|
|
|
|
|
|
|
|
|
|
(defun exwm--update-utf8-title (id &optional force)
|
|
|
|
|
"Update _NET_WM_NAME."
|
2015-08-10 08:23:37 +02:00
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(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."
|
2015-08-10 08:23:37 +02:00
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(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--update-utf8-title id)
|
|
|
|
|
(exwm--update-ctext-title id))
|
|
|
|
|
|
|
|
|
|
(defun exwm--update-transient-for (id &optional force)
|
|
|
|
|
"Update WM_TRANSIENT_FOR."
|
2015-08-10 08:23:37 +02:00
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(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."
|
2015-08-10 08:23:37 +02:00
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(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."
|
2015-08-10 08:23:37 +02:00
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(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
|
2016-07-13 12:51:32 +02:00
|
|
|
|
(with-slots (flags input initial-state) reply
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(when flags
|
|
|
|
|
(unless (= 0 (logand flags xcb:icccm:WM_HINTS:InputHint))
|
|
|
|
|
(setq exwm--hints-input (when input (= 1 input))))
|
2016-07-13 12:51:32 +02:00
|
|
|
|
(unless (= 0 (logand flags xcb:icccm:WM_HINTS:StateHint))
|
|
|
|
|
(setq exwm-state initial-state))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(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)))
|
2016-07-21 06:41:51 +02:00
|
|
|
|
(unless (frame-parameter exwm--frame 'exwm-urgency)
|
|
|
|
|
(set-frame-parameter exwm--frame 'exwm-urgency t)
|
2015-09-17 13:48:50 +02:00
|
|
|
|
(setq exwm-workspace--switch-history-outdated t))))))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
(defun exwm--update-protocols (id &optional force)
|
|
|
|
|
"Update WM_PROTOCOLS."
|
2015-08-10 08:23:37 +02:00
|
|
|
|
(with-current-buffer (exwm--id->buffer id)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(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)))))))
|
|
|
|
|
|
2016-07-15 14:04:56 +02:00
|
|
|
|
(defun exwm--update-struts-legacy (id)
|
2016-07-12 12:35:51 +02:00
|
|
|
|
"Update _NET_WM_STRUT."
|
2016-07-15 14:04:56 +02:00
|
|
|
|
(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))
|
2016-08-12 14:28:05 +02:00
|
|
|
|
(if pair
|
|
|
|
|
(setcdr pair struts)
|
|
|
|
|
(push (cons id struts) exwm-workspace--id-struts-alist))
|
2016-07-15 14:04:56 +02:00
|
|
|
|
(exwm-workspace--update-struts))
|
2016-08-15 12:42:35 +02:00
|
|
|
|
;; Update workareas.
|
2016-07-16 08:34:57 +02:00
|
|
|
|
(exwm-workspace--update-workareas)
|
2016-07-12 12:35:51 +02:00
|
|
|
|
;; Update workspaces.
|
|
|
|
|
(dolist (f exwm-workspace--list)
|
2016-07-16 08:34:57 +02:00
|
|
|
|
(exwm-workspace--set-fullscreen f)))))
|
2016-07-12 12:35:51 +02:00
|
|
|
|
|
2016-07-15 14:04:56 +02:00
|
|
|
|
(defun exwm--update-struts-partial (id)
|
2016-07-12 12:35:51 +02:00
|
|
|
|
"Update _NET_WM_STRUT_PARTIAL."
|
|
|
|
|
(let ((reply (xcb:+request-unchecked+reply exwm--connection
|
|
|
|
|
(make-instance 'xcb:ewmh:get-_NET_WM_STRUT_PARTIAL
|
2016-07-15 14:04:56 +02:00
|
|
|
|
:window id)))
|
|
|
|
|
struts pair)
|
|
|
|
|
(when reply
|
|
|
|
|
(setq struts (slot-value reply 'value)
|
|
|
|
|
pair (assq id exwm-workspace--id-struts-alist))
|
2016-08-12 14:28:05 +02:00
|
|
|
|
(if pair
|
|
|
|
|
(setcdr pair struts)
|
|
|
|
|
(push (cons id struts) exwm-workspace--id-struts-alist))
|
2016-07-15 14:04:56 +02:00
|
|
|
|
(exwm-workspace--update-struts))
|
2016-08-15 12:42:35 +02:00
|
|
|
|
;; Update workareas.
|
2016-07-16 08:34:57 +02:00
|
|
|
|
(exwm-workspace--update-workareas)
|
2016-07-12 12:35:51 +02:00
|
|
|
|
;; Update workspaces.
|
|
|
|
|
(dolist (f exwm-workspace--list)
|
2016-07-16 08:34:57 +02:00
|
|
|
|
(exwm-workspace--set-fullscreen f))))
|
2016-07-12 12:35:51 +02:00
|
|
|
|
|
2016-07-15 14:04:56 +02:00
|
|
|
|
(defun exwm--update-struts (id)
|
2016-07-12 12:35:51 +02:00
|
|
|
|
"Update _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT."
|
2016-07-15 14:04:56 +02:00
|
|
|
|
(exwm--update-struts-partial id)
|
|
|
|
|
(exwm--update-struts-legacy id))
|
2016-07-12 12:35:51 +02:00
|
|
|
|
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(defun exwm--on-PropertyNotify (data _synthetic)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
"Handle PropertyNotify event."
|
|
|
|
|
(let ((obj (make-instance 'xcb:PropertyNotify))
|
2015-09-04 03:09:59 +02:00
|
|
|
|
atom id buffer)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(xcb:unmarshal obj data)
|
|
|
|
|
(setq id (slot-value obj 'window)
|
2016-08-09 07:34:29 +02:00
|
|
|
|
atom (slot-value obj 'atom))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(setq buffer (exwm--id->buffer id))
|
2016-07-12 12:35:51 +02:00
|
|
|
|
(if (not (buffer-live-p buffer))
|
|
|
|
|
;; Properties of unmanaged X windows.
|
|
|
|
|
(cond ((= atom xcb:Atom:_NET_WM_STRUT)
|
2016-07-15 14:04:56 +02:00
|
|
|
|
(exwm--update-struts-legacy id))
|
2016-07-12 12:35:51 +02:00
|
|
|
|
((= atom xcb:Atom:_NET_WM_STRUT_PARTIAL)
|
2016-07-15 14:04:56 +02:00
|
|
|
|
(exwm--update-struts-partial id)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(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))
|
2015-08-08 14:12:07 +02:00
|
|
|
|
((= atom xcb:Atom:_NET_WM_USER_TIME)) ;ignored
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(t (exwm--log "Unhandled PropertyNotify: %s(%d)"
|
2015-08-10 04:55:28 +02:00
|
|
|
|
(x-get-atom-name atom exwm-workspace--current)
|
|
|
|
|
atom)))))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(defun exwm--on-ClientMessage (raw-data _synthetic)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
"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))
|
|
|
|
|
(cond
|
2016-07-19 04:30:21 +02:00
|
|
|
|
;; _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))
|
|
|
|
|
(delete-frame (car (last exwm-workspace--list)))))))
|
2016-07-13 12:51:32 +02:00
|
|
|
|
;; _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)))
|
|
|
|
|
(when (buffer-live-p buffer)
|
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
(when (eq exwm--frame exwm-workspace--current)
|
|
|
|
|
(when (exwm-layout--iconic-state-p)
|
|
|
|
|
;; State change: iconic => normal.
|
|
|
|
|
(set-window-buffer (frame-selected-window exwm--frame)
|
|
|
|
|
(current-buffer)))
|
|
|
|
|
;; Focus transfer.
|
2016-07-14 16:08:27 +02:00
|
|
|
|
(select-window (get-buffer-window nil t)))))))
|
2016-07-13 12:51:32 +02:00
|
|
|
|
;; _NET_CLOSE_WINDOW.
|
|
|
|
|
((= type xcb:Atom:_NET_CLOSE_WINDOW)
|
|
|
|
|
(let ((buffer (exwm--id->buffer id)))
|
|
|
|
|
(when (buffer-live-p buffer)
|
|
|
|
|
(kill-buffer buffer))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; _NET_WM_MOVERESIZE
|
|
|
|
|
((= type xcb:Atom:_NET_WM_MOVERESIZE)
|
|
|
|
|
(let ((direction (elt data 2))
|
|
|
|
|
(buffer (exwm--id->buffer id)))
|
2016-07-21 06:41:51 +02:00
|
|
|
|
(unless (and buffer
|
|
|
|
|
(not (buffer-local-value 'exwm--floating-frame buffer)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(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))
|
|
|
|
|
(t (exwm-floating--start-moveresize id direction))))))
|
|
|
|
|
;; _NET_REQUEST_FRAME_EXTENTS
|
|
|
|
|
((= type xcb:Atom:_NET_REQUEST_FRAME_EXTENTS)
|
|
|
|
|
(let ((buffer (exwm--id->buffer id))
|
2015-09-04 03:09:59 +02:00
|
|
|
|
left right top btm)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(if (or (not buffer)
|
2016-07-21 06:41:51 +02:00
|
|
|
|
(not (buffer-local-value 'exwm--floating-frame buffer)))
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(setq left 0 right 0 top 0 btm 0)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(setq left exwm-floating-border-width
|
|
|
|
|
right exwm-floating-border-width
|
|
|
|
|
top (+ exwm-floating-border-width (window-header-line-height))
|
2015-09-04 03:09:59 +02:00
|
|
|
|
btm (+ exwm-floating-border-width
|
|
|
|
|
(window-mode-line-height))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:ewmh:set-_NET_FRAME_EXTENTS
|
|
|
|
|
:window id :left left :right right
|
2015-09-04 03:09:59 +02:00
|
|
|
|
:top top :bottom btm)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(xcb:flush exwm--connection))
|
2016-07-13 12:51:32 +02:00
|
|
|
|
;; _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))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; _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)
|
2015-08-24 21:09:42 +02:00
|
|
|
|
;; 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))
|
|
|
|
|
(dolist (f exwm-workspace--list)
|
|
|
|
|
(when (equal (frame-parameter f 'exwm-outer-id) id)
|
2016-07-12 12:35:51 +02:00
|
|
|
|
(exwm-workspace--set-fullscreen f)
|
2016-03-06 06:45:13 +01:00
|
|
|
|
(xcb:+request
|
2015-08-24 21:09:42 +02:00
|
|
|
|
exwm--connection
|
|
|
|
|
(make-instance 'xcb:ewmh:set-_NET_WM_STATE
|
|
|
|
|
:window id
|
|
|
|
|
:data (vector
|
|
|
|
|
xcb:Atom:_NET_WM_STATE_FULLSCREEN)))
|
2016-03-06 06:45:13 +01:00
|
|
|
|
(xcb:flush exwm--connection))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(when buffer ;ensure it's managed
|
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
;; _NET_WM_STATE_MODAL
|
|
|
|
|
(when (memq xcb:Atom:_NET_WM_STATE_MODAL props)
|
|
|
|
|
(cond ((= action xcb:ewmh:_NET_WM_STATE_ADD)
|
|
|
|
|
(unless exwm--floating-frame
|
|
|
|
|
(exwm-floating--set-floating id))
|
|
|
|
|
(push xcb:Atom:_NET_WM_STATE_MODAL props-new))
|
|
|
|
|
((= action xcb:ewmh:_NET_WM_STATE_REMOVE)
|
|
|
|
|
(when exwm--floating-frame
|
|
|
|
|
(exwm-floating--unset-floating id)))
|
|
|
|
|
((= action xcb:ewmh:_NET_WM_STATE_TOGGLE)
|
|
|
|
|
(if exwm--floating-frame
|
|
|
|
|
(exwm-floating--unset-floating id)
|
|
|
|
|
(exwm-floating--set-floating id)
|
|
|
|
|
(push xcb:Atom:_NET_WM_STATE_MODAL props-new)))))
|
|
|
|
|
;; _NET_WM_STATE_FULLSCREEN
|
2015-08-06 06:32:14 +02:00
|
|
|
|
(when (or (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN props)
|
|
|
|
|
(memq xcb:Atom:_NET_WM_STATE_ABOVE props))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(cond ((= action xcb:ewmh:_NET_WM_STATE_ADD)
|
2016-08-12 13:27:26 +02:00
|
|
|
|
(unless (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN
|
|
|
|
|
exwm--ewmh-state)
|
|
|
|
|
(exwm-layout-set-fullscreen id))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(push xcb:Atom:_NET_WM_STATE_FULLSCREEN props-new))
|
|
|
|
|
((= action xcb:ewmh:_NET_WM_STATE_REMOVE)
|
2016-08-12 13:27:26 +02:00
|
|
|
|
(when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN
|
|
|
|
|
exwm--ewmh-state)
|
|
|
|
|
(exwm-layout-unset-fullscreen id)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
((= action xcb:ewmh:_NET_WM_STATE_TOGGLE)
|
2016-08-12 13:27:26 +02:00
|
|
|
|
(if (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN
|
|
|
|
|
exwm--ewmh-state)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(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)
|
2016-07-17 14:00:00 +02:00
|
|
|
|
(unless (eq exwm--frame exwm-workspace--current)
|
2016-07-21 06:41:51 +02:00
|
|
|
|
(set-frame-parameter exwm--frame 'exwm-urgency t)
|
2016-07-17 14:00:00 +02:00
|
|
|
|
(setq exwm-workspace--switch-history-outdated t)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; 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)
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(setq exwm-manage--ping-lock nil))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(t (exwm--log "Unhandled WM_PROTOCOLS of type: %d" type)))))
|
2016-07-13 12:51:32 +02:00
|
|
|
|
((= 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
|
|
|
|
|
(bury-buffer)))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(t (exwm--log "Unhandled client message: %s" obj)))))
|
|
|
|
|
|
|
|
|
|
(defun exwm--init-icccm-ewmh ()
|
|
|
|
|
"Initialize ICCCM/EWMH support."
|
|
|
|
|
;; Handle PropertyNotify event
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(xcb:+event exwm--connection 'xcb:PropertyNotify #'exwm--on-PropertyNotify)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; Handle relevant client messages
|
2015-09-04 03:09:59 +02:00
|
|
|
|
(xcb:+event exwm--connection 'xcb:ClientMessage #'exwm--on-ClientMessage)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; Set _NET_SUPPORTED
|
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:ewmh:set-_NET_SUPPORTED
|
|
|
|
|
:window exwm--root
|
2016-07-13 12:51:32 +02:00
|
|
|
|
: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
|
2016-08-15 12:42:35 +02:00
|
|
|
|
;; xcb:Atom:_NET_WORKAREA
|
2016-07-13 12:51:32 +02:00
|
|
|
|
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)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; Create a child window for setting _NET_SUPPORTING_WM_CHECK
|
|
|
|
|
(let ((new-id (xcb:generate-id exwm--connection)))
|
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:CreateWindow
|
2016-08-12 13:18:32 +02:00
|
|
|
|
:depth 0
|
|
|
|
|
:wid new-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
|
2015-07-17 13:16:08 +02:00
|
|
|
|
:override-redirect 1))
|
|
|
|
|
(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_WM_NAME
|
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:ewmh:set-_NET_WM_NAME
|
|
|
|
|
:window i :data "EXWM"))))
|
2016-08-12 14:30:07 +02:00
|
|
|
|
;; 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]))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(xcb:flush exwm--connection))
|
|
|
|
|
|
2016-07-30 13:01:33 +02:00
|
|
|
|
(defun exwm--exit-icccm-ewmh ()
|
|
|
|
|
"Remove ICCCM/EWMH properties."
|
|
|
|
|
(dolist (p (list
|
|
|
|
|
xcb:Atom:_NET_WM_NAME
|
|
|
|
|
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_ACTIVE_WINDOW
|
|
|
|
|
xcb:Atom:_NET_SUPPORTING_WM_CHECK
|
|
|
|
|
xcb:Atom:_NET_VIRTUAL_ROOTS
|
|
|
|
|
;; TODO: Keep this list synchronized with that in
|
|
|
|
|
;; `exwm--init-icccm-ewmh'.
|
|
|
|
|
))
|
|
|
|
|
(xcb:+request exwm--connection
|
|
|
|
|
(make-instance 'xcb:DeleteProperty
|
|
|
|
|
:window exwm--root
|
|
|
|
|
:property p))
|
|
|
|
|
(xcb:flush exwm--connection)))
|
|
|
|
|
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(defvar exwm-init-hook nil
|
|
|
|
|
"Normal hook run when EXWM has just finished initialization.")
|
|
|
|
|
|
|
|
|
|
(defun exwm-init (&optional frame)
|
|
|
|
|
"Initialize EXWM."
|
2016-05-23 13:13:42 +02:00
|
|
|
|
(if frame
|
|
|
|
|
;; The frame might not be selected if it's created by emacslicnet.
|
|
|
|
|
(select-frame-set-input-focus frame)
|
|
|
|
|
(setq frame (selected-frame)))
|
|
|
|
|
(if (not (eq 'x (framep frame)))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(exwm--log "Not running under X environment")
|
|
|
|
|
(unless exwm--connection
|
2015-08-13 01:54:19 +02:00
|
|
|
|
(exwm-enable 'undo) ;never initialize again
|
2016-05-12 18:11:12 +02:00
|
|
|
|
(setq exwm--connection (xcb:connect))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(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))
|
|
|
|
|
(if (xcb:+request-checked+request-check exwm--connection
|
|
|
|
|
(make-instance 'xcb:ChangeWindowAttributes
|
|
|
|
|
:window exwm--root :value-mask xcb:CW:EventMask
|
|
|
|
|
:event-mask xcb:EventMask:SubstructureRedirect))
|
|
|
|
|
;; Other window manager is running
|
|
|
|
|
(progn (xcb:disconnect exwm--connection)
|
|
|
|
|
(setq exwm--connection nil)
|
|
|
|
|
(exwm--log "Other window manager detected"))
|
2015-11-02 04:19:59 +01:00
|
|
|
|
;; Disable some features not working well with EXWM
|
2016-02-03 05:12:24 +01:00
|
|
|
|
(setq use-dialog-box nil)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
;; Initialize ICCCM/EWMH support
|
2016-05-23 13:13:42 +02:00
|
|
|
|
(xcb:icccm:init exwm--connection t)
|
|
|
|
|
(xcb:ewmh:init exwm--connection t)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(exwm--lock)
|
|
|
|
|
(exwm--init-icccm-ewmh)
|
|
|
|
|
(exwm-layout--init)
|
|
|
|
|
(exwm-floating--init)
|
|
|
|
|
(exwm-manage--init)
|
2016-02-03 05:12:24 +01:00
|
|
|
|
(exwm-workspace--init)
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(exwm-input--init)
|
|
|
|
|
(exwm--unlock)
|
2016-02-12 07:10:11 +01:00
|
|
|
|
(exwm-workspace--post-init)
|
2015-08-24 21:09:42 +02:00
|
|
|
|
;; Manage existing windows
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(exwm-manage--scan)
|
|
|
|
|
(run-hooks 'exwm-init-hook)))))
|
|
|
|
|
|
2016-07-30 13:01:33 +02:00
|
|
|
|
(defvar exwm-exit-hook nil "Normal hook run just before EXWM exits.")
|
2016-05-23 13:13:42 +02:00
|
|
|
|
|
|
|
|
|
(defun exwm--exit ()
|
|
|
|
|
"Exit EXWM."
|
|
|
|
|
(run-hooks 'exwm-exit-hook)
|
|
|
|
|
;; Exit modules.
|
|
|
|
|
(exwm-input--exit)
|
|
|
|
|
(exwm-workspace--exit)
|
|
|
|
|
(exwm-manage--exit)
|
|
|
|
|
(exwm-floating--exit)
|
|
|
|
|
(exwm-layout--exit)
|
2016-07-30 13:01:33 +02:00
|
|
|
|
(exwm--exit-icccm-ewmh))
|
2016-05-23 13:13:42 +02:00
|
|
|
|
|
2015-09-27 13:31:00 +02:00
|
|
|
|
(defvar exwm-blocking-subrs '(x-file-dialog x-popup-dialog x-select-font)
|
|
|
|
|
"Subrs (primitives) that would normally block EXWM.")
|
|
|
|
|
|
2015-07-17 13:16:08 +02:00
|
|
|
|
(defun exwm-enable (&optional undo)
|
2015-09-04 03:09:59 +02:00
|
|
|
|
"Enable/Disable EXWM."
|
2015-09-27 13:31:00 +02:00
|
|
|
|
(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
|
2016-07-30 04:17:57 +02:00
|
|
|
|
(setq frame-resize-pixelwise t ;mandatory; before init
|
|
|
|
|
window-resize-pixelwise t)
|
2016-07-30 12:59:19 +02:00
|
|
|
|
;; Ignore unrecognized command line arguments. This can be helpful
|
|
|
|
|
;; when EXWM is launched by some session manager.
|
|
|
|
|
(push #'vector command-line-functions)
|
2015-09-27 13:31:00 +02:00
|
|
|
|
(add-hook 'window-setup-hook #'exwm-init t) ;for Emacs
|
|
|
|
|
(add-hook 'after-make-frame-functions #'exwm-init t) ;for Emacs Client
|
|
|
|
|
(add-hook 'kill-emacs-hook #'exwm--server-stop)
|
|
|
|
|
(dolist (i exwm-blocking-subrs)
|
|
|
|
|
(advice-add i :around #'exwm--server-eval-at)))))
|
|
|
|
|
|
|
|
|
|
(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--server-stop ()
|
|
|
|
|
"Stop the subordinate Emacs server."
|
|
|
|
|
(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
|
|
|
|
|
(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
|
2016-03-25 06:57:42 +01:00
|
|
|
|
"-d" (frame-parameter nil 'display)
|
2015-09-27 13:31:00 +02:00
|
|
|
|
"-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))))))
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
2015-10-28 11:55:49 +01:00
|
|
|
|
(define-obsolete-function-alias 'exwm-enable-ido-workaround 'exwm-config-ido
|
|
|
|
|
"25.1" "Enable workarounds for Ido.")
|
2015-08-11 05:54:38 +02:00
|
|
|
|
|
|
|
|
|
(defun exwm-disable-ido-workaround ()
|
2015-10-28 11:55:49 +01:00
|
|
|
|
"This function does nothing actually."
|
|
|
|
|
(declare (obsolete nil "25.1")))
|
2015-08-11 05:54:38 +02:00
|
|
|
|
|
2015-07-17 13:16:08 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(provide 'exwm)
|
|
|
|
|
|
|
|
|
|
;;; exwm.el ends here
|