Commit | Line | Data |
---|---|---|
872870b2 | 1 | /* Function for handling the GLib event loop. |
95df8112 | 2 | |
ba318903 | 3 | Copyright (C) 2009-2014 Free Software Foundation, Inc. |
872870b2 JD |
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 | |
a2332e8d | 18 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ |
872870b2 | 19 | |
08a494a3 | 20 | #include <config.h> |
872870b2 | 21 | |
aefd87e1 PE |
22 | #include "xgselect.h" |
23 | ||
55a87246 | 24 | #ifdef HAVE_GLIB |
aefd87e1 | 25 | |
872870b2 JD |
26 | #include <glib.h> |
27 | #include <errno.h> | |
e6e8a5eb | 28 | #include <stdbool.h> |
43aac990 | 29 | #include <timespec.h> |
7452b7bd | 30 | #include "frame.h" |
6709d4da JC |
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. */ | |
872870b2 | 43 | |
872870b2 | 44 | int |
d486344e | 45 | xg_select (int fds_lim, fd_set *rfds, fd_set *wfds, fd_set *efds, |
43aac990 | 46 | struct timespec const *timeout, sigset_t const *sigmask) |
872870b2 | 47 | { |
d486344e | 48 | fd_set all_rfds, all_wfds; |
43aac990 PE |
49 | struct timespec tmo; |
50 | struct timespec const *tmop = timeout; | |
872870b2 | 51 | |
b0572523 | 52 | GMainContext *context; |
872870b2 | 53 | int have_wfds = wfds != NULL; |
0f46bc75 PE |
54 | GPollFD gfds_buf[128]; |
55 | GPollFD *gfds = gfds_buf; | |
faa52174 | 56 | int gfds_size = ARRAYELTS (gfds_buf); |
0f46bc75 | 57 | int n_gfds, retval = 0, our_fds = 0, max_fds = fds_lim - 1; |
ff23cd9f | 58 | int i, nfds, tmo_in_millisec; |
e6e8a5eb | 59 | bool need_to_dispatch; |
0f46bc75 | 60 | USE_SAFE_ALLOCA; |
872870b2 | 61 | |
5de0e011 | 62 | context = g_main_context_default (); |
b0572523 | 63 | |
ae1d87e2 | 64 | if (rfds) all_rfds = *rfds; |
872870b2 | 65 | else FD_ZERO (&all_rfds); |
ae1d87e2 | 66 | if (wfds) all_wfds = *wfds; |
872870b2 JD |
67 | else FD_ZERO (&all_wfds); |
68 | ||
0f46bc75 PE |
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 | } | |
872870b2 | 78 | |
41729b81 | 79 | for (i = 0; i < n_gfds; ++i) |
872870b2 JD |
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 | ||
0f46bc75 PE |
94 | SAFE_FREE (); |
95 | ||
872870b2 JD |
96 | if (tmo_in_millisec >= 0) |
97 | { | |
43aac990 PE |
98 | tmo = make_timespec (tmo_in_millisec / 1000, |
99 | 1000 * 1000 * (tmo_in_millisec % 1000)); | |
100 | if (!timeout || timespec_cmp (tmo, *timeout) < 0) | |
d35af63c | 101 | tmop = &tmo; |
872870b2 JD |
102 | } |
103 | ||
97107e2e | 104 | fds_lim = max_fds + 1; |
d35af63c PE |
105 | nfds = pselect (fds_lim, &all_rfds, have_wfds ? &all_wfds : NULL, |
106 | efds, tmop, sigmask); | |
872870b2 JD |
107 | |
108 | if (nfds < 0) | |
109 | retval = nfds; | |
41729b81 | 110 | else if (nfds > 0) |
872870b2 | 111 | { |
97107e2e | 112 | for (i = 0; i < fds_lim; ++i) |
872870b2 JD |
113 | { |
114 | if (FD_ISSET (i, &all_rfds)) | |
115 | { | |
116 | if (rfds && FD_ISSET (i, rfds)) ++retval; | |
117 | else ++our_fds; | |
118 | } | |
42d3022b J |
119 | else if (rfds) |
120 | FD_CLR (i, rfds); | |
121 | ||
872870b2 JD |
122 | if (have_wfds && FD_ISSET (i, &all_wfds)) |
123 | { | |
124 | if (wfds && FD_ISSET (i, wfds)) ++retval; | |
125 | else ++our_fds; | |
126 | } | |
42d3022b J |
127 | else if (wfds) |
128 | FD_CLR (i, wfds); | |
129 | ||
872870b2 JD |
130 | if (efds && FD_ISSET (i, efds)) |
131 | ++retval; | |
132 | } | |
133 | } | |
134 | ||
86d2bf49 MA |
135 | /* If Gtk+ is in use eventually gtk_main_iteration will be called, |
136 | unless retval is zero. */ | |
872870b2 | 137 | #ifdef USE_GTK |
e6e8a5eb PE |
138 | need_to_dispatch = retval == 0; |
139 | #else | |
140 | need_to_dispatch = true; | |
872870b2 | 141 | #endif |
e6e8a5eb PE |
142 | if (need_to_dispatch) |
143 | { | |
144 | int pselect_errno = errno; | |
6709d4da JC |
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 (); | |
e6e8a5eb | 149 | while (g_main_context_pending (context)) |
6709d4da JC |
150 | g_main_context_dispatch (context); |
151 | unblock_input (); | |
e6e8a5eb PE |
152 | errno = pselect_errno; |
153 | } | |
872870b2 | 154 | |
86d2bf49 MA |
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; | |
872870b2 JD |
160 | } |
161 | ||
162 | return retval; | |
163 | } | |
55a87246 | 164 | #endif /* HAVE_GLIB */ |