aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGerd Moellmann1999-07-21 21:43:52 +0000
committerGerd Moellmann1999-07-21 21:43:52 +0000
commit7840ced19916e4443b30d00c386b6b3d8584459e (patch)
tree0afcd8ef7ba95f8551c23923ddfaa2da4e06b0d0
parenta168702af09590a85cd744f6174d782237b861d8 (diff)
downloademacs-7840ced19916e4443b30d00c386b6b3d8584459e.tar.gz
emacs-7840ced19916e4443b30d00c386b6b3d8584459e.zip
New file.
-rw-r--r--lisp/gs.el185
-rw-r--r--lisp/image.el192
-rw-r--r--lisp/jit-lock.el433
-rw-r--r--lisp/tooltip.el476
-rw-r--r--src/sound.c824
5 files changed, 2110 insertions, 0 deletions
diff --git a/lisp/gs.el b/lisp/gs.el
new file mode 100644
index 00000000000..2a368bae199
--- /dev/null
+++ b/lisp/gs.el
@@ -0,0 +1,185 @@
1;;; gs.el --- interface to Ghostscript
2
3;; Copyright (C) 1998 Free Software Foundation, Inc.
4
5;; Maintainer: FSF
6;; Keywords: internal
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs 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 2, or (at your option)
13;; any later version.
14
15;; GNU Emacs 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 GNU Emacs; see the file COPYING. If not, write to the
22;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23;; Boston, MA 02111-1307, USA.
24
25;;; Commentary:
26
27;; This code is experimental. Don't use it.
28
29;;; Code:
30
31(defvar gs-program "gs"
32 "The name of the Ghostscript interpreter.")
33
34
35(defvar gs-device "x11"
36 "The Ghostscript device to use to produce images.")
37
38
39(defvar gs-options
40 '("-q"
41 ;"-dNOPAUSE"
42 "-dBATCH"
43 "-sDEVICE=<device>"
44 "<file>")
45 "List of command line arguments to pass to Ghostscript.
46Arguments may contain place-holders `<file>' for the name of the
47input file, and `<device>' for the device to use.")
48
49
50(defun gs-replace-in-string (string find repl)
51 "Return STRING with all occurrences of FIND replaced by REPL.
52FIND is a regular expression."
53 (while (string-match find string)
54 (setq string (replace-match repl nil t string)))
55 string)
56
57
58(defun gs-options (device file)
59 "Return a list of command line options with place-holders replaced.
60DEVICE is the value to substitute for the place-holder `<device>',
61FILE is the value to substitute for the place-holder `<file>'."
62 (mapcar #'(lambda (option)
63 (setq option (gs-replace-in-string option "<device>" device)
64 option (gs-replace-in-string option "<file>" file)))
65 gs-options))
66
67
68;; The GHOSTVIEW property (taken from gv 3.5.8).
69;;
70;; Type:
71;;
72;; STRING
73;;
74;; Parameters:
75;;
76;; BPIXMAP ORIENT LLX LLY URX URY XDPI YDPI [LEFT BOTTOM TOP RIGHT]
77;;
78;; Scanf format: "%d %d %d %d %d %d %f %f %d %d %d %d"
79;;
80;; Explanation of parameters:
81;;
82;; BPIXMAP: pixmap id of the backing pixmap for the window. If no
83;; pixmap is to be used, this parameter should be zero. This
84;; parameter must be zero when drawing on a pixmap.
85;;
86;; ORIENT: orientation of the page. The number represents clockwise
87;; rotation of the paper in degrees. Permitted values are 0, 90, 180,
88;; 270.
89;;
90;; LLX, LLY, URX, URY: Bounding box of the drawable. The bounding box
91;; is specified in PostScript points in default user coordinates.
92;;
93;; XDPI, YDPI: Resolution of window. (This can be derived from the
94;; other parameters, but not without roundoff error. These values are
95;; included to avoid this error.)
96;;
97;; LEFT, BOTTOM, TOP, RIGHT: (optional) Margins around the window.
98;; The margins extend the imageable area beyond the boundaries of the
99;; window. This is primarily used for popup zoom windows. I have
100;; encountered several instances of PostScript programs that position
101;; themselves with respect to the imageable area. The margins are
102;; specified in PostScript points. If omitted, the margins are
103;; assumed to be 0.
104
105(defun gs-width-in-pt (frame pixel-width)
106 "Return, on FRAME, pixel width PIXEL-WIDTH tranlated to pt."
107 (let ((mm (* (float pixel-width)
108 (/ (float (x-display-mm-width frame))
109 (float (x-display-pixel-width frame))))))
110 (/ (* 25.4 mm) 72.0)))
111
112
113(defun gs-height-in-pt (frame pixel-height)
114 "Return, on FRAME, pixel height PIXEL-HEIGHT tranlated to pt."
115 (let ((mm (* (float pixel-height)
116 (/ (float (x-display-mm-height frame))
117 (float (x-display-pixel-height frame))))))
118 (/ (* 25.4 mm) 72.0)))
119
120
121(defun gs-set-ghostview-window-prop (frame spec img-width img-height)
122 "Set the `GHOSTVIEW' window property of FRAME.
123SPEC is a GS image specification. IMG-WIDTH is the width of the
124requested image, and IMG-HEIGHT is the height of the requested
125image in pixels."
126 (let* ((box (plist-get (cdr spec) :bounding-box))
127 (llx (nth 0 box))
128 (lly (nth 1 box))
129 (urx (nth 2 box))
130 (ury (nth 3 box))
131 (rotation (or (plist-get (cdr spec) :rotate) 0))
132 ;; The pixel width IMG-WIDTH of the pixmap gives the
133 ;; dots, URX - LLX give the inch.
134 (in-width (/ (- urx llx) 72.0))
135 (in-height (/ (- ury lly) 72.0))
136 (xdpi (/ img-width in-width))
137 (ydpi (/ img-height in-height)))
138 (x-change-window-property "GHOSTVIEW"
139 (format "0 %d %d %d %d %d %g %g"
140 rotation llx lly urx ury xdpi ydpi)
141 frame)))
142
143
144(defun gs-set-ghostview-colors-window-prop (frame pixel-colors)
145 "Set the `GHOSTVIEW_COLORS' environment variable depending on FRAME."
146 (let ((mode (cond ((x-display-color-p frame) "Color")
147 ((x-display-grayscale-p frame) "Grayscale")
148 (t "Monochrome"))))
149 (x-change-window-property "GHOSTVIEW_COLORS"
150 (format "%s %s" mode pixel-colors))))
151
152
153;
154;;;###autoload
155(defun gs-load-image (frame spec img-width img-height window-and-pixmap-id
156 pixel-colors)
157 "Load a PS image for display on FRAME.
158SPEC is an image specification, IMG-HEIGHT and IMG-WIDTH are width
159and height of the image in pixels. WINDOW-AND-PIXMAP-ID is a string of
160the form \"WINDOW-ID PIXMAP-ID\". Value is non-nil if successful."
161 (unwind-protect
162 (let ((file (plist-get (cdr spec) :file))
163 gs)
164 (gs-set-ghostview-window-prop frame spec img-width img-height)
165 (gs-set-ghostview-colors-window-prop frame pixel-colors)
166 (setenv "GHOSTVIEW" window-and-pixmap-id)
167 (setq gs (apply 'start-process "gs" "*GS*" gs-program
168 (gs-options gs-device file)))
169 (process-kill-without-query gs)
170 gs)
171 nil))
172
173
174;(defun gs-put-tiger ()
175; (let* ((ps-file "/usr/local/share/ghostscript/5.10/examples/tiger.ps")
176; (spec `(image :type ghostscript
177; :pt-width 200 :pt-height 200
178; :bounding-box (22 171 567 738)
179; :file ,ps-file)))
180; (put-text-property 1 2 'display spec)))
181;
182
183(provide 'gs)
184
185;; gs.el ends here.
diff --git a/lisp/image.el b/lisp/image.el
new file mode 100644
index 00000000000..800196ddc65
--- /dev/null
+++ b/lisp/image.el
@@ -0,0 +1,192 @@
1;;; image.el --- image API
2
3;; Copyright (C) 1998 Free Software Foundation, Inc.
4
5;; This file is part of GNU Emacs.
6
7;; GNU Emacs is free software; you can redistribute it and/or modify
8;; it under the terms of the GNU General Public License as published by
9;; the Free Software Foundation; either version 2, or (at your option)
10;; any later version.
11
12;; GNU Emacs is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details.
16
17;; You should have received a copy of the GNU General Public License
18;; along with GNU Emacs; see the file COPYING. If not, write to the
19;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20;; Boston, MA 02111-1307, USA.
21
22;;; Commentary:
23
24;;; Code:
25
26(defconst image-type-regexps
27 '(("^/\\*.*XPM.\\*/" . xpm)
28 ("^P[1-6]" . pbm)
29 ("^GIF8" . gif)
30 ("JFIF" . jpeg)
31 ("^\211PNG\r\n" . png)
32 ("^#define" . xbm)
33 ("^\\(MM\0\\*\\)\\|\\(II\\*\0\\)" . tiff)
34 ("^%!PS" . ghostscript))
35 "Alist of (REGEXP . IMAGE-TYPE) pairs used to auto-detect image types.
36When the first bytes of an image file match REGEXP, it is assumed to
37be of image type IMAGE-TYPE.")
38
39
40;;;###autoload
41(defun image-type-from-file-header (file)
42 "Determine the type of image file FILE from its first few bytes.
43Value is a symbol specifying the image type, or nil if type cannot
44be determined."
45 (unless (file-name-directory file)
46 (setq file (concat data-directory file)))
47 (setq file (expand-file-name file))
48 (let ((header (with-temp-buffer
49 (insert-file-contents-literally file nil 0 256)
50 (buffer-string)))
51 (types image-type-regexps)
52 type)
53 (while (and types (null type))
54 (let ((regexp (car (car types)))
55 (image-type (cdr (car types))))
56 (when (string-match regexp header)
57 (setq type image-type))
58 (setq types (cdr types))))
59 type))
60
61
62;;;###autoload
63(defun image-type-available-p (type)
64 "Value is non-nil if image type TYPE is available.
65Image types are symbols like `xbm' or `jpeg'."
66 (not (null (memq type image-types))))
67
68
69;;;###autoload
70(defun create-image (file &optional type &rest props)
71 "Create an image which will be loaded from FILE.
72Optional TYPE is a symbol describing the image type. If TYPE is omitted
73or nil, try to determine the image file type from its first few bytes.
74If that doesn't work, use FILE's extension.as image type.
75Optional PROPS are additional image attributes to assign to the image,
76like, e.g. `:heuristic-mask t'.
77Value is the image created, or nil if images of type TYPE are not supported."
78 (unless (stringp file)
79 (error "Invalid image file name %s" file))
80 (unless (or type
81 (setq type (image-type-from-file-header file)))
82 (let ((extension (file-name-extension file)))
83 (unless extension
84 (error "Cannot determine image type"))
85 (setq type (intern extension))))
86 (unless (symbolp type)
87 (error "Invalid image type %s" type))
88 (when (image-type-available-p type)
89 (append (list 'image :type type :file file) props)))
90
91
92;;;###autoload
93(defun put-image (image pos &optional buffer area)
94 "Put image IMAGE in front of POS in BUFFER.
95IMAGE must be an image created with `create-image' or `defimage'.
96POS may be an integer or marker.
97BUFFER nil or omitted means use the current buffer.
98AREA is where to display the image. AREA nil or omitted means
99display it in the text area, a value of `left-margin' means
100display it in the left marginal area, a value of `right-margin'
101means display it in the right marginal area.
102IMAGE is displayed by putting an overlay into BUFFER with a
103`before-string' that has a `display' property whose value is the
104image."
105 (unless buffer
106 (setq buffer (current-buffer)))
107 (unless (eq (car image) 'image)
108 (error "Not an image: %s" image))
109 (unless (or (null area) (memq area '(left-margin right-margin)))
110 (error "Invalid area %s" area))
111 (let ((overlay (make-overlay pos pos buffer))
112 (string (make-string 1 ?x))
113 (prop (if (null area) image (cons area image))))
114 (put-text-property 0 1 'display prop string)
115 (overlay-put overlay 'put-image t)
116 (overlay-put overlay 'before-string string)))
117
118
119;;;###autoload
120(defun insert-image (image &optional area)
121 "Insert IMAGE into current buffer at point.
122AREA is where to display the image. AREA nil or omitted means
123display it in the text area, a value of `left-margin' means
124display it in the left marginal area, a value of `right-margin'
125means display it in the right marginal area.
126IMAGE is displayed by inserting an \"x\" into the current buffer
127having a `display' property whose value is the image."
128 (unless (eq (car image) 'image)
129 (error "Not an image: %s" image))
130 (unless (or (null area) (memq area '(left-margin right-margin)))
131 (error "Invalid area %s" area))
132 (insert "x")
133 (add-text-properties (1- (point)) (point)
134 (list 'display (if (null area) image (cons area image))
135 'rear-nonsticky (list 'display))))
136
137
138;;;###autoload
139(defun remove-images (start end &optional buffer)
140 "Remove images between START and END in BUFFER.
141Remove only images that were put in BUFFER with calls to `put-image'.
142BUFFER nil or omitted means use the current buffer."
143 (unless buffer
144 (setq buffer (current-buffer)))
145 (let ((overlays (overlays-in start end)))
146 (while overlays
147 (let ((overlay (car overlays)))
148 (when (overlay-get overlay 'put-image)
149 (delete-overlay overlay)
150 (setq overlays (cdr overlays)))))))
151
152
153;;;###autoload
154(defmacro defimage (symbol specs &optional doc)
155 "Define SYMBOL as an image.
156
157SPECS is a list of image specifications. DOC is an optional
158documentation string.
159
160Each image specification in SPECS is a property list. The contents of
161a specification are image type dependent. All specifications must at
162least contain the properties `:type TYPE' and `:file FILE', where TYPE
163is a symbol specifying the image type, e.g. `xbm', and FILE is the
164file to load the image from. The first image specification whose TYPE
165is supported, and FILE exists, is used to define SYMBOL.
166
167Example:
168
169 (defimage test-image ((:type xpm :file \"~/test1.xpm\")
170 (:type xbm :file \"~/test1.xbm\")))"
171 (let (image)
172 (while (and specs (null image))
173 (let* ((spec (car specs))
174 (type (plist-get spec :type))
175 (file (plist-get spec :file)))
176 (when (and (image-type-available-p type) (stringp file))
177 (setq file (expand-file-name file))
178 (unless (file-name-absolute-p file)
179 (setq file (concat data-directory "/" file)))
180 (when (file-exists-p file)
181 (setq image (cons 'image spec))))
182 (setq specs (cdr specs))))
183 `(defvar ,symbol ',image ,doc)))
184
185
186(provide 'image)
187
188 ;; image.el ends here.
189
190
191
192
diff --git a/lisp/jit-lock.el b/lisp/jit-lock.el
new file mode 100644
index 00000000000..ffc4b1be3bc
--- /dev/null
+++ b/lisp/jit-lock.el
@@ -0,0 +1,433 @@
1;;; jit-lock.el --- just-in-time fontification.
2
3;; Copyright (C) 1998 Free Software Foundation, Inc.
4
5;; Author: Gerd Moellmann <gerd@gnu.org>
6;; Keywords: faces files
7;; Version: 1.0
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
25
26;;; Commentary:
27
28;; Just-in-time fontification, triggered by C redisplay code.
29
30;;; Code:
31
32
33(require 'font-lock)
34
35(eval-when-compile
36 (defmacro with-buffer-prepared-for-font-lock (&rest body)
37 "Execute BODY in current buffer, overriding several variables.
38Preserves the `buffer-modified-p' state of the current buffer."
39 `(let ((modified (buffer-modified-p))
40 (buffer-undo-list t)
41 (inhibit-read-only t)
42 (inhibit-point-motion-hooks t)
43 before-change-functions
44 after-change-functions
45 deactivate-mark
46 buffer-file-name
47 buffer-file-truename)
48 ,@body
49 (set-buffer-modified-p modified))))
50
51
52
53;;; Customization.
54
55(defcustom jit-lock-chunk-size 500
56 "*Font-lock chunks of this many characters, or smaller."
57 :type 'integer
58 :group 'jit-lock)
59
60
61(defcustom jit-lock-stealth-time 3
62 "*Time in seconds to wait before beginning stealth fontification.
63Stealth fontification occurs if there is no input within this time.
64If nil, means stealth fontification is never performed.
65
66The value of this variable is used when JIT Lock mode is turned on."
67 :type '(choice (const :tag "never" nil)
68 (number :tag "seconds"))
69 :group 'jit-lock)
70
71
72(defcustom jit-lock-stealth-nice 0.125
73 "*Time in seconds to pause between chunks of stealth fontification.
74Each iteration of stealth fontification is separated by this amount of time,
75thus reducing the demand that stealth fontification makes on the system.
76If nil, means stealth fontification is never paused.
77To reduce machine load during stealth fontification, at the cost of stealth
78taking longer to fontify, you could increase the value of this variable.
79See also `jit-lock-stealth-load'."
80 :type '(choice (const :tag "never" nil)
81 (number :tag "seconds"))
82 :group 'jit-lock)
83
84
85(defcustom jit-lock-stealth-load
86 (if (condition-case nil (load-average) (error)) 200)
87 "*Load in percentage above which stealth fontification is suspended.
88Stealth fontification pauses when the system short-term load average (as
89returned by the function `load-average' if supported) goes above this level,
90thus reducing the demand that stealth fontification makes on the system.
91If nil, means stealth fontification is never suspended.
92To reduce machine load during stealth fontification, at the cost of stealth
93taking longer to fontify, you could reduce the value of this variable.
94See also `jit-lock-stealth-nice'."
95 :type (if (condition-case nil (load-average) (error))
96 '(choice (const :tag "never" nil)
97 (integer :tag "load"))
98 '(const :format "%t: unsupported\n" nil))
99 :group 'jit-lock)
100
101
102(defcustom jit-lock-stealth-verbose nil
103 "*If non-nil, means stealth fontification should show status messages."
104 :type 'boolean
105 :group 'jit-lock)
106
107
108(defcustom jit-lock-defer-contextually 'syntax-driven
109 "*If non-nil, means deferred fontification should be syntactically true.
110If nil, means deferred fontification occurs only on those lines modified. This
111means where modification on a line causes syntactic change on subsequent lines,
112those subsequent lines are not refontified to reflect their new context.
113If t, means deferred fontification occurs on those lines modified and all
114subsequent lines. This means those subsequent lines are refontified to reflect
115their new syntactic context, either immediately or when scrolling into them.
116If any other value, e.g., `syntax-driven', means deferred syntactically true
117fontification occurs only if syntactic fontification is performed using the
118buffer mode's syntax table, i.e., only if `font-lock-keywords-only' is nil.
119
120The value of this variable is used when JIT Lock mode is turned on."
121 :type '(choice (const :tag "never" nil)
122 (const :tag "always" t)
123 (other :tag "syntax-driven" syntax-driven))
124 :group 'jit-lock)
125
126
127
128;;; Variables that are not customizable.
129
130(defvar jit-lock-mode nil
131 "Non-nil means Just-in-time Lock mode is active.")
132(make-variable-buffer-local 'jit-lock-mode)
133
134
135(defvar jit-lock-first-unfontify-pos nil
136 "Consider text after this position as unfontified.")
137(make-variable-buffer-local 'jit-lock-first-unfontify-pos)
138
139
140(defvar jit-lock-stealth-timer nil
141 "Timer for stealth fontification in Just-in-time Lock mode.")
142
143
144
145;;; JIT lock mode
146
147;;;###autoload
148(defun jit-lock-mode (arg)
149 "Toggle Just-in-time Lock mode.
150With arg, turn Just-in-time Lock mode on if and only if arg is positive.
151Enable it automatically by customizing group `font-lock'.
152
153When Just-in-time Lock mode is enabled, fontification is different in the
154following ways:
155
156- Demand-driven buffer fontification triggered by Emacs C code.
157 This means initial fontification of the whole buffer does not occur.
158 Instead, fontification occurs when necessary, such as when scrolling
159 through the buffer would otherwise reveal unfontified areas. This is
160 useful if buffer fontification is too slow for large buffers.
161
162- Stealthy buffer fontification if `jit-lock-stealth-time' is non-nil.
163 This means remaining unfontified areas of buffers are fontified if Emacs has
164 been idle for `jit-lock-stealth-time' seconds, while Emacs remains idle.
165 This is useful if any buffer has any deferred fontification.
166
167- Deferred context fontification if `jit-lock-defer-contextually' is
168 non-nil. This means fontification updates the buffer corresponding to
169 true syntactic context, after `jit-lock-stealth-time' seconds of Emacs
170 idle time, while Emacs remains idle. Otherwise, fontification occurs
171 on modified lines only, and subsequent lines can remain fontified
172 corresponding to previous syntactic contexts. This is useful where
173 strings or comments span lines.
174
175Stealth fontification only occurs while the system remains unloaded.
176If the system load rises above `jit-lock-stealth-load' percent, stealth
177fontification is suspended. Stealth fontification intensity is controlled via
178the variable `jit-lock-stealth-nice' and `jit-lock-stealth-lines'."
179 (interactive "P")
180 (setq jit-lock-mode (if arg
181 (> (prefix-numeric-value arg) 0)
182 (not jit-lock-mode)))
183 (cond ((and jit-lock-mode
184 (or (not (boundp 'font-lock-mode))
185 (not font-lock-mode)))
186 ;; If font-lock is not on, turn it on, with Just-in-time
187 ;; Lock mode as support mode; font-lock will call us again.
188 (let ((font-lock-support-mode 'jit-lock-mode))
189 (font-lock-mode t)))
190
191 ;; Turn Just-in-time Lock mode on.
192 (jit-lock-mode
193 ;; Setting `font-lock-fontified' makes font-lock believe the
194 ;; buffer is already fontified, so that it won't highlight
195 ;; the whole buffer.
196 (make-local-variable 'font-lock-fontified)
197 (setq font-lock-fontified t)
198
199 (setq jit-lock-first-unfontify-pos nil)
200
201 ;; Install an idle timer for stealth fontification.
202 (when (and jit-lock-stealth-time
203 (null jit-lock-stealth-timer))
204 (setq jit-lock-stealth-timer
205 (run-with-idle-timer jit-lock-stealth-time
206 jit-lock-stealth-time
207 'jit-lock-stealth-fontify)))
208
209 ;; Add a hook for deferred contectual fontification.
210 (when (or (eq jit-lock-defer-contextually 'always)
211 (and (not (eq jit-lock-defer-contextually 'never))
212 (null font-lock-keywords-only)))
213 (add-hook 'after-change-functions 'jit-lock-after-change))
214
215 ;; Install the fontification hook.
216 (add-hook 'fontification-functions 'jit-lock-function))
217
218 ;; Turn Just-in-time Lock mode off.
219 (t
220 ;; Cancel our idle timer.
221 (when jit-lock-stealth-timer
222 (cancel-timer jit-lock-stealth-timer)
223 (setq jit-lock-stealth-timer nil))
224
225 ;; Remove hooks.
226 (remove-hook 'after-change-functions 'jit-lock-after-change)
227 (remove-hook 'fontification-functions 'jit-lock-function))))
228
229
230;;;###autoload
231(defun turn-on-jit-lock ()
232 "Unconditionally turn on Just-in-time Lock mode."
233 (jit-lock-mode 1))
234
235
236
237;;; On demand fontification.
238
239(defun jit-lock-function (start)
240 "Fontify current buffer starting at position START.
241This function is added to `fontification-functions' when `jit-lock-mode'
242is active."
243 (when jit-lock-mode
244 (with-buffer-prepared-for-font-lock
245 (let ((end (min (point-max) (+ start jit-lock-chunk-size)))
246 (parse-sexp-lookup-properties font-lock-syntactic-keywords)
247 (old-syntax-table (syntax-table))
248 (font-lock-beginning-of-syntax-function nil)
249 next)
250 (when font-lock-syntax-table
251 (set-syntax-table font-lock-syntax-table))
252 (save-excursion
253 (save-restriction
254 (widen)
255 (save-match-data
256 (condition-case error
257 ;; Fontify chunks beginning at START. The end of a
258 ;; chunk is either `end', or the start of a region
259 ;; before `end' that has already been fontified.
260 (while start
261 ;; Determine the end of this chunk.
262 (setq next (or (text-property-any start end 'fontified t)
263 end))
264
265 ;; Goto to the start of the chunk. Make sure we
266 ;; start fontifying at the beginning of the line
267 ;; containing the chunk start because font-lock
268 ;; functions seem to expects this, if I believe
269 ;; lazy-lock.
270 (goto-char start)
271 (unless (bolp)
272 (beginning-of-line)
273 (setq start (point)))
274
275 ;; Fontify the chunk, and mark it as fontified.
276 (unwind-protect
277 (font-lock-fontify-region start end nil))
278
279 ;; Even if we got an error above, mark the region as
280 ;; fontified. If we get an error now, we're
281 ;; probably getting the same error the next time we
282 ;; try, so it's moot to try again.
283 (add-text-properties start next '(fontified t))
284
285 ;; Find the start of the next chunk, if any.
286 (setq start (text-property-any next end 'fontified nil)))
287
288 ((error quit)
289 (message "Fontifying region...%s" error))))))
290
291 ;; Restore previous buffer settings.
292 (set-syntax-table old-syntax-table)))))
293
294
295(defun jit-lock-after-fontify-buffer ()
296 "Mark the current buffer as fontified.
297Called from `font-lock-after-fontify-buffer."
298 (with-buffer-prepared-for-font-lock
299 (add-text-properties (point-min) (point-max) '(fontified t))))
300
301
302(defun jit-lock-after-unfontify-buffer ()
303 "Mark the current buffer as unfontified.
304Called from `font-lock-after-fontify-buffer."
305 (with-buffer-prepared-for-font-lock
306 (remove-text-properties (point-min) (point-max) '(fontified nil))))
307
308
309
310;;; Stealth fontification.
311
312(defsubst jit-lock-stealth-chunk-start (around)
313 "Return the start of the next chunk to fontify around position AROUND..
314Value is nil if there is nothing more to fontify."
315 (save-restriction
316 (widen)
317 (let ((prev (previous-single-property-change around 'fontified))
318 (next (text-property-any around (point-max) 'fontified nil))
319 (prop (get-text-property around 'fontified)))
320 (cond ((and (null prop)
321 (< around (point-max)))
322 ;; Text at position AROUND is not fontified. The value of
323 ;; prev, if non-nil, is the start of the region of
324 ;; unfontified text. As a special case, prop will always
325 ;; be nil at point-max. So don't handle that case here.
326 (max (or prev (point-min))
327 (- around jit-lock-chunk-size)))
328
329 ((null prev)
330 ;; Text at AROUND is fontified, and everything up to
331 ;; point-min is. Return the value of next. If that is
332 ;; nil, there is nothing left to fontify.
333 next)
334
335 ((or (null next)
336 (< (- around prev) (- next around)))
337 ;; We either have no unfontified text following AROUND, or
338 ;; the unfontified text in front of AROUND is nearer. The
339 ;; value of prev is the end of the region of unfontified
340 ;; text in front of AROUND.
341 (let ((start (previous-single-property-change prev 'fontified)))
342 (max (or start (point-min))
343 (- prev jit-lock-chunk-size))))
344
345 (t
346 next)))))
347
348
349(defun jit-lock-stealth-fontify ()
350 "Fontify buffers stealthily.
351This functions is called after Emacs has been idle for
352`jit-lock-stealth-time' seconds."
353 (unless (or executing-kbd-macro
354 (window-minibuffer-p (selected-window)))
355 (let ((buffers (buffer-list))
356 minibuffer-auto-raise
357 message-log-max)
358 (while (and buffers
359 (not (input-pending-p)))
360 (let ((buffer (car buffers)))
361 (setq buffers (cdr buffers))
362 (with-current-buffer buffer
363 (when jit-lock-mode
364 ;; This is funny. Calling sit-for with 3rd arg non-nil
365 ;; so that it doesn't redisplay, internally calls
366 ;; wait_reading_process_input also with a parameter
367 ;; saying "don't redisplay." Since this function here
368 ;; is called periodically, this effectively leads to
369 ;; process output not being redisplayed at all because
370 ;; redisplay_internal is never called. (That didn't
371 ;; work in the old redisplay either.) So, we learn that
372 ;; we mustn't call sit-for that way here. But then, we
373 ;; have to be cautious not to call sit-for in a widened
374 ;; buffer, since this could display hidden parts of that
375 ;; buffer. This explains the seemingly weird use of
376 ;; save-restriction/widen here.
377
378 (with-temp-message (if jit-lock-stealth-verbose
379 (concat "JIT stealth lock "
380 (buffer-name)))
381
382 ;; Perform deferred unfontification, if any.
383 (when jit-lock-first-unfontify-pos
384 (save-restriction
385 (widen)
386 (when (and (>= jit-lock-first-unfontify-pos (point-min))
387 (< jit-lock-first-unfontify-pos (point-max)))
388 (with-buffer-prepared-for-font-lock
389 (put-text-property jit-lock-first-unfontify-pos
390 (point-max) 'fontified nil))
391 (setq jit-lock-first-unfontify-pos nil))))
392
393 (let (start
394 (nice (or jit-lock-stealth-nice 0))
395 (point (point)))
396 (while (and (setq start
397 (jit-lock-stealth-chunk-start point))
398 (sit-for nice))
399
400 ;; Wait a little if load is too high.
401 (when (and jit-lock-stealth-load
402 (> (car (load-average)) jit-lock-stealth-load))
403 (sit-for (or jit-lock-stealth-time 30)))
404
405 ;; Unless there's input pending now, fontify.
406 (unless (input-pending-p)
407 (jit-lock-function start))))))))))))
408
409
410
411;;; Deferred fontification.
412
413(defun jit-lock-after-change (start end old-len)
414 "Mark the rest of the buffer as not fontified after a change.
415Installed on `after-change-functions'.
416START and END are the start and end of the changed text. OLD-LEN
417is the pre-change length.
418This function ensures that lines following the change will be refontified
419in case the syntax of those lines has changed. Refontification
420will take place when text is fontified stealthily."
421 ;; Don't do much here---removing text properties is too slow for
422 ;; fast typers, giving them the impression of Emacs not being
423 ;; very responsive.
424 (when jit-lock-mode
425 (setq jit-lock-first-unfontify-pos
426 (if jit-lock-first-unfontify-pos
427 (min jit-lock-first-unfontify-pos start)
428 start))))
429
430
431(provide 'jit-lock)
432
433;; jit-lock.el ends here
diff --git a/lisp/tooltip.el b/lisp/tooltip.el
new file mode 100644
index 00000000000..ab21bf13dfd
--- /dev/null
+++ b/lisp/tooltip.el
@@ -0,0 +1,476 @@
1;;; tooltip.el --- Show tooltip windows
2
3;; Copyright (C) 1997 Free Software Foundation, Inc.
4
5;; Author: Gerd Moellmann <gerd@acm.org>
6;; Keywords: help c mouse tools
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs 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 2, or (at your option)
13;; any later version.
14
15;; GNU Emacs 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 GNU Emacs; see the file COPYING. If not, write to the
22;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23;; Boston, MA 02111-1307, USA.
24
25;;; Commentary:
26
27;; Put into your `.emacs'
28
29;; (require 'tooltip)
30;; (tooltip-mode 1)
31
32
33
34;;; Code:
35
36(eval-when-compile
37 (require 'cl)
38 (require 'comint)
39 (require 'gud))
40
41(provide 'tooltip)
42
43
44;;; Customizable settings
45
46(defgroup tooltip nil
47 "Customization group for the `tooltip' package."
48 :group 'help
49 :group 'c
50 :group 'mouse
51 :group 'tools
52 :tag "Tool Tips")
53
54
55(defcustom tooltip-delay 1.0
56 "Seconds to wait before displaying a tooltip the first time."
57 :tag "Delay"
58 :type 'number
59 :group 'tooltip)
60
61
62(defcustom tooltip-short-delay 0.1
63 "Seconds to wait between subsequent tooltips on different items."
64 :tag "Short delay"
65 :type 'number
66 :group 'tooltip)
67
68
69(defcustom tooltip-recent-seconds 1
70 "Display tooltips after `tooltip-short-delay' if changing tip items
71within this many seconds."
72 :tag "Recent seconds"
73 :type 'number
74 :group 'tooltip)
75
76
77(defcustom tooltip-frame-parameters
78 '((name . "tooltip")
79 (foreground-color . "black")
80 (background-color . "lightyellow")
81 (internal-border-width . 5)
82 (border-color . "lightyellow")
83 (border-width . 1))
84 "Frame parameters used for tooltips."
85 :type 'sexp
86 :tag "Frame Parameters"
87 :group 'tooltip)
88
89
90(defcustom tooltip-gud-tips-p nil
91 "Non-nil means show tooltips in GUD sessions."
92 :type 'boolean
93 :tag "GUD"
94 :group 'tooltip)
95
96
97(defcustom tooltip-gud-modes '(gud-mode c-mode c++-mode)
98 "List of modes for which to enable GUD tips."
99 :type 'sexp
100 :tag "GUD modes"
101 :group 'tooltip)
102
103
104(defcustom tooltip-gud-display
105 '((eq (tooltip-event-buffer tooltip-gud-event)
106 (marker-buffer overlay-arrow-position)))
107 "List of forms determining where GUD tooltips are displayed.
108
109Forms in the list are combined with AND. The default is to display
110only tooltips in the buffer containing the overlay arrow."
111 :type 'sexp
112 :tag "GUD buffers predicate"
113 :group 'tooltip)
114
115
116
117;;; Variables that are not customizable.
118
119(defvar tooltip-hook nil
120 "Functions to call to display tooltips.
121Each function is called with one argument EVENT which is a copy of
122the last mouse movement event that occurred.")
123
124
125(defvar tooltip-timeout-id nil
126 "The id of the timeout started when Emacs becomes idle.")
127
128
129(defvar tooltip-last-mouse-motion-event nil
130 "A copy of the last mouse motion event seen.")
131
132
133(defvar tooltip-hide-time nil
134 "Time when the last tooltip was hidden.")
135
136
137(defvar tooltip-mode nil
138 "Non-nil means tooltip mode is on.")
139
140
141(defvar tooltip-gud-debugger nil
142 "The debugger for which we show tooltips.")
143
144
145
146;;; Event accessors
147
148(defun tooltip-event-buffer (event)
149 "Return the buffer over which event EVENT occurred.
150This might return nil if the event did not occur over a buffer."
151 (let ((window (posn-window (event-end event))))
152 (and window (window-buffer window))))
153
154
155
156;;; Switching tooltips on/off
157
158;; We don't set track-mouse globally because this is a big redisplay
159;; problem in buffers having a pre-command-hook or such installed,
160;; which does a set-buffer, like the summary buffer of Gnus. Calling
161;; set-buffer prevents redisplay optimizations, so every mouse motion
162;; would be accompanied by a full redisplay.
163
164;;;###autoload
165(defun tooltip-mode (&optional arg)
166 "Mode for tooltip display.
167With ARG, turn tooltip mode on if and only if ARG is positive."
168 (interactive "P")
169 (let* ((on (if arg
170 (> (prefix-numeric-value arg) 0)
171 (not tooltip-mode)))
172 (hook-fn (if on 'add-hook 'remove-hook)))
173 (setq tooltip-mode on)
174 (funcall hook-fn 'change-major-mode-hook 'tooltip-change-major-mode)
175 (tooltip-activate-mouse-motions-if-enabled)
176 (funcall hook-fn 'pre-command-hook 'tooltip-hide)
177 (funcall hook-fn 'tooltip-hook 'tooltip-gud-tips)
178 (funcall hook-fn 'tooltip-hook 'tooltip-help-tips)
179 (setq show-help-function (if on 'tooltip-show-help-function nil))
180 ;; `ignore' is the default binding for mouse movements.
181 (define-key global-map [mouse-movement]
182 (if on 'tooltip-mouse-motion 'ignore))
183 (when (and on tooltip-gud-tips-p)
184 (global-set-key [S-mouse-3] 'tooltip-gud-toggle-dereference)
185 (add-hook 'gdb-mode-hook
186 #'(lambda () (setq tooltip-gud-debugger 'gdb)))
187 (add-hook 'sdb-mode-hook
188 #'(lambda () (setq tooltip-gud-debugger 'sdb)))
189 (add-hook 'dbx-mode-hook
190 #'(lambda () (setq tooltip-gud-debugger 'dbx)))
191 (add-hook 'xdb-mode-hook
192 #'(lambda () (setq tooltip-gud-debugger 'xdb)))
193 (add-hook 'perldb-mode-hook
194 #'(lambda () (setq tooltip-gud-debugger 'perldb))))))
195
196
197
198;;; Timeout for tooltip display
199
200(defun tooltip-float-time ()
201 "Return the values of `current-time' as a float."
202 (let ((now (current-time)))
203 (+ (* 65536.0 (nth 0 now))
204 (nth 1 now)
205 (/ (nth 2 now) 1000000.0))))
206
207
208(defun tooltip-delay ()
209 "Return the delay in seconds for the next tooltip."
210 (let ((delay tooltip-delay)
211 (now (tooltip-float-time)))
212 (when (and tooltip-hide-time
213 (< (- now tooltip-hide-time) tooltip-recent-seconds))
214 (setq delay tooltip-short-delay))
215 delay))
216
217
218(defun tooltip-disable-timeout ()
219 "Disable the tooltip timeout."
220 (when tooltip-timeout-id
221 (disable-timeout tooltip-timeout-id)
222 (setq tooltip-timeout-id nil)))
223
224
225(defun tooltip-add-timeout ()
226 "Add a one-shot timeout to call function tooltip-timeout."
227 (setq tooltip-timeout-id
228 (add-timeout (tooltip-delay) 'tooltip-timeout nil)))
229
230
231(defun tooltip-timeout (object)
232 "Function called when timer with id tooltip-timeout-id fires."
233 (run-hook-with-args-until-success 'tooltip-hook
234 tooltip-last-mouse-motion-event))
235
236
237
238;;; Reacting on mouse movements
239
240(defun tooltip-change-major-mode ()
241 "Function added to `change-major-mode-hook' when tooltip mode is on."
242 (add-hook 'post-command-hook 'tooltip-activate-mouse-motions-if-enabled))
243
244
245(defun tooltip-activate-mouse-motions-if-enabled ()
246 "Reconsider for all buffers whether mouse motion events are desired."
247 (remove-hook 'post-command-hook 'tooltip-activate-mouse-motions-if-enabled)
248 (let ((buffers (buffer-list)))
249 (save-excursion
250 (while buffers
251 (set-buffer (car buffers))
252 (if (and tooltip-mode
253 tooltip-gud-tips-p
254 (memq major-mode tooltip-gud-modes))
255 (tooltip-activate-mouse-motions t)
256 (tooltip-activate-mouse-motions nil))
257 (setq buffers (cdr buffers))))))
258
259
260(defun tooltip-activate-mouse-motions (activatep)
261 "Activate/deactivate mouse motion events for the current buffer.
262ACTIVATEP non-nil means activate mouse motion events."
263 (if activatep
264 (progn
265 (make-local-variable 'track-mouse)
266 (setq track-mouse t))
267 (kill-local-variable 'track-mouse)))
268
269
270(defun tooltip-mouse-motion (event)
271 "Command handler for mouse movement events in `global-map'."
272 (interactive "e")
273 (tooltip-hide)
274 (when (car (mouse-pixel-position))
275 (setq tooltip-last-mouse-motion-event (copy-sequence event))
276 (tooltip-add-timeout)))
277
278
279
280;;; Displaying tips
281
282(defun tooltip-show (text)
283 "Show a tooltip window at the current mouse position displaying TEXT."
284 (x-show-tip text (selected-frame) tooltip-frame-parameters))
285
286
287(defun tooltip-hide (&optional ignored-arg)
288 "Hide a tooltip, if one is displayed.
289Value is non-nil if tooltip was open."
290 (tooltip-disable-timeout)
291 (when (x-hide-tip)
292 (setq tooltip-hide-time (tooltip-float-time))))
293
294
295
296;;; Debugger-related functions
297
298(defun tooltip-identifier-from-point (point)
299 "Extract the identifier at POINT, if any.
300Value is nil if no identifier exists at point. Identifier extraction
301is based on the current syntax table."
302 (save-excursion
303 (goto-char point)
304 (let ((start (progn (skip-syntax-backward "w_") (point))))
305 (unless (looking-at "[0-9]")
306 (skip-syntax-forward "w_")
307 (when (> (point) start)
308 (buffer-substring start (point)))))))
309
310
311(defmacro tooltip-region-active-p ()
312 "Value is non-nil if the region is currently active."
313 (if (string-match "^GNU" (emacs-version))
314 `(and transient-mark-mode mark-active)
315 `(region-active-p)))
316
317
318(defun tooltip-expr-to-print (event)
319 "Return an expression that should be printed for EVENT.
320If a region is active and the mouse is inside the region, print
321the region. Otherwise, figure out the identifier around the point
322where the mouse is."
323 (save-excursion
324 (set-buffer (tooltip-event-buffer event))
325 (let ((point (posn-point (event-end event))))
326 (if (tooltip-region-active-p)
327 (when (and (<= (region-beginning) point) (<= point (region-end)))
328 (buffer-substring (region-beginning) (region-end)))
329 (tooltip-identifier-from-point point)))))
330
331
332(defun tooltip-process-prompt-regexp (process)
333 "Return regexp matching the prompt of PROCESS at the end of a string.
334The prompt is taken from the value of COMINT-PROMPT-REGEXP in the buffer
335of PROCESS."
336 (let ((prompt-regexp (save-excursion
337 (set-buffer (process-buffer process))
338 comint-prompt-regexp)))
339 ;; Most start with `^' but the one for `sdb' cannot be easily
340 ;; stripped. Code the prompt for `sdb' fixed here.
341 (if (= (aref prompt-regexp 0) ?^)
342 (setq prompt-regexp (substring prompt-regexp 1))
343 (setq prompt-regexp "\\*"))
344 (concat "\n*" prompt-regexp "$")))
345
346
347(defun tooltip-strip-prompt (process output)
348 "Return OUTPUT with any prompt of PROCESS stripped from its end."
349 (let ((prompt-regexp (tooltip-process-prompt-regexp process)))
350 (save-match-data
351 (when (string-match prompt-regexp output)
352 (setq output (substring output 0 (match-beginning 0)))))
353 output))
354
355
356
357;;; Tips for `gud'
358
359(defvar tooltip-gud-original-filter nil
360 "Process filter to restore after GUD output has been received.")
361
362
363(defvar tooltip-gud-dereference nil
364 "Non-nil means print expressions with a `*' in front of them.
365For C this would dereference a pointer expression.")
366
367
368(defvar tooltip-gud-event nil
369 "The mouse movement event that led to a tooltip display.
370This event can be examined by forms in TOOLTIP-GUD-DISPLAY.")
371
372
373(defvar tooltip-gud-debugger nil
374 "A symbol describing the debugger running under GUD.")
375
376
377(defun tooltip-gud-toggle-dereference ()
378 "Toggle whether tooltips should show `* exor' or `expr'."
379 (interactive)
380 (setq tooltip-gud-dereference (not tooltip-gud-dereference))
381 (when (interactive-p)
382 (message "Dereferencing is now %s."
383 (if tooltip-gud-dereference "on" "off"))))
384
385
386(defun tooltip-gud-process-output (process output)
387 "Process debugger output and show it in a tooltip window."
388 (set-process-filter process tooltip-gud-original-filter)
389 (tooltip-show (tooltip-strip-prompt process output)))
390
391
392(defun tooltip-gud-print-command (expr)
393 "Return a suitable command to print the expression EXPR.
394If TOOLTIP-GUD-DEREFERENCE is t, also prepend a `*' to EXPR."
395 (when tooltip-gud-dereference
396 (setq expr (concat "*" expr)))
397 (case tooltip-gud-debugger
398 ((gdb dbx) (concat "print " expr))
399 (xdb (concat "p " expr))
400 (sdb (concat expr "/"))
401 (perldb expr)))
402
403
404(defun tooltip-gud-tips (event)
405 "Show tip for identifier or selection under the mouse. The mouse
406must either point at an identifier or inside a selected region for the
407tip window to be shown. If tooltip-gud-dereference is t, add a `*' in
408front of the printed expression.
409
410This function must return nil if it doesn't handle EVENT."
411 (let (gud-buffer process)
412 (when (and (eventp event)
413 tooltip-gud-tips-p
414 (boundp 'gud-comint-buffer)
415 (setq gud-buffer gud-comint-buffer)
416 (setq process (get-buffer-process gud-buffer))
417 (posn-point (event-end event))
418 (progn (setq tooltip-gud-event event)
419 (eval (cons 'and tooltip-gud-display))))
420 (let ((expr (tooltip-expr-to-print event)))
421 (when expr
422 (setq tooltip-gud-original-filter (process-filter process))
423 (set-process-filter process 'tooltip-gud-process-output)
424 (process-send-string
425 process (concat (tooltip-gud-print-command expr) "\n"))
426 expr)))))
427
428
429
430;;; Tooltip help.
431
432(defvar tooltip-help-message nil
433 "The last help message received via `tooltip-show-help-function'.")
434
435
436(defun tooltip-show-help-function (msg)
437 "Function installed as `show-help-function'.
438MSG is either a help string to display, or nil to cancel the display."
439 (let ((previous-help tooltip-help-message))
440 (setq tooltip-help-message msg)
441 (cond ((null msg)
442 (tooltip-hide))
443 ((or (not (stringp previous-help))
444 (not (string= msg previous-help)))
445 (tooltip-hide)
446 (tooltip-add-timeout))
447 (t
448 (tooltip-disable-timeout)
449 (tooltip-add-timeout)))))
450
451
452(defun tooltip-help-tips (event)
453 "Hook function to display a help tooltip.
454Value is non-nil if this function handled the tip."
455 (when (stringp tooltip-help-message)
456 (tooltip-show tooltip-help-message)
457 (setq tooltip-help-message nil)
458 t))
459
460
461
462;;; Do this after all functions have been defined that are called
463;;; from `tooltip-mode'.
464
465(defcustom tooltip-active nil
466 "*Non-nil means tooltips are active."
467 :tag "Activate tooltips"
468 :type 'boolean
469 :set #'(lambda (symbol value)
470 (set-default symbol value)
471 (tooltip-mode (or value 0)))
472 :require 'tooltip
473 :group 'tooltip)
474
475
476;;; tooltip.el ends here
diff --git a/src/sound.c b/src/sound.c
new file mode 100644
index 00000000000..51ebc70218a
--- /dev/null
+++ b/src/sound.c
@@ -0,0 +1,824 @@
1/* sound.c -- sound support.
2 Copyright (C) 1998 Free Software Foundation.
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
20
21/* Written by Gerd Moellmann <gerd@gnu.org>. Tested with Luigi's
22 driver on FreeBSD 2.2.7 with a SoundBlaster 16. */
23
24#include <config.h>
25
26#if defined HAVE_SOUND
27
28#include <lisp.h>
29#include <fcntl.h>
30#include <unistd.h>
31#include <sys/types.h>
32#include <dispextern.h>
33#include <errno.h>
34
35/* FreeBSD has machine/soundcard.h. Voxware sound driver docs mention
36 sys/soundcard.h. So, let's try whatever's there. */
37
38#ifdef HAVE_MACHINE_SOUNDCARD_H
39#include <machine/soundcard.h>
40#endif
41#ifdef HAVE_SYS_SOUNDCARD_H
42#include <sys/soundcard.h>
43#endif
44
45#define max(X, Y) ((X) > (Y) ? (X) : (Y))
46#define min(X, Y) ((X) < (Y) ? (X) : (Y))
47#define abs(X) ((X) < 0 ? -(X) : (X))
48
49/* Structure forward declarations. */
50
51struct sound_file;
52struct sound_device;
53
54/* The file header of RIFF-WAVE files (*.wav). Files are always in
55 little-endian byte-order. */
56
57struct wav_header
58{
59 u_int32_t magic;
60 u_int32_t length;
61 u_int32_t chunk_type;
62 u_int32_t chunk_format;
63 u_int32_t chunk_length;
64 u_int16_t format;
65 u_int16_t channels;
66 u_int32_t sample_rate;
67 u_int32_t bytes_per_second;
68 u_int16_t sample_size;
69 u_int16_t precision;
70 u_int32_t chunk_data;
71 u_int32_t data_length;
72};
73
74/* The file header of Sun adio files (*.au). Files are always in
75 big-endian byte-order. */
76
77struct au_header
78{
79 /* ASCII ".snd" */
80 u_int32_t magic_number;
81
82 /* Offset of data part from start of file. Minimum value is 24. */
83 u_int32_t data_offset;
84
85 /* Size of data part, 0xffffffff if unknown. */
86 u_int32_t data_size;
87
88 /* Data encoding format.
89 1 8-bit ISDN u-law
90 2 8-bit linear PCM (REF-PCM)
91 3 16-bit linear PCM
92 4 24-bit linear PCM
93 5 32-bit linear PCM
94 6 32-bit IEEE floating-point
95 7 64-bit IEEE floating-point
96 23 8-bit u-law compressed using CCITT 0.721 ADPCM voice data
97 encoding scheme. */
98 u_int32_t encoding;
99
100 /* Number of samples per second. */
101 u_int32_t sample_rate;
102
103 /* Number of interleaved channels. */
104 u_int32_t channels;
105};
106
107/* Maximum of all sound file headers sizes. */
108
109#define MAX_SOUND_HEADER_BYTES \
110 max (sizeof (struct wav_header), sizeof (struct au_header))
111
112/* Interface structure for sound devices. */
113
114struct sound_device
115{
116 /* The name of the device or null meaning use a default device name. */
117 char *file;
118
119 /* File descriptor of the device. */
120 int fd;
121
122 /* Device-dependent format. */
123 int format;
124
125 /* Volume (0..100). Zero means unspecified. */
126 int volume;
127
128 /* Sample size. */
129 int sample_size;
130
131 /* Sample rate. */
132 int sample_rate;
133
134 /* Bytes per second. */
135 int bps;
136
137 /* 1 = mono, 2 = stereo, 0 = don't set. */
138 int channels;
139
140 /* Open device SD. */
141 void (* open) P_ ((struct sound_device *sd));
142
143 /* Close device SD. */
144 void (* close) P_ ((struct sound_device *sd));
145
146 /* Configure SD accoring to device-dependent parameters. */
147 void (* configure) P_ ((struct sound_device *device));
148
149 /* Choose a device-dependent format for outputting sound file SF. */
150 void (* choose_format) P_ ((struct sound_device *sd,
151 struct sound_file *sf));
152
153 /* Write NYBTES bytes from BUFFER to device SD. */
154 void (* write) P_ ((struct sound_device *sd, char *buffer, int nbytes));
155
156 /* A place for devices to store additional data. */
157 void *data;
158};
159
160/* An enumerator for each supported sound file type. */
161
162enum sound_type
163{
164 RIFF,
165 SUN_AUDIO
166};
167
168/* Interface structure for sound files. */
169
170struct sound_file
171{
172 /* The type of the file. */
173 enum sound_type type;
174
175 /* File descriptor of the file. */
176 int fd;
177
178 /* Pointer to sound file header. This contains the first
179 MAX_SOUND_HEADER_BYTES read from the file. */
180 char *header;
181
182 /* Play sound file SF on device SD. */
183 void (* play) P_ ((struct sound_file *sf, struct sound_device *sd));
184};
185
186/* Indices of attributes in a sound attributes vector. */
187
188enum sound_attr
189{
190 SOUND_FILE,
191 SOUND_DEVICE,
192 SOUND_VOLUME,
193 SOUND_ATTR_SENTINEL
194};
195
196/* Symbols. */
197
198extern Lisp_Object QCfile;
199Lisp_Object QCvolume, QCdevice;
200Lisp_Object Qsound;
201Lisp_Object Qplay_sound_hook;
202
203/* These are set during `play-sound' so that sound_cleanup has
204 access to them. */
205
206struct sound_device *sound_device;
207struct sound_file *sound_file;
208
209/* Function prototypes. */
210
211static void vox_open P_ ((struct sound_device *));
212static void vox_configure P_ ((struct sound_device *));
213static void vox_close P_ ((struct sound_device *sd));
214static void vox_choose_format P_ ((struct sound_device *, struct sound_file *));
215static void vox_init P_ ((struct sound_device *));
216static void vox_write P_ ((struct sound_device *, char *, int));
217static void sound_perror P_ ((char *));
218static int parse_sound P_ ((Lisp_Object, Lisp_Object *));
219static void find_sound_file_type P_ ((struct sound_file *));
220static u_int32_t le2hl P_ ((u_int32_t));
221static u_int16_t le2hs P_ ((u_int16_t));
222static u_int32_t be2hl P_ ((u_int32_t));
223static u_int16_t be2hs P_ ((u_int16_t));
224static int wav_init P_ ((struct sound_file *));
225static void wav_play P_ ((struct sound_file *, struct sound_device *));
226static int au_init P_ ((struct sound_file *));
227static void au_play P_ ((struct sound_file *, struct sound_device *));
228
229
230
231/***********************************************************************
232 General
233 ***********************************************************************/
234
235/* Like perror, but signals an error. */
236
237static void
238sound_perror (msg)
239 char *msg;
240{
241 error ("%s: %s", msg, strerror (errno));
242}
243
244
245/* Parse sound specification SOUND, and fill ATTRS with what is
246 found. Value is non-zero if SOUND Is a valid sound specification.
247 A valid sound specification is a list starting with the symbol
248 `sound'. The rest of the list is a property list which may
249 contain the following key/value pairs:
250
251 - `:file FILE'
252
253 FILE is the sound file to play. If it isn't an absolute name,
254 it's searched under `data-directory'.
255
256 - `:device DEVICE'
257
258 DEVICE is the name of the device to play on, e.g. "/dev/dsp2".
259 If not specified, a default device is used.
260
261 - `:volume VOL'
262
263 VOL must be an integer in the range 0..100. */
264
265static int
266parse_sound (sound, attrs)
267 Lisp_Object sound;
268 Lisp_Object *attrs;
269{
270 /* SOUND must be a list starting with the symbol `sound'. */
271 if (!CONSP (sound) || !EQ (XCAR (sound), Qsound))
272 return 0;
273
274 sound = XCDR (sound);
275 attrs[SOUND_FILE] = Fplist_get (sound, QCfile);
276 attrs[SOUND_DEVICE] = Fplist_get (sound, QCdevice);
277 attrs[SOUND_VOLUME] = Fplist_get (sound, QCvolume);
278
279 /* File name must be specified. */
280 if (!STRINGP (attrs[SOUND_FILE]))
281 return 0;
282
283 /* Volume must be in the range 0..100 or unspecified. */
284 if (!NILP (attrs[SOUND_VOLUME]))
285 {
286 if (!INTEGERP (attrs[SOUND_VOLUME]))
287 return 0;
288 if (XINT (attrs[SOUND_VOLUME]) < 0
289 || XINT (attrs[SOUND_VOLUME]) > 100)
290 return 0;
291 }
292
293 /* Device must be a string or unspecified. */
294 if (!NILP (attrs[SOUND_DEVICE])
295 && !STRINGP (attrs[SOUND_DEVICE]))
296 return 0;
297
298 return 1;
299}
300
301
302/* Find out the type of the sound file whose file descriptor is FD.
303 SF is the sound file structure to fill in. */
304
305static void
306find_sound_file_type (sf)
307 struct sound_file *sf;
308{
309 if (!wav_init (sf)
310 && !au_init (sf))
311 error ("Unknown sound file format");
312}
313
314
315/* Function installed by play-sound with record_unwind_protect. */
316
317static Lisp_Object
318sound_cleanup (arg)
319 Lisp_Object arg;
320{
321 if (sound_device)
322 {
323 sound_device->close (sound_device);
324 if (sound_file->fd > 0)
325 close (sound_file->fd);
326 }
327}
328
329
330DEFUN ("play-sound", Fplay_sound, Splay_sound, 1, 1, 0,
331 "Play sound SOUND.")
332 (sound)
333 Lisp_Object sound;
334{
335 Lisp_Object attrs[SOUND_ATTR_SENTINEL];
336 char *header;
337 Lisp_Object file;
338 struct gcpro gcpro1, gcpro2;
339 int nbytes;
340 char *msg;
341 struct sound_device sd;
342 struct sound_file sf;
343 Lisp_Object args[2];
344 int count = specpdl_ptr - specpdl;
345
346 file = Qnil;
347 GCPRO2 (sound, file);
348 bzero (&sd, sizeof sd);
349 bzero (&sf, sizeof sf);
350 sf.header = (char *) alloca (MAX_SOUND_HEADER_BYTES);
351
352 sound_device = &sd;
353 sound_file = &sf;
354 record_unwind_protect (sound_cleanup, Qnil);
355
356 /* Parse the sound specification. Give up if it is invalid. */
357 if (!parse_sound (sound, attrs))
358 {
359 UNGCPRO;
360 error ("Invalid sound specification");
361 }
362
363 /* Open the sound file. */
364 sf.fd = openp (Fcons (Vdata_directory, Qnil),
365 attrs[SOUND_FILE], "", &file, 0);
366 if (sf.fd < 0)
367 sound_perror ("Open sound file");
368
369 /* Read the first bytes from the file. */
370 nbytes = read (sf.fd, sf.header, MAX_SOUND_HEADER_BYTES);
371 if (nbytes < 0)
372 sound_perror ("Reading sound file header");
373
374 /* Find out the type of sound file. Give up if we can't tell. */
375 find_sound_file_type (&sf);
376
377 /* Set up a device. */
378 if (STRINGP (attrs[SOUND_DEVICE]))
379 {
380 int len = XSTRING (attrs[SOUND_DEVICE])->size;
381 sd.file = (char *) alloca (len + 1);
382 strcpy (sd.file, XSTRING (attrs[SOUND_DEVICE])->data);
383 }
384 if (INTEGERP (attrs[SOUND_VOLUME]))
385 sd.volume = XFASTINT (attrs[SOUND_VOLUME]);
386
387 args[0] = Qplay_sound_hook;
388 args[1] = sound;
389 Frun_hook_with_args (make_number (2), args);
390
391 vox_init (&sd);
392 sd.open (&sd);
393
394 sf.play (&sf, &sd);
395 close (sf.fd);
396 sf.fd = -1;
397 sd.close (&sd);
398 sound_device = NULL;
399 sound_file = NULL;
400 UNGCPRO;
401 unbind_to (count, Qnil);
402 return Qnil;
403}
404
405
406/***********************************************************************
407 Byte-order Conversion
408 ***********************************************************************/
409
410/* Convert 32-bit value VALUE which is in little-endian byte-order
411 to host byte-order. */
412
413static u_int32_t
414le2hl (value)
415 u_int32_t value;
416{
417#ifdef WORDS_BIG_ENDIAN
418 unsigned char *p = (unsigned char *) &value;
419 value = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24);
420#endif
421 return value;
422}
423
424
425/* Convert 16-bit value VALUE which is in little-endian byte-order
426 to host byte-order. */
427
428static u_int16_t
429le2hs (value)
430 u_int16_t value;
431{
432#ifdef WORDS_BIG_ENDIAN
433 unsigned char *p = (unsigned char *) &value;
434 value = p[0] + (p[1] << 8);
435#endif
436 return value;
437}
438
439
440/* Convert 32-bit value VALUE which is in big-endian byte-order
441 to host byte-order. */
442
443static u_int32_t
444be2hl (value)
445 u_int32_t value;
446{
447#ifndef WORDS_BIG_ENDIAN
448 unsigned char *p = (unsigned char *) &value;
449 value = p[3] + (p[2] << 8) + (p[1] << 16) + (p[0] << 24);
450#endif
451 return value;
452}
453
454
455/* Convert 16-bit value VALUE which is in big-endian byte-order
456 to host byte-order. */
457
458static u_int16_t
459be2hs (value)
460 u_int16_t value;
461{
462#ifndef WORDS_BIG_ENDIAN
463 unsigned char *p = (unsigned char *) &value;
464 value = p[1] + (p[0] << 8);
465#endif
466 return value;
467}
468
469
470
471/***********************************************************************
472 RIFF-WAVE (*.wav)
473 ***********************************************************************/
474
475/* Try to initialize sound file SF from SF->header. SF->header
476 contains the first MAX_SOUND_HEADER_BYTES number of bytes from the
477 sound file. If the file is a WAV-format file, set up interface
478 functions in SF and convert header fields to host byte-order.
479 Value is non-zero if the file is a WAV file. */
480
481static int
482wav_init (sf)
483 struct sound_file *sf;
484{
485 struct wav_header *header = (struct wav_header *) sf->header;
486
487 if (bcmp (sf->header, "RIFF", 4) != 0)
488 return 0;
489
490 /* WAV files are in little-endian order. Convert the header
491 if on a big-endian machine. */
492 header->magic = le2hl (header->magic);
493 header->length = le2hl (header->length);
494 header->chunk_type = le2hl (header->chunk_type);
495 header->chunk_format = le2hl (header->chunk_format);
496 header->chunk_length = le2hl (header->chunk_length);
497 header->format = le2hs (header->format);
498 header->channels = le2hs (header->channels);
499 header->sample_rate = le2hl (header->sample_rate);
500 header->bytes_per_second = le2hl (header->bytes_per_second);
501 header->sample_size = le2hs (header->sample_size);
502 header->precision = le2hs (header->precision);
503 header->chunk_data = le2hl (header->chunk_data);
504 header->data_length = le2hl (header->data_length);
505
506 /* Set up the interface functions for WAV. */
507 sf->type = RIFF;
508 sf->play = wav_play;
509
510 return 1;
511}
512
513
514/* Play RIFF-WAVE audio file SF on sound device SD. */
515
516static void
517wav_play (sf, sd)
518 struct sound_file *sf;
519 struct sound_device *sd;
520{
521 struct wav_header *header = (struct wav_header *) sf->header;
522 char *buffer;
523 int nbytes;
524 int blksize = 2048;
525
526 /* Let the device choose a suitable device-dependent format
527 for the file. */
528 sd->choose_format (sd, sf);
529
530 /* Configure the device. */
531 sd->sample_size = header->sample_size;
532 sd->sample_rate = header->sample_rate;
533 sd->bps = header->bytes_per_second;
534 sd->channels = header->channels;
535 sd->configure (sd);
536
537 /* Copy sound data to the device. The WAV file specification is
538 actually more complex. This simple scheme worked with all WAV
539 files I found so far. If someone feels inclined to implement the
540 whole RIFF-WAVE spec, please do. */
541 buffer = (char *) alloca (blksize);
542 lseek (sf->fd, sizeof *header, SEEK_SET);
543
544 while ((nbytes = read (sf->fd, buffer, blksize)) > 0)
545 sd->write (sd, buffer, nbytes);
546
547 if (nbytes < 0)
548 sound_perror ("Reading sound file");
549}
550
551
552
553/***********************************************************************
554 Sun Audio (*.au)
555 ***********************************************************************/
556
557/* Sun audio file encodings. */
558
559enum au_encoding
560{
561 AU_ENCODING_ULAW_8 = 1,
562 AU_ENCODING_8,
563 AU_ENCODING_16,
564 AU_ENCODING_24,
565 AU_ENCODING_32,
566 AU_ENCODING_IEEE32,
567 AU_ENCODING_IEEE64,
568 AU_COMPRESSED = 23
569};
570
571
572/* Try to initialize sound file SF from SF->header. SF->header
573 contains the first MAX_SOUND_HEADER_BYTES number of bytes from the
574 sound file. If the file is a AU-format file, set up interface
575 functions in SF and convert header fields to host byte-order.
576 Value is non-zero if the file is an AU file. */
577
578static int
579au_init (sf)
580 struct sound_file *sf;
581{
582 struct au_header *header = (struct au_header *) sf->header;
583
584 if (bcmp (sf->header, ".snd", 4) != 0)
585 return 0;
586
587 header->magic_number = be2hl (header->magic_number);
588 header->data_offset = be2hl (header->data_offset);
589 header->data_size = be2hl (header->data_size);
590 header->encoding = be2hl (header->encoding);
591 header->sample_rate = be2hl (header->sample_rate);
592 header->channels = be2hl (header->channels);
593
594 /* Set up the interface functions for AU. */
595 sf->type = SUN_AUDIO;
596 sf->play = au_play;
597
598 return 1;
599}
600
601
602/* Play Sun audio file SF on sound device SD. */
603
604static void
605au_play (sf, sd)
606 struct sound_file *sf;
607 struct sound_device *sd;
608{
609 struct au_header *header = (struct au_header *) sf->header;
610 int blksize = 2048;
611 char *buffer;
612 int nbytes;
613
614 sd->sample_size = 0;
615 sd->sample_rate = header->sample_rate;
616 sd->bps = 0;
617 sd->channels = header->channels;
618 sd->choose_format (sd, sf);
619 sd->configure (sd);
620
621 /* Seek */
622 lseek (sf->fd, header->data_offset, SEEK_SET);
623
624 /* Copy sound data to the device. */
625 buffer = (char *) alloca (blksize);
626 while ((nbytes = read (sf->fd, buffer, blksize)) > 0)
627 sd->write (sd, buffer, nbytes);
628
629 if (nbytes < 0)
630 sound_perror ("Reading sound file");
631}
632
633
634
635/***********************************************************************
636 Voxware Driver Interface
637 ***********************************************************************/
638
639/* This driver is available on GNU/Linux, and the free BSDs. FreeBSD
640 has a compatible own driver aka Luigi's driver. */
641
642
643/* Open device SD. If SD->file is non-null, open that device,
644 otherwise use a default device name. */
645
646static void
647vox_open (sd)
648 struct sound_device *sd;
649{
650 char *file;
651
652 /* Open the sound device. Default is /dev/dsp. */
653 if (sd->file)
654 file = sd->file;
655 else
656 file = "/dev/dsp";
657
658 sd->fd = open (file, O_WRONLY);
659 if (sd->fd < 0)
660 sound_perror (file);
661}
662
663
664/* Configure device SD from parameters in it. */
665
666static void
667vox_configure (sd)
668 struct sound_device *sd;
669{
670 int requested;
671
672 xassert (sd->fd >= 0);
673
674 /* Device parameters apparently depend on each other in undocumented
675 ways (not to imply that there is any real documentation). Be
676 careful when reordering the calls below. */
677 if (sd->sample_size > 0
678 && ioctl (sd->fd, SNDCTL_DSP_SAMPLESIZE, &sd->sample_size) < 0)
679 sound_perror ("Setting sample size");
680
681 if (sd->bps > 0
682 && ioctl (sd->fd, SNDCTL_DSP_SPEED, &sd->bps) < 0)
683 sound_perror ("Setting speed");
684
685 if (sd->sample_rate > 0
686 && ioctl (sd->fd, SOUND_PCM_WRITE_RATE, &sd->sample_rate) < 0)
687 sound_perror ("Setting sample rate");
688
689 requested = sd->format;
690 if (ioctl (sd->fd, SNDCTL_DSP_SETFMT, &sd->format) < 0)
691 sound_perror ("Setting format");
692 else if (requested != sd->format)
693 error ("Setting format");
694
695 if (sd->channels > 1
696 && ioctl (sd->fd, SNDCTL_DSP_STEREO, &sd->channels) < 0)
697 sound_perror ("Setting channels");
698
699 if (sd->volume > 0
700 && ioctl (sd->fd, SOUND_MIXER_WRITE_PCM, &sd->volume) < 0)
701 sound_perror ("Setting volume");
702}
703
704
705/* Close device SD if it is open. */
706
707static void
708vox_close (sd)
709 struct sound_device *sd;
710{
711 if (sd->fd >= 0)
712 {
713 /* Flush sound data, and reset the device. */
714 ioctl (sd->fd, SNDCTL_DSP_SYNC, NULL);
715 ioctl (sd->fd, SNDCTL_DSP_RESET, NULL);
716
717 /* Close the device. */
718 close (sd->fd);
719 sd->fd = -1;
720 }
721}
722
723
724/* Choose device-dependent format for device SD from sound file SF. */
725
726static void
727vox_choose_format (sd, sf)
728 struct sound_device *sd;
729 struct sound_file *sf;
730{
731 if (sf->type == RIFF)
732 {
733 struct wav_header *h = (struct wav_header *) sf->header;
734 if (h->precision == 8)
735 sd->format = AFMT_U8;
736 else if (h->precision == 16)
737 sd->format = AFMT_S16_LE;
738 else
739 error ("Unsupported WAV file format");
740 }
741 else if (sf->type == SUN_AUDIO)
742 {
743 struct au_header *header = (struct au_header *) sf->header;
744 switch (header->encoding)
745 {
746 case AU_ENCODING_ULAW_8:
747 case AU_ENCODING_IEEE32:
748 case AU_ENCODING_IEEE64:
749 sd->format = AFMT_MU_LAW;
750 break;
751
752 case AU_ENCODING_8:
753 case AU_ENCODING_16:
754 case AU_ENCODING_24:
755 case AU_ENCODING_32:
756 sd->format = AFMT_S16_LE;
757 break;
758
759 default:
760 error ("Unsupported AU file format");
761 }
762 }
763 else
764 abort ();
765}
766
767
768/* Initialize device SD. Set up the interface functions in the device
769 structure. */
770
771static void
772vox_init (sd)
773 struct sound_device *sd;
774{
775 sd->fd = -1;
776 sd->open = vox_open;
777 sd->close = vox_close;
778 sd->configure = vox_configure;
779 sd->choose_format = vox_choose_format;
780 sd->write = vox_write;
781}
782
783
784/* Write NBYTES bytes from BUFFER to device SD. */
785
786static void
787vox_write (sd, buffer, nbytes)
788 struct sound_device *sd;
789 char *buffer;
790 int nbytes;
791{
792 int nwritten = write (sd->fd, buffer, nbytes);
793 if (nwritten < 0)
794 sound_perror ("Writing to sound device");
795}
796
797
798
799/***********************************************************************
800 Initialization
801 ***********************************************************************/
802
803void
804syms_of_sound ()
805{
806 QCdevice = intern (":device");
807 staticpro (&QCdevice);
808 QCvolume = intern (":volume");
809 staticpro (&QCvolume);
810 Qsound = intern ("sound");
811 staticpro (&Qsound);
812 Qplay_sound_hook = intern ("play-sound-hook");
813 staticpro (&Qplay_sound_hook);
814
815 defsubr (&Splay_sound);
816}
817
818
819void
820init_sound ()
821{
822}
823
824#endif /* HAVE_SOUND */