1 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
2  * Copyright 2011-2014 Pierre Ossman for Cendio AB
3  *
4  * This is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This software is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this software; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
17  * USA.
18  */
19 
20 #ifdef HAVE_DIX_CONFIG_H
21 #include <dix-config.h>
22 #endif
23 
24 #include <errno.h>
25 
26 #include <X11/Xpoll.h>
27 
28 #include "os.h"
29 #include "dix.h"
30 #include "scrnintstr.h"
31 
32 #include "vncExtInit.h"
33 #include "vncBlockHandler.h"
34 #include "xorg-version.h"
35 
36 #if XORG_AT_LEAST(1, 19, 0)
37 static void vncBlockHandler(void* data, void* timeout);
38 static void vncSocketNotify(int fd, int xevents, void *data);
39 #else
40 static void vncBlockHandler(void * data, OSTimePtr t, void * readmask);
41 static void vncWakeupHandler(void * data, int nfds, void * readmask);
42 
43 struct vncFdEntry {
44   int fd;
45   int read, write;
46   int scrIdx;
47   struct vncFdEntry* next;
48 };
49 
50 static struct vncFdEntry* fdsHead = NULL;
51 #endif
52 
vncRegisterBlockHandlers(void)53 void vncRegisterBlockHandlers(void)
54 {
55   if (!RegisterBlockAndWakeupHandlers(vncBlockHandler,
56 #if XORG_AT_LEAST(1, 19, 0)
57                                       (ServerWakeupHandlerProcPtr)NoopDDA,
58 #else
59                                       vncWakeupHandler,
60 #endif
61                                       0))
62     FatalError("RegisterBlockAndWakeupHandlers() failed\n");
63 }
64 
vncSetNotifyFd(int fd,int scrIdx,int read,int write)65 void vncSetNotifyFd(int fd, int scrIdx, int read, int write)
66 {
67 #if XORG_AT_LEAST(1, 19, 0)
68   int mask = (read ? X_NOTIFY_READ : 0) | (write ? X_NOTIFY_WRITE : 0);
69   SetNotifyFd(fd, vncSocketNotify, mask, (void*)(intptr_t)scrIdx);
70 #else
71   struct vncFdEntry* entry;
72 
73   entry = fdsHead;
74   while (entry) {
75     if (entry->fd == fd) {
76       assert(entry->scrIdx == scrIdx);
77       entry->read = read;
78       entry->write = write;
79       return;
80     }
81     entry = entry->next;
82   }
83 
84   entry = malloc(sizeof(struct vncFdEntry));
85   memset(entry, 0, sizeof(struct vncFdEntry));
86 
87   entry->fd = fd;
88   entry->scrIdx = scrIdx;
89   entry->read = read;
90   entry->write = write;
91 
92   entry->next = fdsHead;
93   fdsHead = entry;
94 #endif
95 }
96 
vncRemoveNotifyFd(int fd)97 void vncRemoveNotifyFd(int fd)
98 {
99 #if XORG_AT_LEAST(1, 19, 0)
100   RemoveNotifyFd(fd);
101 #else
102   struct vncFdEntry** prev;
103   struct vncFdEntry* entry;
104 
105   prev = &fdsHead;
106   entry = fdsHead;
107   while (entry) {
108     if (entry->fd == fd) {
109       *prev = entry->next;
110       return;
111     }
112     prev = &entry->next;
113     entry = entry->next;
114   }
115 
116   assert(FALSE);
117 #endif
118 }
119 
120 #if XORG_AT_LEAST(1, 19, 0)
vncSocketNotify(int fd,int xevents,void * data)121 static void vncSocketNotify(int fd, int xevents, void *data)
122 {
123   int scrIdx;
124 
125   scrIdx = (intptr_t)data;
126   vncHandleSocketEvent(fd, scrIdx,
127                        xevents & X_NOTIFY_READ,
128                        xevents & X_NOTIFY_WRITE);
129 }
130 #endif
131 
132 #if XORG_OLDER_THAN(1, 19, 0)
133 static void vncWriteBlockHandlerFallback(OSTimePtr timeout);
134 static void vncWriteWakeupHandlerFallback(void);
135 void vncWriteBlockHandler(fd_set *fds);
136 void vncWriteWakeupHandler(int nfds, fd_set *fds);
137 #endif
138 
139 //
140 // vncBlockHandler - called just before the X server goes into poll().
141 //
142 // For older versions of X this also allows us to register file
143 // descriptors that we want read events on.
144 //
145 
146 #if XORG_AT_LEAST(1, 19, 0)
vncBlockHandler(void * data,void * timeout)147 static void vncBlockHandler(void* data, void* timeout)
148 #else
149 static void vncBlockHandler(void * data, OSTimePtr t, void * readmask)
150 #endif
151 {
152 #if XORG_OLDER_THAN(1, 19, 0)
153   int _timeout;
154   int* timeout = &_timeout;
155   static struct timeval tv;
156 
157   fd_set* fds;
158   static struct vncFdEntry* entry;
159 
160   if (*t == NULL)
161     _timeout = -1;
162   else
163     _timeout = (*t)->tv_sec * 1000 + (*t)->tv_usec / 1000;
164 #endif
165 
166   vncCallBlockHandlers(timeout);
167 
168 #if XORG_OLDER_THAN(1, 19, 0)
169   if (_timeout != -1) {
170     tv.tv_sec= _timeout / 1000;
171     tv.tv_usec = (_timeout % 1000) * 1000;
172     *t = &tv;
173   }
174 
175   fds = (fd_set*)readmask;
176   entry = fdsHead;
177   while (entry) {
178     if (entry->read)
179       FD_SET(entry->fd, fds);
180     entry = entry->next;
181   }
182 
183   vncWriteBlockHandlerFallback(t);
184 #endif
185 }
186 
187 #if XORG_OLDER_THAN(1, 19, 0)
vncWakeupHandler(void * data,int nfds,void * readmask)188 static void vncWakeupHandler(void * data, int nfds, void * readmask)
189 {
190   fd_set* fds = (fd_set*)readmask;
191 
192   static struct vncFdEntry* entry;
193 
194   if (nfds <= 0)
195     return;
196 
197   entry = fdsHead;
198   while (entry) {
199     if (entry->read && FD_ISSET(entry->fd, fds))
200       vncHandleSocketEvent(entry->fd, entry->scrIdx, TRUE, FALSE);
201     entry = entry->next;
202   }
203 
204   vncWriteWakeupHandlerFallback();
205 }
206 #endif
207 
208 //
209 // vncWriteBlockHandler - extra hack to be able to get old versions of the X
210 // server to monitor writeable fds and not just readable. This requirers a
211 // modified Xorg and might therefore not be called.
212 //
213 
214 #if XORG_OLDER_THAN(1, 19, 0)
215 static Bool needFallback = TRUE;
216 static fd_set fallbackFds;
217 static struct timeval tw;
218 
vncWriteBlockHandler(fd_set * fds)219 void vncWriteBlockHandler(fd_set *fds)
220 {
221   static struct vncFdEntry* entry;
222 
223   needFallback = FALSE;
224 
225   entry = fdsHead;
226   while (entry) {
227     if (entry->write)
228       FD_SET(entry->fd, fds);
229     entry = entry->next;
230   }
231 }
232 
vncWriteWakeupHandler(int nfds,fd_set * fds)233 void vncWriteWakeupHandler(int nfds, fd_set *fds)
234 {
235   static struct vncFdEntry* entry;
236 
237   if (nfds <= 0)
238     return;
239 
240   entry = fdsHead;
241   while (entry) {
242     if (entry->write && FD_ISSET(entry->fd, fds))
243       vncHandleSocketEvent(entry->fd, entry->scrIdx, FALSE, TRUE);
244     entry = entry->next;
245   }
246 }
247 
vncWriteBlockHandlerFallback(OSTimePtr timeout)248 static void vncWriteBlockHandlerFallback(OSTimePtr timeout)
249 {
250   if (!needFallback)
251     return;
252 
253   FD_ZERO(&fallbackFds);
254   vncWriteBlockHandler(&fallbackFds);
255 
256   // vncWriteBlockHandler() will clear this, so we need to restore it
257   needFallback = TRUE;
258 
259   if (!XFD_ANYSET(&fallbackFds))
260     return;
261 
262   if ((*timeout == NULL) ||
263       ((*timeout)->tv_sec > 0) || ((*timeout)->tv_usec > 10000)) {
264     tw.tv_sec = 0;
265     tw.tv_usec = 10000;
266     *timeout = &tw;
267   }
268 }
269 
vncWriteWakeupHandlerFallback(void)270 static void vncWriteWakeupHandlerFallback(void)
271 {
272   int ret;
273   struct timeval timeout;
274 
275   if (!needFallback)
276     return;
277 
278   if (!XFD_ANYSET(&fallbackFds))
279     return;
280 
281   timeout.tv_sec = 0;
282   timeout.tv_usec = 0;
283 
284   ret = select(XFD_SETSIZE, NULL, &fallbackFds, NULL, &timeout);
285   if (ret < 0) {
286     ErrorF("vncWriteWakeupHandlerFallback(): select: %s\n",
287            strerror(errno));
288     return;
289   }
290 
291   if (ret == 0)
292     return;
293 
294   vncWriteWakeupHandler(ret, &fallbackFds);
295 }
296 #endif
297