-
Notifications
You must be signed in to change notification settings - Fork 3
/
dispwatch.el
136 lines (114 loc) · 4.75 KB
/
dispwatch.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
;;; dispwatch.el --- Watch displays for configuration changes -*- lexical-binding: t -*-
;; Copyright (C) 2018-2019 Mitchell Perilstein
;; Author: Mitchell Perilstein <mitchell.perilstein@gmail.com>
;; Keywords: frames
;; URL: https://github.com/mnp/dispwatch
;; Version: 1
;; Package-Requires: ((emacs "24.4"))
;; This program 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.
;; This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This package watches the current display configuration (name, pixel width and height,
;; physical dimensions, and work area) and gives your hook a call if it changes.
;; Intended use case is plugging/unplugging a monitor.
;;
;; Usage
;;
;; Require or use-package this. Make a hook function which takes one argument, a pair of
;; pixel width and height, like `(1024 . 768)`, then add your hook to
;; `dispwatch-display-change-hooks`. You will get called when that changes, eg by removing
;; or adding a monitor. Then call `(dispwatch-mode 1)` to get started and `(dispwatch-mode -1)`
;; to stop. `(dispwatch-mode)` toggles.
;;
;; Example
;;
;; (defun my-display-changed-hook (disp)
;; (message "rejiggering for %s" disp)
;; (cond ((equal disp '(3840 . 1080)) ; laptop + ext monitor
;; (setq font-size-pt 10))
;; ((equal disp '(1920 . 1080)) ; just laptop
;; (setq font-size-pt 11))
;; (t (message "Unknown display size %sx%s" (car disp) (cdr disp)))))
;;
;; (use-package dispwatch
;; :config (and
;; (add-hook 'dispwatch-display-change-hooks #'my-display-changed-hook)
;; (dispwatch-mode 1)))
;;
;;; Code:
;; Local variables
(defgroup dispwatch nil
"Minor mode for watching display geometry changes."
:prefix "dispwatch-"
:group 'Environment)
(defcustom dispwatch-interval 2
"Frequency to check display, in seconds.
Checking operation does not shell out of Emacs so there isn't much penalty."
:type 'integer
:group 'dispwatch
:safe #'integerp)
(defvar dispwatch-display-change-hooks nil
"List of hook functions called when a display change is detected.
Each takes one argument: a cons pair of pixel width and height.
The dimensions are determined by `frame-monitor-attributes'.
These hooks are run when a display change is detected.")
(defvar dispwatch-timer nil)
(defvar dispwatch-current-display nil)
(define-minor-mode dispwatch-mode
"Toggle dispwatch mode.
Interactively with no argument, this command toggles the mode. A
positive prefix argument enables the mode, any other prefix
argument disables it. From Lisp, argument omitted or nil enables
the mode, `toggle' toggles the state.
When dispwatch mode is enabled, the display configuration is
checked every `dispwatch-interval' seconds and if a change is
observed, the hook functions in `dispwatch-display-change-hooks'
with the new display resolution."
nil ;; The initial value.
"dispwatch" ;; The indicator for the mode line.
nil ;; The minor mode bindings.
:group 'dispwatch
:after-hook (if dispwatch-mode
(dispwatch--enable)
(dispwatch--disable)))
(defun dispwatch--enable ()
"Enable display reconfiguration detection."
(interactive)
(setq dispwatch-current-display (dispwatch--get-display))
(unless dispwatch-timer
(setq dispwatch-timer (run-at-time dispwatch-interval dispwatch-interval #'dispwatch--check-display)))
(message "dispwatch enabled"))
(defun dispwatch--disable ()
"Disable display reconfiguration detection."
(interactive)
(when dispwatch-timer
(cancel-timer dispwatch-timer)
(setq dispwatch-timer nil)
(message "dispwatch disabled")))
(defun dispwatch--get-display()
"Current display configuration, to compare against future configurations."
(let ((atts (frame-monitor-attributes)))
(list
(assoc 'name atts)
(assoc 'geometry atts)
(assoc 'mm-size atts)
(assoc 'workarea atts))))
(defun dispwatch--check-display()
"Did it change? Run hooks if so."
(let ((new (dispwatch--get-display)))
(unless (equal new dispwatch-current-display)
(setq dispwatch-current-display new)
(let* ((geom (assoc 'geometry new))
(w (nth 3 geom))
(h (nth 4 geom)))
(run-hook-with-args 'dispwatch-display-change-hooks (cons w h))))))
(provide 'dispwatch)
;;; dispwatch.el ends here