contrib/toolbar.lisp: beginning of clickable modules
[clfswm.git] / contrib / volume-mode.lisp
blob5809f262ffe868b806e2d6b6463534e633cd2114
1 ;;; --------------------------------------------------------------------------
2 ;;; CLFSWM - FullScreen Window Manager
3 ;;;
4 ;;; --------------------------------------------------------------------------
5 ;;; Documentation: Volume mode
6 ;;; --------------------------------------------------------------------------
7 ;;;
8 ;;; (C) 2011 Desmond O. Chang <dochang@gmail.com>
9 ;;;
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.
14 ;;;
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.
19 ;;;
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.
23 ;;;
24 ;;; Documentation: A volume mode.
25 ;;; If you want to use this file, just add this line in
26 ;;; your configuration file:
27 ;;;
28 ;;; (load-contrib "volume-mode.lisp")
29 ;;; And with the alsa mixer:
30 ;;; (load-contrib "amixer.lisp")
31 ;;;
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.
37 ;;;
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.
42 ;;;
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
50 ;;; return NIL.
51 ;;;
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.
56 ;;;
57 ;;; contrib/amixer.lisp shows how to use volume mode with alsa.
58 ;;;
59 ;;; --------------------------------------------------------------------------
61 (in-package :clfswm)
63 (format t "Loading Volume mode code... ")
65 (defparameter *volume-keys* nil)
66 (defconfig *volume-mode-placement* 'bottom-middle-root-placement
67 'Placement "Volume mode window placement")
70 (defvar *volume-window* nil)
71 (defvar *volume-font* nil)
72 (defvar *volume-gc* nil)
73 (defvar *in-volume-mode* nil)
74 (defvar *leave-volume-mode* nil)
76 (defvar *volume-ratio* nil)
77 (defvar *volume-mute* nil)
79 (defvar *volume-mode-timeout* 3
80 "Volume mode timeout in seconds:
81 > 0 means volume mode will exit when timeout occurs;
82 = 0 means exit right now;
83 < 0 means exit manually.")
86 ;;; CONFIG - Volume mode
87 (defconfig *volume-font-string* *default-font-string*
88 'Volume-mode "Volume window font string")
89 (defconfig *volume-background* "black"
90 'Volume-mode "Volume window background color")
91 (defconfig *volume-foreground* "green"
92 'Volume-mode "Volume window foreground color")
93 (defconfig *volume-border* "red"
94 'Volume-mode "Volume window border color")
95 (defconfig *volume-border-size* 1
96 'Volume-mode "Volume window border size")
97 (defconfig *volume-width* 400
98 'Volume-mode "Volume mode window width")
99 (defconfig *volume-height* 15
100 'Volume-mode "Volume mode window height")
101 (defconfig *volume-text-limit* 30
102 'Volume-mode "Maximum text limit in the volume window")
103 (defconfig *volume-external-mixer-cmd* "/usr/bin/gnome-alsamixer"
104 'Volume-mode "Command to start an external mixer program")
106 (define-init-hash-table-key *volume-keys* "Volume mode keys")
107 (define-define-key "volume" *volume-keys*)
109 (add-hook *binding-hook* 'init-*volume-keys*)
111 (defun set-default-volume-keys ()
112 (define-volume-key ("XF86AudioMute") 'volume-mute)
113 (define-volume-key ("XF86AudioLowerVolume") 'volume-lower)
114 (define-volume-key ("XF86AudioRaiseVolume") 'volume-raise)
115 (define-volume-key (#\/) 'volume-mute)
116 (define-volume-key (#\,) 'volume-lower)
117 (define-volume-key (#\.) 'volume-raise)
118 (define-volume-key ("m") 'volume-mute)
119 (define-volume-key ("l") 'volume-lower)
120 (define-volume-key ("r") 'volume-raise)
121 (define-volume-key ("Down") 'volume-lower)
122 (define-volume-key ("Up") 'volume-raise)
123 (define-volume-key ("Left") 'volume-lower)
124 (define-volume-key ("Right") 'volume-raise)
125 (define-volume-key ("PageUp") 'volume-lower)
126 (define-volume-key ("PageDown") 'volume-raise)
127 (define-volume-key ("Return") 'leave-volume-mode)
128 (define-volume-key ("Escape") 'leave-volume-mode)
129 (define-volume-key ("g" :control) 'leave-volume-mode)
130 (define-volume-key ("e") 'run-external-volume-mixer)
131 ;;; Main mode
132 (define-main-key ("XF86AudioMute") 'volume-mute)
133 (define-main-key ("XF86AudioLowerVolume") 'volume-lower)
134 (define-main-key ("XF86AudioRaiseVolume") 'volume-raise)
135 ;;; Second mode
136 (define-second-key ("XF86AudioMute") 'volume-mute)
137 (define-second-key ("XF86AudioLowerVolume") 'volume-lower)
138 (define-second-key ("XF86AudioRaiseVolume") 'volume-raise))
140 (add-hook *binding-hook* 'set-default-volume-keys)
142 (defun volume-mode-window-message (width)
143 (if *volume-ratio*
144 (let* ((mute (if *volume-mute* #\- #\+))
145 (percentage (round (* 100 *volume-ratio*)))
146 (n (round (* width *volume-ratio*))))
147 (format nil "[~A] ~3@A% ~A~A" mute percentage
148 (repeat-chars n #\#) (repeat-chars (- width n) #\.)))
149 "unknown"))
151 (defun draw-volume-mode-window ()
152 (raise-window *volume-window*)
153 (clear-pixmap-buffer *volume-window* *volume-gc*)
154 (let* ((text (limit-length (volume-mode-window-message 20) *volume-text-limit*))
155 (len (length text)))
156 (xlib:draw-glyphs *pixmap-buffer* *volume-gc*
157 (truncate (/ (- *volume-width* (* (xlib:max-char-width *volume-font*) len)) 2))
158 (truncate (/ (+ *volume-height* (- (xlib:font-ascent *volume-font*) (xlib:font-descent *volume-font*))) 2))
159 text))
160 (copy-pixmap-buffer *volume-window* *volume-gc*))
162 (defun leave-volume-mode ()
163 "Leave the volume mode"
164 (throw 'exit-volume-loop nil))
166 (defun update-volume-mode ()
167 (draw-volume-mode-window)
168 (cond ((plusp *volume-mode-timeout*)
169 (erase-timer :volume-mode-timer)
170 (with-timer (*volume-mode-timeout* :volume-mode-timer)
171 (setf *leave-volume-mode* t)))
172 ((zerop *volume-mode-timeout*)
173 (erase-timer :volume-mode-timer)
174 (setf *leave-volume-mode* t))
175 ((minusp *volume-mode-timeout*)
176 (erase-timer :volume-mode-timer))))
178 (defun volume-enter-function ()
179 (with-placement (*volume-mode-placement* x y *volume-width* *volume-height* *volume-border-size*)
180 (setf *volume-font* (xlib:open-font *display* *volume-font-string*)
181 *volume-window* (xlib:create-window :parent *root*
182 :x x
183 :y y
184 :width *volume-width*
185 :height *volume-height*
186 :background (get-color *volume-background*)
187 :border-width *volume-border-size*
188 :border (when (plusp *volume-border-size*)
189 (get-color *volume-border*))
190 :colormap (xlib:screen-default-colormap *screen*)
191 :event-mask '(:exposure :key-press))
192 *volume-gc* (xlib:create-gcontext :drawable *volume-window*
193 :foreground (get-color *volume-foreground*)
194 :background (get-color *volume-background*)
195 :font *volume-font*
196 :line-style :solid))
197 (map-window *volume-window*))
198 (setf *in-volume-mode* t
199 *leave-volume-mode* nil)
200 (update-volume-mode))
202 (defun volume-loop-function ()
203 (when *leave-volume-mode*
204 (leave-volume-mode)))
206 (defun volume-leave-function ()
207 (when *volume-gc*
208 (xlib:free-gcontext *volume-gc*))
209 (when *volume-window*
210 (xlib:destroy-window *volume-window*))
211 (when *volume-font*
212 (xlib:close-font *volume-font*))
213 (xlib:display-finish-output *display*)
214 (erase-timer :volume-mode-timer)
215 (setf *volume-window* nil
216 *volume-gc* nil
217 *volume-font* nil
218 *in-volume-mode* nil
219 *leave-volume-mode* nil))
221 (define-handler volume-mode :key-press (code state)
222 (funcall-key-from-code *volume-keys* code state))
224 (defun volume-mode ()
225 (let ((grab-keyboard-p (xgrab-keyboard-p))
226 (grab-pointer-p (xgrab-pointer-p)))
227 (xgrab-pointer *root* 92 93)
228 (unless grab-keyboard-p
229 (ungrab-main-keys)
230 (xgrab-keyboard *root*))
231 (generic-mode 'volume-mode 'exit-volume-loop
232 :enter-function 'volume-enter-function
233 :loop-function 'volume-loop-function
234 :leave-function 'volume-leave-function
235 :original-mode '(main-mode))
236 (unless grab-keyboard-p
237 (xungrab-keyboard)
238 (grab-main-keys))
239 (if grab-pointer-p
240 (xgrab-pointer *root* 66 67)
241 (xungrab-pointer))))
243 (defun volume-set (fn)
244 (when fn
245 (setf (values *volume-ratio* *volume-mute*) (funcall fn))
246 (if *in-volume-mode*
247 (update-volume-mode)
248 (volume-mode))))
250 (defvar *volume-mute-function* nil)
251 (defvar *volume-lower-function* nil)
252 (defvar *volume-raise-function* nil)
254 (defun volume-mute ()
255 "Toggle mute."
256 (volume-set *volume-mute-function*))
258 (defun volume-lower ()
259 "Lower volume."
260 (volume-set *volume-lower-function*))
262 (defun volume-raise ()
263 "Raise volume."
264 (volume-set *volume-raise-function*))
266 (defun run-external-volume-mixer ()
267 "Start an external volume mixer"
268 (do-shell *volume-external-mixer-cmd*))
270 (format t "done~%")