1/*
2 * Copyright 2002-2003 Red Hat Inc., Durham, North Carolina.
3 *
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation on the rights to use, copy, modify, merge,
10 * publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial
16 * portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28/*
29 * Authors:
30 *   Rickard E. (Rik) Faith <faith@redhat.com>
31 *
32 */
33
34/** \file
35 *
36 * Provides an interface for handling SIGIO signals for input devices. */
37
38#ifdef HAVE_DMX_CONFIG_H
39#include <dmx-config.h>
40#endif
41
42#include "inputstr.h"
43#include "dmxinputinit.h"
44#include "dmxsigio.h"
45#include "dmxevents.h"
46#include <signal.h>
47#include <unistd.h>
48#include <fcntl.h>
49
50static int  dmxFdCount      = 0;
51static Bool dmxInputEnabled = TRUE;
52
53/* Define equivalents for non-POSIX systems (e.g., SGI IRIX, Solaris) */
54#ifndef O_ASYNC
55# ifdef FASYNC
56# define O_ASYNC FASYNC
57# else
58# define O_ASYNC 0
59# endif
60#endif
61#ifndef O_NONBLOCK
62#define O_NONBLOCK FNONBLK
63#endif
64
65static void dmxSigioHandler(int sig)
66{
67    int          i, j;
68    DMXInputInfo *dmxInput;
69
70    for (i = 0, dmxInput = &dmxInputs[0]; i < dmxNumInputs; i++, dmxInput++) {
71        if (dmxInput->sigioState == DMX_ACTIVESIGIO) {
72            for (j = 0; j < dmxInput->numDevs; j++) {
73                DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[j];
74                if (dmxLocal->collect_events) {
75                    dmxLocal->collect_events(&dmxLocal->pDevice->public,
76                                             dmxMotion,
77                                             dmxEnqueue,
78                                             dmxCheckSpecialKeys,
79                                             DMX_NO_BLOCK);
80                }
81            }
82        }
83    }
84}
85
86/** Block SIGIO handling. */
87void dmxSigioBlock(void)
88{
89    sigset_t s;
90
91    sigemptyset(&s);
92    sigaddset(&s, SIGIO);
93    sigprocmask(SIG_BLOCK, &s, 0);
94}
95
96/** Unblock SIGIO handling. */
97void dmxSigioUnblock(void)
98{
99    sigset_t s;
100
101    sigemptyset(&s);
102    sigaddset(&s, SIGIO);
103    sigprocmask(SIG_UNBLOCK, &s, 0);
104}
105
106static void dmxSigioHook(void)
107{
108    struct sigaction a;
109    sigset_t         s;
110
111    memset(&a, 0, sizeof(a));
112    a.sa_handler = dmxSigioHandler;
113    sigemptyset(&a.sa_mask);
114    sigaddset(&a.sa_mask, SIGIO);
115    sigaddset(&a.sa_mask, SIGALRM);
116    sigaddset(&a.sa_mask, SIGVTALRM);
117    sigaction(SIGIO, &a, 0);
118
119    sigemptyset(&s);
120    sigprocmask(SIG_SETMASK, &s, 0);
121}
122
123static void dmxSigioUnhook(void)
124{
125    struct sigaction a;
126
127    memset (&a, 0, sizeof(a));
128    a.sa_handler = SIG_IGN;
129    sigemptyset(&a.sa_mask);
130    sigaction(SIGIO, &a, 0);
131}
132
133static void dmxSigioAdd(DMXInputInfo *dmxInput)
134{
135    int flags;
136    int i;
137
138    switch (dmxInput->sigioState) {
139    case DMX_NOSIGIO:     return;
140    case DMX_USESIGIO:    dmxInput->sigioState = DMX_ACTIVESIGIO; break;
141    case DMX_ACTIVESIGIO: return;
142    }
143
144    for (i = 0; i < dmxInput->sigioFdCount; i++) {
145        if (!dmxInput->sigioAdded[i]) {
146            int fd = dmxInput->sigioFd[i];
147
148            fcntl(fd, F_SETOWN, getpid());
149            flags = fcntl(fd, F_GETFL);
150            flags |= O_ASYNC|O_NONBLOCK;
151            fcntl(fd, F_SETFL, flags);
152
153            AddEnabledDevice(fd);
154            dmxInput->sigioAdded[i] = TRUE;
155
156            if (++dmxFdCount == 1) dmxSigioHook();
157        }
158    }
159}
160
161static void dmxSigioRemove(DMXInputInfo *dmxInput)
162{
163    int flags;
164    int i;
165
166    switch (dmxInput->sigioState) {
167    case DMX_NOSIGIO:     return;
168    case DMX_USESIGIO:    return;
169    case DMX_ACTIVESIGIO: dmxInput->sigioState = DMX_USESIGIO; break;
170    }
171
172    for (i = 0; i < dmxInput->sigioFdCount; i++) {
173        if (dmxInput->sigioAdded[i]) {
174            int fd = dmxInput->sigioFd[i];
175
176            dmxInput->sigioAdded[i] = FALSE;
177            RemoveEnabledDevice(fd);
178
179            flags = fcntl(fd, F_GETFL);
180            flags &= ~(O_ASYNC|O_NONBLOCK);
181            fcntl(fd, F_SETFL, flags);
182
183            if (!--dmxFdCount) dmxSigioUnhook();
184        }
185    }
186}
187
188/** Enable SIGIO handling.  This instantiates the handler with the OS. */
189void dmxSigioEnableInput(void)
190{
191    int              i;
192    DMXInputInfo     *dmxInput;
193
194    dmxInputEnabled = TRUE;
195    for (i = 0, dmxInput = &dmxInputs[0]; i < dmxNumInputs; i++, dmxInput++)
196        dmxSigioAdd(dmxInput);
197}
198
199/** Disable SIGIO handling.  This removes the hanlder from the OS. */
200void dmxSigioDisableInput(void)
201{
202    int              i;
203    DMXInputInfo     *dmxInput;
204
205    dmxInputEnabled = FALSE;
206    for (i = 0, dmxInput = &dmxInputs[0]; i < dmxNumInputs; i++, dmxInput++)
207        dmxSigioRemove(dmxInput);
208}
209
210/** Make a note that the input device described in \a dmxInput will be
211 * using the file descriptor \a fd for SIGIO signals.  Calls
212 * AddEnabledDevice ifi SIGIO handling has been enabled with
213 * #dmxSigioEnableInput(). */
214void dmxSigioRegister(DMXInputInfo *dmxInput, int fd)
215{
216    dmxInput->sigioState = DMX_USESIGIO;
217    if (dmxInput->sigioFdCount >= DMX_MAX_SIGIO_FDS)
218        dmxLog(dmxFatal, "Too many SIGIO file descriptors (%d >= %d)\n",
219               dmxInput->sigioFdCount, DMX_MAX_SIGIO_FDS);
220
221    dmxInput->sigioFd[dmxInput->sigioFdCount++] = fd;
222    if (dmxInputEnabled) dmxSigioAdd(dmxInput);
223}
224
225/** Remove the notes that \a dmxInput is using any file descriptors for
226 * SIGIO signals.  Calls RemoveEnabledDevice. */
227void dmxSigioUnregister(DMXInputInfo *dmxInput)
228{
229    if (dmxInput->sigioState == DMX_NOSIGIO) return;
230    dmxSigioRemove(dmxInput);
231    dmxInput->sigioState   = DMX_NOSIGIO;
232    dmxInput->sigioFdCount = 0;
233}
234