1 ;;; --------------------------------------------------------------------------
2 ;;; CLFSWM - FullScreen Window Manager
4 ;;; --------------------------------------------------------------------------
5 ;;; Documentation: Volume mode
6 ;;; --------------------------------------------------------------------------
8 ;;; (C) 2015 Desmond O. Chang <dochang@gmail.com>
10 ;;; This program is free software; you can redistribute it and/or modify
11 ;;; it under the terms of the GNU General Public License as published by
12 ;;; the Free Software Foundation; either version 3 of the License, or
13 ;;; (at your option) any later version.
15 ;;; This program is distributed in the hope that it will be useful,
16 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;;; GNU General Public License for more details.
20 ;;; You should have received a copy of the GNU General Public License
21 ;;; along with this program; if not, write to the Free Software
22 ;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 ;;; Documentation: A volume mode.
25 ;;; If you want to use this file, just add this line in
26 ;;; your configuration file:
28 ;;; (load-contrib "volume-mode.lisp")
29 ;;; And with the alsa mixer:
30 ;;; (load-contrib "amixer.lisp")
32 ;;; This mode is inspired by the emms volume package. When you change the
33 ;;; volume in main mode or second mode, clfswm will enter volume mode and
34 ;;; set a timer to leave this mode. Changing volume in volume mode will
35 ;;; reset the timer. You can also leave volume mode manually by return,
36 ;;; escape or control-g.
38 ;;; Special variable *VOLUME-MODE-TIMEOUT* controls the timeout in
39 ;;; seconds. If it's positive, volume mode will exit when timeout occurs;
40 ;;; if it's 0, volume mode will exit right now; if it's negative, volume
41 ;;; will not exit even if timeout occurs. Default timeout is 3 seconds.
43 ;;; Volume mode uses three special variables to control the mixer:
44 ;;; *VOLUME-MUTE-FUNCTION*, *VOLUME-LOWER-FUNCTION* and
45 ;;; *VOLUME-RAISE-FUNCTION*. Their values are functions which must accept
46 ;;; no arguments and return two values indicating the mixer state. The
47 ;;; first value is the volume ratio whose type must be (real 0 1). If the
48 ;;; mixer is mute, the second value should be true, otherwise it should be
49 ;;; false. If volume controller cannot get the mixer state, it must
52 ;;; Volume mode shows a mute sign, a percentage and a ratio bar on the
53 ;;; screen. A plus sign '+' means it's unmute and a minus sign '-' means
54 ;;; it's mute now. If volume mode doesn't know the mixer state, a message
55 ;;; "unknown" will be shown.
57 ;;; contrib/amixer.lisp shows how to use volume mode with alsa.
59 ;;; --------------------------------------------------------------------------
63 (format t
"Loading Volume mode code... ")
65 (defparameter *volume-keys
* nil
)
66 (defparameter *volume-mouse
* nil
)
68 (defconfig *volume-mode-placement
* 'bottom-middle-root-placement
69 'Placement
"Volume mode window placement")
72 (defvar *volume-window
* nil
)
73 (defvar *volume-font
* nil
)
74 (defvar *volume-gc
* nil
)
75 (defvar *in-volume-mode
* nil
)
76 (defvar *leave-volume-mode
* nil
)
78 (defvar *volume-ratio
* nil
)
79 (defvar *volume-mute
* nil
)
81 (defvar *volume-mode-timeout
* 3
82 "Volume mode timeout in seconds:
83 > 0 means volume mode will exit when timeout occurs;
84 = 0 means exit right now;
85 < 0 means exit manually.")
88 ;;; CONFIG - Volume mode
89 (defconfig *volume-font-string
* *default-font-string
*
90 'Volume-mode
"Volume window font string")
91 (defconfig *volume-background
* "black"
92 'Volume-mode
"Volume window background color")
93 (defconfig *volume-foreground
* "green"
94 'Volume-mode
"Volume window foreground color")
95 (defconfig *volume-border
* "red"
96 'Volume-mode
"Volume window border color")
97 (defconfig *volume-border-size
* 1
98 'Volume-mode
"Volume window border size")
99 (defconfig *volume-width
* 400
100 'Volume-mode
"Volume mode window width")
101 (defconfig *volume-height
* 15
102 'Volume-mode
"Volume mode window height")
103 (defconfig *volume-text-limit
* 30
104 'Volume-mode
"Maximum text limit in the volume window")
105 (defconfig *volume-external-mixer-cmd
* "/usr/bin/gnome-alsamixer"
106 'Volume-mode
"Command to start an external mixer program")
108 (define-init-hash-table-key *volume-keys
* "Volume mode keys")
109 (define-init-hash-table-key *volume-mouse
* "Volume mode mouse button")
111 (define-define-key "volume" *volume-keys
*)
112 (define-define-mouse "volume-mouse" *volume-mouse
*)
114 (add-hook *binding-hook
* 'init-
*volume-keys
*)
116 (defun set-default-volume-keys ()
117 (define-volume-key ("XF86AudioMute") 'volume-mute
)
118 (define-volume-key ("XF86AudioLowerVolume") 'volume-lower
)
119 (define-volume-key ("XF86AudioRaiseVolume") 'volume-raise
)
120 (define-volume-key (#\
/) 'volume-mute
)
121 (define-volume-key (#\
,) 'volume-lower
)
122 (define-volume-key (#\.
) 'volume-raise
)
123 (define-volume-key ("m") 'volume-mute
)
124 (define-volume-key ("l") 'volume-lower
)
125 (define-volume-key ("r") 'volume-raise
)
126 (define-volume-key ("Down") 'volume-lower
)
127 (define-volume-key ("Up") 'volume-raise
)
128 (define-volume-key ("Left") 'volume-lower
)
129 (define-volume-key ("Right") 'volume-raise
)
130 (define-volume-key ("PageUp") 'volume-lower
)
131 (define-volume-key ("PageDown") 'volume-raise
)
132 (define-volume-key ("Return") 'leave-volume-mode
)
133 (define-volume-key ("Escape") 'leave-volume-mode
)
134 (define-volume-key ("g" :control
) 'leave-volume-mode
)
135 (define-volume-key ("e") 'run-external-volume-mixer
)
136 (define-volume-mouse (1) 'leave-volume-mode
)
137 (define-volume-mouse (2) 'run-external-volume-mixer
)
138 (define-volume-mouse (3) 'volume-mute
)
139 (define-volume-mouse (4) 'volume-raise
)
140 (define-volume-mouse (5) 'volume-lower
)
142 (define-main-key ("XF86AudioMute") 'volume-mute
)
143 (define-main-key ("XF86AudioLowerVolume") 'volume-lower
)
144 (define-main-key ("XF86AudioRaiseVolume") 'volume-raise
)
146 (define-second-key ("XF86AudioMute") 'volume-mute
)
147 (define-second-key ("XF86AudioLowerVolume") 'volume-lower
)
148 (define-second-key ("XF86AudioRaiseVolume") 'volume-raise
))
150 (add-hook *binding-hook
* 'set-default-volume-keys
)
152 (defun volume-mode-window-message (width)
154 (let* ((mute (if *volume-mute
* #\-
#\
+))
155 (percentage (round (* 100 *volume-ratio
*)))
156 (n (round (* width
*volume-ratio
*))))
157 (format nil
"[~A] ~3@A% ~A~A" mute percentage
158 (repeat-chars n
#\
#) (repeat-chars (- width n
) #\.
)))
161 (defun draw-volume-mode-window ()
162 (raise-window *volume-window
*)
163 (clear-pixmap-buffer *volume-window
* *volume-gc
*)
164 (let* ((text (limit-length (volume-mode-window-message 20) *volume-text-limit
*))
166 (xlib:draw-glyphs
*pixmap-buffer
* *volume-gc
*
167 (truncate (/ (- *volume-width
* (* (xlib:max-char-width
*volume-font
*) len
)) 2))
168 (truncate (/ (+ *volume-height
* (- (xlib:font-ascent
*volume-font
*) (xlib:font-descent
*volume-font
*))) 2))
170 (copy-pixmap-buffer *volume-window
* *volume-gc
*))
172 (defun leave-volume-mode (&optional window root-x root-y
)
173 "Leave the volume mode"
174 (declare (ignore window root-x root-y
))
175 (when *in-volume-mode
*
176 (throw 'exit-volume-loop nil
)))
178 (defun update-volume-mode ()
179 (draw-volume-mode-window)
180 (cond ((plusp *volume-mode-timeout
*)
181 (erase-timer :volume-mode-timer
)
182 (with-timer (*volume-mode-timeout
* :volume-mode-timer
)
183 (setf *leave-volume-mode
* t
)))
184 ((zerop *volume-mode-timeout
*)
185 (erase-timer :volume-mode-timer
)
186 (setf *leave-volume-mode
* t
))
187 ((minusp *volume-mode-timeout
*)
188 (erase-timer :volume-mode-timer
))))
190 (defun volume-enter-function ()
191 (with-placement (*volume-mode-placement
* x y
*volume-width
* *volume-height
* *volume-border-size
*)
192 (setf *volume-font
* (xlib:open-font
*display
* *volume-font-string
*)
193 *volume-window
* (xlib:create-window
:parent
*root
*
196 :width
*volume-width
*
197 :height
*volume-height
*
198 :background
(get-color *volume-background
*)
199 :border-width
*volume-border-size
*
200 :border
(when (plusp *volume-border-size
*)
201 (get-color *volume-border
*))
202 :colormap
(xlib:screen-default-colormap
*screen
*)
203 :event-mask
'(:exposure
:key-press
:button-press
))
204 *volume-gc
* (xlib:create-gcontext
:drawable
*volume-window
*
205 :foreground
(get-color *volume-foreground
*)
206 :background
(get-color *volume-background
*)
209 (map-window *volume-window
*))
210 (setf *in-volume-mode
* t
211 *leave-volume-mode
* nil
)
212 (update-volume-mode))
214 (defun volume-loop-function ()
215 (when *leave-volume-mode
*
216 (leave-volume-mode)))
218 (defun volume-leave-function ()
219 (setf *in-volume-mode
* nil
220 *leave-volume-mode
* nil
)
222 (xlib:free-gcontext
*volume-gc
*))
223 (when *volume-window
*
224 (xlib:destroy-window
*volume-window
*))
226 (xlib:close-font
*volume-font
*))
227 (xlib:display-finish-output
*display
*)
228 (erase-timer :volume-mode-timer
)
229 (setf *volume-window
* nil
233 (define-handler volume-mode
:key-press
(code state
)
234 (funcall-key-from-code *volume-keys
* code state
))
236 (define-handler volume-mode
:button-press
(code state window root-x root-y
)
237 (funcall-button-from-code *volume-mouse
* code state window root-x root-y
*fun-press
*))
240 (defun volume-mode ()
241 (with-grab-keyboard-and-pointer (92 93 66 67 t
)
242 (generic-mode 'volume-mode
'exit-volume-loop
243 :enter-function
'volume-enter-function
244 :loop-function
'volume-loop-function
245 :leave-function
'volume-leave-function
246 :original-mode
'(main-mode))))
249 (defun volume-set (fn)
251 (setf (values *volume-ratio
* *volume-mute
*) (funcall fn
))
256 (defvar *volume-mute-function
* nil
)
257 (defvar *volume-lower-function
* nil
)
258 (defvar *volume-raise-function
* nil
)
260 (defun volume-mute (&optional window root-x root-y
)
262 (declare (ignore window root-x root-y
))
263 (volume-set *volume-mute-function
*))
265 (defun volume-lower (&optional window root-x root-y
)
267 (declare (ignore window root-x root-y
))
268 (volume-set *volume-lower-function
*))
270 (defun volume-raise (&optional window root-x root-y
)
272 (declare (ignore window root-x root-y
))
273 (volume-set *volume-raise-function
*))
275 (defun run-external-volume-mixer (&optional window root-x root-y
)
276 "Start an external volume mixer"
277 (declare (ignore window root-x root-y
))
278 (do-shell *volume-external-mixer-cmd
*))
283 (define-toolbar-color volume-mode-button
"Volume mode color")
285 (define-toolbar-module (volume-mode-button (text "Vol"))
287 (with-set-toolbar-module-rectangle (module)
288 (toolbar-module-text toolbar module
(tb-color volume-mode-button
) text
)))
290 (define-toolbar-module-click (volume-mode-button text
)
292 (declare (ignore text module
))
295 (funcall-button-from-code *volume-mouse
* code state
(toolbar-window toolbar
)
296 root-x root-y
*fun-press
*)