declare smobs in alloc.c
[bpt/emacs.git] / src / xgselect.c
1 /* Function for handling the GLib event loop.
2
3 Copyright (C) 2009-2014 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 3 of the License, or
10 (at your option) 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. If not, see <http://www.gnu.org/licenses/>. */
19
20 #include <config.h>
21
22 #include "xgselect.h"
23
24 #ifdef HAVE_GLIB
25
26 #include <glib.h>
27 #include <errno.h>
28 #include <stdbool.h>
29 #include <timespec.h>
30 #include "frame.h"
31 #include "blockinput.h"
32
33 /* `xg_select' is a `pselect' replacement. Why do we need a separate function?
34 1. Timeouts. Glib and Gtk rely on timer events. If we did pselect
35 with a greater timeout then the one scheduled by Glib, we would
36 not allow Glib to process its timer events. We want Glib to
37 work smoothly, so we need to reduce our timeout to match Glib.
38 2. Descriptors. Glib may listen to more file descriptors than we do.
39 So we add Glib descriptors to our pselect pool, but we don't change
40 the value returned by the function. The return value matches only
41 the descriptors passed as arguments, making it compatible with
42 plain pselect. */
43
44 int
45 xg_select (int fds_lim, fd_set *rfds, fd_set *wfds, fd_set *efds,
46 struct timespec const *timeout, sigset_t const *sigmask)
47 {
48 fd_set all_rfds, all_wfds;
49 struct timespec tmo;
50 struct timespec const *tmop = timeout;
51
52 GMainContext *context;
53 int have_wfds = wfds != NULL;
54 GPollFD gfds_buf[128];
55 GPollFD *gfds = gfds_buf;
56 int gfds_size = ARRAYELTS (gfds_buf);
57 int n_gfds, retval = 0, our_fds = 0, max_fds = fds_lim - 1;
58 int i, nfds, tmo_in_millisec;
59 bool need_to_dispatch;
60 USE_SAFE_ALLOCA;
61
62 context = g_main_context_default ();
63
64 if (rfds) all_rfds = *rfds;
65 else FD_ZERO (&all_rfds);
66 if (wfds) all_wfds = *wfds;
67 else FD_ZERO (&all_wfds);
68
69 n_gfds = g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
70 gfds, gfds_size);
71 if (gfds_size < n_gfds)
72 {
73 SAFE_NALLOCA (gfds, sizeof *gfds, n_gfds);
74 gfds_size = n_gfds;
75 n_gfds = g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
76 gfds, gfds_size);
77 }
78
79 for (i = 0; i < n_gfds; ++i)
80 {
81 if (gfds[i].events & G_IO_IN)
82 {
83 FD_SET (gfds[i].fd, &all_rfds);
84 if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
85 }
86 if (gfds[i].events & G_IO_OUT)
87 {
88 FD_SET (gfds[i].fd, &all_wfds);
89 if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
90 have_wfds = 1;
91 }
92 }
93
94 SAFE_FREE ();
95
96 if (tmo_in_millisec >= 0)
97 {
98 tmo = make_timespec (tmo_in_millisec / 1000,
99 1000 * 1000 * (tmo_in_millisec % 1000));
100 if (!timeout || timespec_cmp (tmo, *timeout) < 0)
101 tmop = &tmo;
102 }
103
104 fds_lim = max_fds + 1;
105 nfds = pselect (fds_lim, &all_rfds, have_wfds ? &all_wfds : NULL,
106 efds, tmop, sigmask);
107
108 if (nfds < 0)
109 retval = nfds;
110 else if (nfds > 0)
111 {
112 for (i = 0; i < fds_lim; ++i)
113 {
114 if (FD_ISSET (i, &all_rfds))
115 {
116 if (rfds && FD_ISSET (i, rfds)) ++retval;
117 else ++our_fds;
118 }
119 else if (rfds)
120 FD_CLR (i, rfds);
121
122 if (have_wfds && FD_ISSET (i, &all_wfds))
123 {
124 if (wfds && FD_ISSET (i, wfds)) ++retval;
125 else ++our_fds;
126 }
127 else if (wfds)
128 FD_CLR (i, wfds);
129
130 if (efds && FD_ISSET (i, efds))
131 ++retval;
132 }
133 }
134
135 /* If Gtk+ is in use eventually gtk_main_iteration will be called,
136 unless retval is zero. */
137 #ifdef USE_GTK
138 need_to_dispatch = retval == 0;
139 #else
140 need_to_dispatch = true;
141 #endif
142 if (need_to_dispatch)
143 {
144 int pselect_errno = errno;
145 /* Prevent g_main_dispatch recursion, that would occur without
146 block_input wrapper, because event handlers call
147 unblock_input. Event loop recursion was causing Bug#15801. */
148 block_input ();
149 while (g_main_context_pending (context))
150 g_main_context_dispatch (context);
151 unblock_input ();
152 errno = pselect_errno;
153 }
154
155 /* To not have to recalculate timeout, return like this. */
156 if ((our_fds > 0 || (nfds == 0 && tmop == &tmo)) && (retval == 0))
157 {
158 retval = -1;
159 errno = EINTR;
160 }
161
162 return retval;
163 }
164 #endif /* HAVE_GLIB */