1/************************************************************
2Copyright (c) 1993 by Silicon Graphics Computer Systems, Inc.
3
4Permission to use, copy, modify, and distribute this
5software and its documentation for any purpose and without
6fee is hereby granted, provided that the above copyright
7notice appear in all copies and that both that copyright
8notice and this permission notice appear in supporting
9documentation, and that the name of Silicon Graphics not be
10used in advertising or publicity pertaining to distribution
11of the software without specific prior written permission.
12Silicon Graphics makes no representation about the suitability
13of this software for any purpose. It is provided "as is"
14without any express or implied warranty.
15
16SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23THE USE OR PERFORMANCE OF THIS SOFTWARE.
24
25********************************************************/
26
27#ifdef HAVE_DIX_CONFIG_H
28#include <dix-config.h>
29#endif
30
31#include <stdio.h>
32#include <X11/X.h>
33#include <X11/Xproto.h>
34#include <X11/keysym.h>
35#include "inputstr.h"
36#include "scrnintstr.h"
37#include "windowstr.h"
38#include <xkbsrv.h>
39#include <X11/extensions/XI.h>
40
41/*#define FALLING_TONE	1*/
42/*#define RISING_TONE	1*/
43#define FALLING_TONE	10
44#define RISING_TONE	10
45#define	SHORT_TONE	50
46#define	SHORT_DELAY	60
47#define	LONG_TONE	75
48#define	VERY_LONG_TONE	100
49#define	LONG_DELAY	85
50#define CLICK_DURATION	1
51
52#define	DEEP_PITCH	250
53#define	LOW_PITCH	500
54#define	MID_PITCH	1000
55#define	HIGH_PITCH	2000
56#define CLICK_PITCH	1500
57
58static unsigned long atomGeneration = 0;
59static Atom featureOn;
60static Atom featureOff;
61static Atom featureChange;
62static Atom ledOn;
63static Atom ledOff;
64static Atom ledChange;
65static Atom slowWarn;
66static Atom slowPress;
67static Atom slowReject;
68static Atom slowAccept;
69static Atom slowRelease;
70static Atom stickyLatch;
71static Atom stickyLock;
72static Atom stickyUnlock;
73static Atom bounceReject;
74static char doesPitch = 1;
75
76#define	FEATURE_ON	"AX_FeatureOn"
77#define	FEATURE_OFF	"AX_FeatureOff"
78#define	FEATURE_CHANGE	"AX_FeatureChange"
79#define	LED_ON		"AX_IndicatorOn"
80#define	LED_OFF		"AX_IndicatorOff"
81#define	LED_CHANGE	"AX_IndicatorChange"
82#define	SLOW_WARN	"AX_SlowKeysWarning"
83#define	SLOW_PRESS	"AX_SlowKeyPress"
84#define	SLOW_REJECT	"AX_SlowKeyReject"
85#define	SLOW_ACCEPT	"AX_SlowKeyAccept"
86#define	SLOW_RELEASE	"AX_SlowKeyRelease"
87#define	STICKY_LATCH	"AX_StickyLatch"
88#define	STICKY_LOCK	"AX_StickyLock"
89#define	STICKY_UNLOCK	"AX_StickyUnlock"
90#define	BOUNCE_REJECT	"AX_BounceKeyReject"
91
92#define	MAKE_ATOM(a)	MakeAtom(a,sizeof(a)-1,TRUE)
93
94static void
95_XkbDDXBeepInitAtoms(void)
96{
97    featureOn = MAKE_ATOM(FEATURE_ON);
98    featureOff = MAKE_ATOM(FEATURE_OFF);
99    featureChange = MAKE_ATOM(FEATURE_CHANGE);
100    ledOn = MAKE_ATOM(LED_ON);
101    ledOff = MAKE_ATOM(LED_OFF);
102    ledChange = MAKE_ATOM(LED_CHANGE);
103    slowWarn = MAKE_ATOM(SLOW_WARN);
104    slowPress = MAKE_ATOM(SLOW_PRESS);
105    slowReject = MAKE_ATOM(SLOW_REJECT);
106    slowAccept = MAKE_ATOM(SLOW_ACCEPT);
107    slowRelease = MAKE_ATOM(SLOW_RELEASE);
108    stickyLatch = MAKE_ATOM(STICKY_LATCH);
109    stickyLock = MAKE_ATOM(STICKY_LOCK);
110    stickyUnlock = MAKE_ATOM(STICKY_UNLOCK);
111    bounceReject = MAKE_ATOM(BOUNCE_REJECT);
112    return;
113}
114
115static CARD32
116_XkbDDXBeepExpire(OsTimerPtr timer, CARD32 now, void *arg)
117{
118    DeviceIntPtr dev = (DeviceIntPtr) arg;
119    KbdFeedbackPtr feed;
120    KeybdCtrl *ctrl;
121    XkbSrvInfoPtr xkbInfo;
122    CARD32 next;
123    int pitch, duration;
124    int oldPitch, oldDuration;
125    Atom name;
126
127    if ((dev == NULL) || (dev->key == NULL) || (dev->key->xkbInfo == NULL) ||
128        (dev->kbdfeed == NULL))
129        return 0;
130    if (atomGeneration != serverGeneration) {
131        _XkbDDXBeepInitAtoms();
132        atomGeneration = serverGeneration;
133    }
134
135    feed = dev->kbdfeed;
136    ctrl = &feed->ctrl;
137    xkbInfo = dev->key->xkbInfo;
138    next = 0;
139    pitch = oldPitch = ctrl->bell_pitch;
140    duration = oldDuration = ctrl->bell_duration;
141    name = None;
142    switch (xkbInfo->beepType) {
143    default:
144        ErrorF("[xkb] Unknown beep type %d\n", xkbInfo->beepType);
145    case _BEEP_NONE:
146        duration = 0;
147        break;
148
149        /* When an LED is turned on, we want a high-pitched beep.
150         * When the LED it turned off, we want a low-pitched beep.
151         * If we cannot do pitch, we want a single beep for on and two
152         * beeps for off.
153         */
154    case _BEEP_LED_ON:
155        if (name == None)
156            name = ledOn;
157        duration = SHORT_TONE;
158        pitch = HIGH_PITCH;
159        break;
160    case _BEEP_LED_OFF:
161        if (name == None)
162            name = ledOff;
163        duration = SHORT_TONE;
164        pitch = LOW_PITCH;
165        if (!doesPitch && xkbInfo->beepCount < 1)
166            next = SHORT_DELAY;
167        break;
168
169        /* When a Feature is turned on, we want an up-siren.
170         * When a Feature is turned off, we want a down-siren.
171         * If we cannot do pitch, we want a single beep for on and two
172         * beeps for off.
173         */
174    case _BEEP_FEATURE_ON:
175        if (name == None)
176            name = featureOn;
177        if (xkbInfo->beepCount < 1) {
178            pitch = LOW_PITCH;
179            duration = VERY_LONG_TONE;
180            if (doesPitch)
181                next = SHORT_DELAY;
182        }
183        else {
184            pitch = MID_PITCH;
185            duration = SHORT_TONE;
186        }
187        break;
188
189    case _BEEP_FEATURE_OFF:
190        if (name == None)
191            name = featureOff;
192        if (xkbInfo->beepCount < 1) {
193            pitch = MID_PITCH;
194            if (doesPitch)
195                duration = VERY_LONG_TONE;
196            else
197                duration = SHORT_TONE;
198            next = SHORT_DELAY;
199        }
200        else {
201            pitch = LOW_PITCH;
202            duration = SHORT_TONE;
203        }
204        break;
205
206        /* Two high beeps indicate an LED or Feature changed
207         * state, but that another LED or Feature is also on.
208         * [[[WDW - This is not in AccessDOS ]]]
209         */
210    case _BEEP_LED_CHANGE:
211        if (name == None)
212            name = ledChange;
213    case _BEEP_FEATURE_CHANGE:
214        if (name == None)
215            name = featureChange;
216        duration = SHORT_TONE;
217        pitch = HIGH_PITCH;
218        if (xkbInfo->beepCount < 1) {
219            next = SHORT_DELAY;
220        }
221        break;
222
223        /* Three high-pitched beeps are the warning that SlowKeys
224         * is going to be turned on or off.
225         */
226    case _BEEP_SLOW_WARN:
227        if (name == None)
228            name = slowWarn;
229        duration = SHORT_TONE;
230        pitch = HIGH_PITCH;
231        if (xkbInfo->beepCount < 2)
232            next = SHORT_DELAY;
233        break;
234
235        /* Click on SlowKeys press and accept.
236         * Deep pitch when a SlowKey or BounceKey is rejected.
237         * [[[WDW - Rejects are not in AccessDOS ]]]
238         * If we cannot do pitch, we want single beeps.
239         */
240    case _BEEP_SLOW_PRESS:
241        if (name == None)
242            name = slowPress;
243    case _BEEP_SLOW_ACCEPT:
244        if (name == None)
245            name = slowAccept;
246    case _BEEP_SLOW_RELEASE:
247        if (name == None)
248            name = slowRelease;
249        duration = CLICK_DURATION;
250        pitch = CLICK_PITCH;
251        break;
252    case _BEEP_BOUNCE_REJECT:
253        if (name == None)
254            name = bounceReject;
255    case _BEEP_SLOW_REJECT:
256        if (name == None)
257            name = slowReject;
258        duration = SHORT_TONE;
259        pitch = DEEP_PITCH;
260        break;
261
262        /* Low followed by high pitch when a StickyKey is latched.
263         * High pitch when a StickyKey is locked.
264         * Low pitch when unlocked.
265         * If we cannot do pitch, two beeps for latch, nothing for
266         * lock, and two for unlock.
267         */
268    case _BEEP_STICKY_LATCH:
269        if (name == None)
270            name = stickyLatch;
271        duration = SHORT_TONE;
272        if (xkbInfo->beepCount < 1) {
273            next = SHORT_DELAY;
274            pitch = LOW_PITCH;
275        }
276        else
277            pitch = HIGH_PITCH;
278        break;
279    case _BEEP_STICKY_LOCK:
280        if (name == None)
281            name = stickyLock;
282        if (doesPitch) {
283            duration = SHORT_TONE;
284            pitch = HIGH_PITCH;
285        }
286        break;
287    case _BEEP_STICKY_UNLOCK:
288        if (name == None)
289            name = stickyUnlock;
290        duration = SHORT_TONE;
291        pitch = LOW_PITCH;
292        if (!doesPitch && xkbInfo->beepCount < 1)
293            next = SHORT_DELAY;
294        break;
295    }
296    if (timer == NULL && duration > 0) {
297        CARD32 starttime = GetTimeInMillis();
298        CARD32 elapsedtime;
299
300        ctrl->bell_duration = duration;
301        ctrl->bell_pitch = pitch;
302        if (xkbInfo->beepCount == 0) {
303            XkbHandleBell(0, 0, dev, ctrl->bell, (void *) ctrl,
304                          KbdFeedbackClass, name, None, NULL);
305        }
306        else if (xkbInfo->desc->ctrls->enabled_ctrls & XkbAudibleBellMask) {
307            (*dev->kbdfeed->BellProc) (ctrl->bell, dev, (void *) ctrl,
308                                       KbdFeedbackClass);
309        }
310        ctrl->bell_duration = oldDuration;
311        ctrl->bell_pitch = oldPitch;
312        xkbInfo->beepCount++;
313
314        /* Some DDX schedule the beep and return immediately, others don't
315           return until the beep is completed.  We measure the time and if
316           it's less than the beep duration, make sure not to schedule the
317           next beep until after the current one finishes. */
318
319        elapsedtime = GetTimeInMillis();
320        if (elapsedtime > starttime) {  /* watch out for millisecond counter
321                                           overflow! */
322            elapsedtime -= starttime;
323        }
324        else {
325            elapsedtime = 0;
326        }
327        if (elapsedtime < duration) {
328            next += duration - elapsedtime;
329        }
330
331    }
332    return next;
333}
334
335int
336XkbDDXAccessXBeep(DeviceIntPtr dev, unsigned what, unsigned which)
337{
338    XkbSrvInfoRec *xkbInfo = dev->key->xkbInfo;
339    CARD32 next;
340
341    xkbInfo->beepType = what;
342    xkbInfo->beepCount = 0;
343    next = _XkbDDXBeepExpire(NULL, 0, (void *) dev);
344    if (next > 0) {
345        xkbInfo->beepTimer = TimerSet(xkbInfo->beepTimer,
346                                      0, next,
347                                      _XkbDDXBeepExpire, (void *) dev);
348    }
349    return 1;
350}
351