1 /* -*- c-basic-offset: 8 -*-
2    rdesktop: A Remote Desktop Protocol client.
3    Seamless Windows support
4    Copyright (C) Peter Astrand <astrand@cendio.se> 2005-2006
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include "rdesktop.h"
22 #include <stdarg.h>
23 #include <assert.h>
24 
25 /* #define WITH_DEBUG_SEAMLESS */
26 
27 #ifdef WITH_DEBUG_SEAMLESS
28 #define DEBUG_SEAMLESS(args) printf args;
29 #else
30 #define DEBUG_SEAMLESS(args)
31 #endif
32 
33 static char *
seamless_get_token(char ** s)34 seamless_get_token(char **s)
35 {
36 	char *comma, *head;
37 	head = *s;
38 
39 	if (!head)
40 		return NULL;
41 
42 	comma = strchr(head, ',');
43 	if (comma)
44 	{
45 		*comma = '\0';
46 		*s = comma + 1;
47 	}
48 	else
49 	{
50 		*s = NULL;
51 	}
52 
53 	return head;
54 }
55 
56 
57 static BOOL
seamless_process_line(RDPCLIENT * This,const char * line,void * data)58 seamless_process_line(RDPCLIENT * This, const char *line, void *data)
59 {
60 	char *p, *l;
61 	char *tok1, *tok2, *tok3, *tok4, *tok5, *tok6, *tok7, *tok8;
62 	unsigned long id, flags;
63 	char *endptr;
64 
65 	l = xstrdup(line);
66 	p = l;
67 
68 	DEBUG_SEAMLESS(("seamlessrdp got:%s\n", p));
69 
70 	tok1 = seamless_get_token(&p);
71 	tok2 = seamless_get_token(&p);
72 	tok3 = seamless_get_token(&p);
73 	tok4 = seamless_get_token(&p);
74 	tok5 = seamless_get_token(&p);
75 	tok6 = seamless_get_token(&p);
76 	tok7 = seamless_get_token(&p);
77 	tok8 = seamless_get_token(&p);
78 
79 	if (!strcmp("CREATE", tok1))
80 	{
81 		unsigned long group, parent;
82 		if (!tok6)
83 			return False;
84 
85 		id = strtoul(tok3, &endptr, 0);
86 		if (*endptr)
87 			return False;
88 
89 		group = strtoul(tok4, &endptr, 0);
90 		if (*endptr)
91 			return False;
92 
93 		parent = strtoul(tok5, &endptr, 0);
94 		if (*endptr)
95 			return False;
96 
97 		flags = strtoul(tok6, &endptr, 0);
98 		if (*endptr)
99 			return False;
100 
101 		ui_seamless_create_window(This, id, group, parent, flags);
102 	}
103 	else if (!strcmp("DESTROY", tok1))
104 	{
105 		if (!tok4)
106 			return False;
107 
108 		id = strtoul(tok3, &endptr, 0);
109 		if (*endptr)
110 			return False;
111 
112 		flags = strtoul(tok4, &endptr, 0);
113 		if (*endptr)
114 			return False;
115 
116 		ui_seamless_destroy_window(This, id, flags);
117 
118 	}
119 	else if (!strcmp("DESTROYGRP", tok1))
120 	{
121 		if (!tok4)
122 			return False;
123 
124 		id = strtoul(tok3, &endptr, 0);
125 		if (*endptr)
126 			return False;
127 
128 		flags = strtoul(tok4, &endptr, 0);
129 		if (*endptr)
130 			return False;
131 
132 		ui_seamless_destroy_group(This, id, flags);
133 	}
134 	else if (!strcmp("SETICON", tok1))
135 	{
136 		unimpl("SeamlessRDP SETICON1\n");
137 	}
138 	else if (!strcmp("POSITION", tok1))
139 	{
140 		int x, y, width, height;
141 
142 		if (!tok8)
143 			return False;
144 
145 		id = strtoul(tok3, &endptr, 0);
146 		if (*endptr)
147 			return False;
148 
149 		x = strtol(tok4, &endptr, 0);
150 		if (*endptr)
151 			return False;
152 		y = strtol(tok5, &endptr, 0);
153 		if (*endptr)
154 			return False;
155 
156 		width = strtol(tok6, &endptr, 0);
157 		if (*endptr)
158 			return False;
159 		height = strtol(tok7, &endptr, 0);
160 		if (*endptr)
161 			return False;
162 
163 		flags = strtoul(tok8, &endptr, 0);
164 		if (*endptr)
165 			return False;
166 
167 		ui_seamless_move_window(This, id, x, y, width, height, flags);
168 	}
169 	else if (!strcmp("ZCHANGE", tok1))
170 	{
171 		unsigned long behind;
172 
173 		id = strtoul(tok3, &endptr, 0);
174 		if (*endptr)
175 			return False;
176 
177 		behind = strtoul(tok4, &endptr, 0);
178 		if (*endptr)
179 			return False;
180 
181 		flags = strtoul(tok5, &endptr, 0);
182 		if (*endptr)
183 			return False;
184 
185 		ui_seamless_restack_window(This, id, behind, flags);
186 	}
187 	else if (!strcmp("TITLE", tok1))
188 	{
189 		if (!tok5)
190 			return False;
191 
192 		id = strtoul(tok3, &endptr, 0);
193 		if (*endptr)
194 			return False;
195 
196 		flags = strtoul(tok5, &endptr, 0);
197 		if (*endptr)
198 			return False;
199 
200 		ui_seamless_settitle(This, id, tok4, flags);
201 	}
202 	else if (!strcmp("STATE", tok1))
203 	{
204 		unsigned int state;
205 
206 		if (!tok5)
207 			return False;
208 
209 		id = strtoul(tok3, &endptr, 0);
210 		if (*endptr)
211 			return False;
212 
213 		state = strtoul(tok4, &endptr, 0);
214 		if (*endptr)
215 			return False;
216 
217 		flags = strtoul(tok5, &endptr, 0);
218 		if (*endptr)
219 			return False;
220 
221 		ui_seamless_setstate(This, id, state, flags);
222 	}
223 	else if (!strcmp("DEBUG", tok1))
224 	{
225 		DEBUG_SEAMLESS(("SeamlessRDP:%s\n", line));
226 	}
227 	else if (!strcmp("SYNCBEGIN", tok1))
228 	{
229 		if (!tok3)
230 			return False;
231 
232 		flags = strtoul(tok3, &endptr, 0);
233 		if (*endptr)
234 			return False;
235 
236 		ui_seamless_syncbegin(This, flags);
237 	}
238 	else if (!strcmp("SYNCEND", tok1))
239 	{
240 		if (!tok3)
241 			return False;
242 
243 		flags = strtoul(tok3, &endptr, 0);
244 		if (*endptr)
245 			return False;
246 
247 		/* do nothing, currently */
248 	}
249 	else if (!strcmp("HELLO", tok1))
250 	{
251 		if (!tok3)
252 			return False;
253 
254 		flags = strtoul(tok3, &endptr, 0);
255 		if (*endptr)
256 			return False;
257 
258 		ui_seamless_begin(This, !!(flags & SEAMLESSRDP_HELLO_HIDDEN));
259 	}
260 	else if (!strcmp("ACK", tok1))
261 	{
262 		unsigned int serial;
263 
264 		serial = strtoul(tok3, &endptr, 0);
265 		if (*endptr)
266 			return False;
267 
268 		ui_seamless_ack(This, serial);
269 	}
270 	else if (!strcmp("HIDE", tok1))
271 	{
272 		if (!tok3)
273 			return False;
274 
275 		flags = strtoul(tok3, &endptr, 0);
276 		if (*endptr)
277 			return False;
278 
279 		ui_seamless_hide_desktop(This);
280 	}
281 	else if (!strcmp("UNHIDE", tok1))
282 	{
283 		if (!tok3)
284 			return False;
285 
286 		flags = strtoul(tok3, &endptr, 0);
287 		if (*endptr)
288 			return False;
289 
290 		ui_seamless_unhide_desktop(This);
291 	}
292 
293 
294 	xfree(l);
295 	return True;
296 }
297 
298 
299 static BOOL
seamless_line_handler(RDPCLIENT * This,const char * line,void * data)300 seamless_line_handler(RDPCLIENT * This, const char *line, void *data)
301 {
302 	if (!seamless_process_line(This, line, data))
303 	{
304 		warning("SeamlessRDP: Invalid request:%s\n", line);
305 	}
306 	return True;
307 }
308 
309 
310 static void
seamless_process(RDPCLIENT * This,STREAM s)311 seamless_process(RDPCLIENT * This, STREAM s)
312 {
313 	unsigned int pkglen;
314 	static char *rest = NULL;
315 	char *buf;
316 
317 	pkglen = s->end - s->p;
318 	/* str_handle_lines requires null terminated strings */
319 	buf = xmalloc(pkglen + 1);
320 	STRNCPY(buf, (char *) s->p, pkglen + 1);
321 #if 0
322 	printf("seamless recv:\n");
323 	hexdump(s->p, pkglen);
324 #endif
325 
326 	str_handle_lines(This, buf, &rest, seamless_line_handler, NULL);
327 
328 	xfree(buf);
329 }
330 
331 
332 BOOL
seamless_init(RDPCLIENT * This)333 seamless_init(RDPCLIENT * This)
334 {
335 	if (!This->seamless_rdp)
336 		return False;
337 
338 	This->seamless.serial = 0;
339 
340 	This->seamless.channel =
341 		channel_register(This, "seamrdp", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP,
342 				 seamless_process);
343 	return (This->seamless.channel != NULL);
344 }
345 
346 
347 static unsigned int
seamless_send(RDPCLIENT * This,const char * command,const char * format,...)348 seamless_send(RDPCLIENT * This, const char *command, const char *format, ...)
349 {
350 	STREAM s;
351 	size_t len;
352 	va_list argp;
353 	char buf[1025];
354 
355 	len = snprintf(buf, sizeof(buf) - 1, "%s,%u,", command, This->seamless.serial);
356 
357 	assert(len < (sizeof(buf) - 1));
358 
359 	va_start(argp, format);
360 	len += vsnprintf(buf + len, sizeof(buf) - len - 1, format, argp);
361 	va_end(argp);
362 
363 	assert(len < (sizeof(buf) - 1));
364 
365 	buf[len] = '\n';
366 	buf[len + 1] = '\0';
367 
368 	len++;
369 
370 	s = channel_init(This, This->seamless.channel, len);
371 	out_uint8p(s, buf, len) s_mark_end(s);
372 
373 	DEBUG_SEAMLESS(("SeamlessRDP sending:%s", buf));
374 
375 #if 0
376 	printf("seamless send:\n");
377 	hexdump(s->channel_hdr + 8, s->end - s->channel_hdr - 8);
378 #endif
379 
380 	channel_send(This, s, This->seamless.channel);
381 
382 	return This->seamless.serial++;
383 }
384 
385 
386 unsigned int
seamless_send_sync(RDPCLIENT * This)387 seamless_send_sync(RDPCLIENT * This)
388 {
389 	if (!This->seamless_rdp)
390 		return (unsigned int) -1;
391 
392 	return seamless_send(This, "SYNC", "");
393 }
394 
395 
396 unsigned int
seamless_send_state(RDPCLIENT * This,unsigned long id,unsigned int state,unsigned long flags)397 seamless_send_state(RDPCLIENT * This, unsigned long id, unsigned int state, unsigned long flags)
398 {
399 	if (!This->seamless_rdp)
400 		return (unsigned int) -1;
401 
402 	return seamless_send(This, "STATE", "0x%08lx,0x%x,0x%lx", id, state, flags);
403 }
404 
405 
406 unsigned int
seamless_send_position(RDPCLIENT * This,unsigned long id,int x,int y,int width,int height,unsigned long flags)407 seamless_send_position(RDPCLIENT * This, unsigned long id, int x, int y, int width, int height, unsigned long flags)
408 {
409 	return seamless_send(This, "POSITION", "0x%08lx,%d,%d,%d,%d,0x%lx", id, x, y, width, height,
410 			     flags);
411 }
412 
413 
414 /* Update select timeout */
415 void
seamless_select_timeout(RDPCLIENT * This,struct timeval * tv)416 seamless_select_timeout(RDPCLIENT * This, struct timeval *tv)
417 {
418 	struct timeval ourtimeout = { 0, SEAMLESSRDP_POSITION_TIMER };
419 
420 	if (This->seamless_rdp)
421 	{
422 		if (timercmp(&ourtimeout, tv, <))
423 		{
424 			tv->tv_sec = ourtimeout.tv_sec;
425 			tv->tv_usec = ourtimeout.tv_usec;
426 		}
427 	}
428 }
429 
430 
431 unsigned int
seamless_send_zchange(RDPCLIENT * This,unsigned long id,unsigned long below,unsigned long flags)432 seamless_send_zchange(RDPCLIENT * This, unsigned long id, unsigned long below, unsigned long flags)
433 {
434 	if (!This->seamless_rdp)
435 		return (unsigned int) -1;
436 
437 	return seamless_send(This, "ZCHANGE", "0x%08lx,0x%08lx,0x%lx", id, below, flags);
438 }
439 
440 
441 
442 unsigned int
seamless_send_focus(RDPCLIENT * This,unsigned long id,unsigned long flags)443 seamless_send_focus(RDPCLIENT * This, unsigned long id, unsigned long flags)
444 {
445 	if (!This->seamless_rdp)
446 		return (unsigned int) -1;
447 
448 	return seamless_send(This, "FOCUS", "0x%08lx,0x%lx", id, flags);
449 }
450