1 /* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 User interface services - X Window System
4 Copyright (C) Matthew Chapman 1999-2005
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 <X11/Xlib.h>
22 #include <X11/Xutil.h>
23 #include <unistd.h>
24 #include <sys/time.h>
25 #include <time.h>
26 #include <errno.h>
27 #include <strings.h>
28 #include "rdesktop.h"
29 #include "xproto.h"
30
31 /* We can't include Xproto.h because of conflicting defines for BOOL */
32 #define X_ConfigureWindow 12
33
34 /* MWM decorations */
35 #define MWM_HINTS_DECORATIONS (1L << 1)
36 #define PROP_MOTIF_WM_HINTS_ELEMENTS 5
37 typedef struct
38 {
39 unsigned long flags;
40 unsigned long functions;
41 unsigned long decorations;
42 long inputMode;
43 unsigned long status;
44 }
45 PropMotifWmHints;
46
47 typedef struct
48 {
49 uint32 red;
50 uint32 green;
51 uint32 blue;
52 }
53 PixelColour;
54
55 #define ON_ALL_SEAMLESS_WINDOWS(func, args) \
56 do { \
57 seamless_window *sw; \
58 XRectangle rect; \
59 if (!This->xwin.seamless_windows) break; \
60 for (sw = This->xwin.seamless_windows; sw; sw = sw->next) { \
61 rect.x = This->xwin.clip_rectangle.x - sw->xoffset; \
62 rect.y = This->xwin.clip_rectangle.y - sw->yoffset; \
63 rect.width = This->xwin.clip_rectangle.width; \
64 rect.height = This->xwin.clip_rectangle.height; \
65 XSetClipRectangles(This->display, This->xwin.gc, 0, 0, &rect, 1, YXBanded); \
66 func args; \
67 } \
68 XSetClipRectangles(This->display, This->xwin.gc, 0, 0, &This->xwin.clip_rectangle, 1, YXBanded); \
69 } while (0)
70
71 static void
seamless_XFillPolygon(RDPCLIENT * This,Drawable d,XPoint * points,int npoints,int xoffset,int yoffset)72 seamless_XFillPolygon(RDPCLIENT * This, Drawable d, XPoint * points, int npoints, int xoffset, int yoffset)
73 {
74 points[0].x -= xoffset;
75 points[0].y -= yoffset;
76 XFillPolygon(This->display, d, This->xwin.gc, points, npoints, Complex, CoordModePrevious);
77 points[0].x += xoffset;
78 points[0].y += yoffset;
79 }
80
81 static void
seamless_XDrawLines(RDPCLIENT * This,Drawable d,XPoint * points,int npoints,int xoffset,int yoffset)82 seamless_XDrawLines(RDPCLIENT * This, Drawable d, XPoint * points, int npoints, int xoffset, int yoffset)
83 {
84 points[0].x -= xoffset;
85 points[0].y -= yoffset;
86 XDrawLines(This->display, d, This->xwin.gc, points, npoints, CoordModePrevious);
87 points[0].x += xoffset;
88 points[0].y += yoffset;
89 }
90
91 #define FILL_RECTANGLE(x,y,cx,cy)\
92 { \
93 XFillRectangle(This->display, This->wnd, This->xwin.gc, x, y, cx, cy); \
94 ON_ALL_SEAMLESS_WINDOWS(XFillRectangle, (This->display, sw->wnd, This->xwin.gc, x-sw->xoffset, y-sw->yoffset, cx, cy)); \
95 if (This->ownbackstore) \
96 XFillRectangle(This->display, This->xwin.backstore, This->xwin.gc, x, y, cx, cy); \
97 }
98
99 #define FILL_RECTANGLE_BACKSTORE(x,y,cx,cy)\
100 { \
101 XFillRectangle(This->display, This->ownbackstore ? This->xwin.backstore : This->wnd, This->xwin.gc, x, y, cx, cy); \
102 }
103
104 #define FILL_POLYGON(p,np)\
105 { \
106 XFillPolygon(This->display, This->wnd, This->xwin.gc, p, np, Complex, CoordModePrevious); \
107 if (This->ownbackstore) \
108 XFillPolygon(This->display, This->xwin.backstore, This->xwin.gc, p, np, Complex, CoordModePrevious); \
109 ON_ALL_SEAMLESS_WINDOWS(seamless_XFillPolygon, (This, sw->wnd, p, np, sw->xoffset, sw->yoffset)); \
110 }
111
112 #define DRAW_ELLIPSE(x,y,cx,cy,m)\
113 { \
114 switch (m) \
115 { \
116 case 0: /* Outline */ \
117 XDrawArc(This->display, This->wnd, This->xwin.gc, x, y, cx, cy, 0, 360*64); \
118 ON_ALL_SEAMLESS_WINDOWS(XDrawArc, (This->display, sw->wnd, This->xwin.gc, x-sw->xoffset, y-sw->yoffset, cx, cy, 0, 360*64)); \
119 if (This->ownbackstore) \
120 XDrawArc(This->display, This->xwin.backstore, This->xwin.gc, x, y, cx, cy, 0, 360*64); \
121 break; \
122 case 1: /* Filled */ \
123 XFillArc(This->display, This->wnd, This->xwin.gc, x, y, cx, cy, 0, 360*64); \
124 ON_ALL_SEAMLESS_WINDOWS(XCopyArea, (This->display, This->ownbackstore ? This->xwin.backstore : This->wnd, sw->wnd, This->xwin.gc, \
125 x, y, cx, cy, x-sw->xoffset, y-sw->yoffset)); \
126 if (This->ownbackstore) \
127 XFillArc(This->display, This->xwin.backstore, This->xwin.gc, x, y, cx, cy, 0, 360*64); \
128 break; \
129 } \
130 }
131
132 #define TRANSLATE(col) ( This->server_depth != 8 ? translate_colour(This, col) : This->owncolmap ? col : This->xwin.colmap[col] )
133 #define SET_FOREGROUND(col) XSetForeground(This->display, This->xwin.gc, TRANSLATE(col));
134 #define SET_BACKGROUND(col) XSetBackground(This->display, This->xwin.gc, TRANSLATE(col));
135
136 static const int rop2_map[] = {
137 GXclear, /* 0 */
138 GXnor, /* DPon */
139 GXandInverted, /* DPna */
140 GXcopyInverted, /* Pn */
141 GXandReverse, /* PDna */
142 GXinvert, /* Dn */
143 GXxor, /* DPx */
144 GXnand, /* DPan */
145 GXand, /* DPa */
146 GXequiv, /* DPxn */
147 GXnoop, /* D */
148 GXorInverted, /* DPno */
149 GXcopy, /* P */
150 GXorReverse, /* PDno */
151 GXor, /* DPo */
152 GXset /* 1 */
153 };
154
155 #define SET_FUNCTION(rop2) { if (rop2 != ROP2_COPY) XSetFunction(This->display, This->xwin.gc, rop2_map[rop2]); }
156 #define RESET_FUNCTION(rop2) { if (rop2 != ROP2_COPY) XSetFunction(This->display, This->xwin.gc, GXcopy); }
157
158 static seamless_window *
sw_get_window_by_id(RDPCLIENT * This,unsigned long id)159 sw_get_window_by_id(RDPCLIENT * This, unsigned long id)
160 {
161 seamless_window *sw;
162 for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
163 {
164 if (sw->id == id)
165 return sw;
166 }
167 return NULL;
168 }
169
170
171 static seamless_window *
sw_get_window_by_wnd(RDPCLIENT * This,Window wnd)172 sw_get_window_by_wnd(RDPCLIENT * This, Window wnd)
173 {
174 seamless_window *sw;
175 for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
176 {
177 if (sw->wnd == wnd)
178 return sw;
179 }
180 return NULL;
181 }
182
183
184 static void
sw_remove_window(RDPCLIENT * This,seamless_window * win)185 sw_remove_window(RDPCLIENT * This, seamless_window * win)
186 {
187 seamless_window *sw, **prevnext = &This->xwin.seamless_windows;
188 for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
189 {
190 if (sw == win)
191 {
192 *prevnext = sw->next;
193 sw->group->refcnt--;
194 if (sw->group->refcnt == 0)
195 {
196 XDestroyWindow(This->display, sw->group->wnd);
197 xfree(sw->group);
198 }
199 xfree(sw->position_timer);
200 xfree(sw);
201 return;
202 }
203 prevnext = &sw->next;
204 }
205 return;
206 }
207
208
209 /* Move all windows except wnd to new desktop */
210 static void
sw_all_to_desktop(RDPCLIENT * This,Window wnd,unsigned int desktop)211 sw_all_to_desktop(RDPCLIENT * This, Window wnd, unsigned int desktop)
212 {
213 seamless_window *sw;
214 for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
215 {
216 if (sw->wnd == wnd)
217 continue;
218 if (sw->desktop != desktop)
219 {
220 ewmh_move_to_desktop(This, sw->wnd, desktop);
221 sw->desktop = desktop;
222 }
223 }
224 }
225
226
227 /* Send our position */
228 static void
sw_update_position(RDPCLIENT * This,seamless_window * sw)229 sw_update_position(RDPCLIENT * This, seamless_window * sw)
230 {
231 XWindowAttributes wa;
232 int x, y;
233 Window child_return;
234 unsigned int serial;
235
236 XGetWindowAttributes(This->display, sw->wnd, &wa);
237 XTranslateCoordinates(This->display, sw->wnd, wa.root,
238 -wa.border_width, -wa.border_width, &x, &y, &child_return);
239
240 serial = seamless_send_position(This, sw->id, x, y, wa.width, wa.height, 0);
241
242 sw->outstanding_position = True;
243 sw->outpos_serial = serial;
244
245 sw->outpos_xoffset = x;
246 sw->outpos_yoffset = y;
247 sw->outpos_width = wa.width;
248 sw->outpos_height = wa.height;
249 }
250
251
252 /* Check if it's time to send our position */
253 static void
sw_check_timers(RDPCLIENT * This)254 sw_check_timers(RDPCLIENT * This)
255 {
256 seamless_window *sw;
257 struct timeval now;
258
259 gettimeofday(&now, NULL);
260 for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
261 {
262 if (timerisset(sw->position_timer) && timercmp(sw->position_timer, &now, <))
263 {
264 timerclear(sw->position_timer);
265 sw_update_position(This, sw);
266 }
267 }
268 }
269
270
271 static void
sw_restack_window(RDPCLIENT * This,seamless_window * sw,unsigned long behind)272 sw_restack_window(RDPCLIENT * This, seamless_window * sw, unsigned long behind)
273 {
274 seamless_window *sw_above;
275
276 /* Remove window from stack */
277 for (sw_above = This->xwin.seamless_windows; sw_above; sw_above = sw_above->next)
278 {
279 if (sw_above->behind == sw->id)
280 break;
281 }
282
283 if (sw_above)
284 sw_above->behind = sw->behind;
285
286 /* And then add it at the new position */
287 for (sw_above = This->xwin.seamless_windows; sw_above; sw_above = sw_above->next)
288 {
289 if (sw_above->behind == behind)
290 break;
291 }
292
293 if (sw_above)
294 sw_above->behind = sw->id;
295
296 sw->behind = behind;
297 }
298
299
300 static void
sw_handle_restack(RDPCLIENT * This,seamless_window * sw)301 sw_handle_restack(RDPCLIENT * This, seamless_window * sw)
302 {
303 Status status;
304 Window root, parent, *children;
305 unsigned int nchildren, i;
306 seamless_window *sw_below;
307
308 status = XQueryTree(This->display, RootWindowOfScreen(This->xwin.screen),
309 &root, &parent, &children, &nchildren);
310 if (!status || !nchildren)
311 return;
312
313 sw_below = NULL;
314
315 i = 0;
316 while (children[i] != sw->wnd)
317 {
318 i++;
319 if (i >= nchildren)
320 goto end;
321 }
322
323 for (i++; i < nchildren; i++)
324 {
325 sw_below = sw_get_window_by_wnd(This, children[i]);
326 if (sw_below)
327 break;
328 }
329
330 if (!sw_below && !sw->behind)
331 goto end;
332 if (sw_below && (sw_below->id == sw->behind))
333 goto end;
334
335 if (sw_below)
336 {
337 seamless_send_zchange(This, sw->id, sw_below->id, 0);
338 sw_restack_window(This, sw, sw_below->id);
339 }
340 else
341 {
342 seamless_send_zchange(This, sw->id, 0, 0);
343 sw_restack_window(This, sw, 0);
344 }
345
346 end:
347 XFree(children);
348 }
349
350
351 static seamless_group *
sw_find_group(RDPCLIENT * This,unsigned long id,BOOL dont_create)352 sw_find_group(RDPCLIENT * This, unsigned long id, BOOL dont_create)
353 {
354 seamless_window *sw;
355 seamless_group *sg;
356 XSetWindowAttributes attribs;
357
358 for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
359 {
360 if (sw->group->id == id)
361 return sw->group;
362 }
363
364 if (dont_create)
365 return NULL;
366
367 sg = xmalloc(sizeof(seamless_group));
368
369 sg->wnd =
370 XCreateWindow(This->display, RootWindowOfScreen(This->xwin.screen), -1, -1, 1, 1, 0,
371 CopyFromParent, CopyFromParent, CopyFromParent, 0, &attribs);
372
373 sg->id = id;
374 sg->refcnt = 0;
375
376 return sg;
377 }
378
379
380 static void
mwm_hide_decorations(RDPCLIENT * This,Window wnd)381 mwm_hide_decorations(RDPCLIENT * This, Window wnd)
382 {
383 PropMotifWmHints motif_hints;
384 Atom hintsatom;
385
386 /* setup the property */
387 motif_hints.flags = MWM_HINTS_DECORATIONS;
388 motif_hints.decorations = 0;
389
390 /* get the atom for the property */
391 hintsatom = XInternAtom(This->display, "_MOTIF_WM_HINTS", False);
392 if (!hintsatom)
393 {
394 warning("Failed to get atom _MOTIF_WM_HINTS: probably your window manager does not support MWM hints\n");
395 return;
396 }
397
398 XChangeProperty(This->display, wnd, hintsatom, hintsatom, 32, PropModeReplace,
399 (unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
400
401 }
402
403 #define SPLITCOLOUR15(colour, rv) \
404 { \
405 rv.red = ((colour >> 7) & 0xf8) | ((colour >> 12) & 0x7); \
406 rv.green = ((colour >> 2) & 0xf8) | ((colour >> 8) & 0x7); \
407 rv.blue = ((colour << 3) & 0xf8) | ((colour >> 2) & 0x7); \
408 }
409
410 #define SPLITCOLOUR16(colour, rv) \
411 { \
412 rv.red = ((colour >> 8) & 0xf8) | ((colour >> 13) & 0x7); \
413 rv.green = ((colour >> 3) & 0xfc) | ((colour >> 9) & 0x3); \
414 rv.blue = ((colour << 3) & 0xf8) | ((colour >> 2) & 0x7); \
415 } \
416
417 #define SPLITCOLOUR24(colour, rv) \
418 { \
419 rv.blue = (colour & 0xff0000) >> 16; \
420 rv.green = (colour & 0x00ff00) >> 8; \
421 rv.red = (colour & 0x0000ff); \
422 }
423
424 #define MAKECOLOUR(pc) \
425 ((pc.red >> This->xwin.red_shift_r) << This->xwin.red_shift_l) \
426 | ((pc.green >> This->xwin.green_shift_r) << This->xwin.green_shift_l) \
427 | ((pc.blue >> This->xwin.blue_shift_r) << This->xwin.blue_shift_l) \
428
429 #define BSWAP16(x) { x = (((x & 0xff) << 8) | (x >> 8)); }
430 #define BSWAP24(x) { x = (((x & 0xff) << 16) | (x >> 16) | (x & 0xff00)); }
431 #define BSWAP32(x) { x = (((x & 0xff00ff) << 8) | ((x >> 8) & 0xff00ff)); \
432 x = (x << 16) | (x >> 16); }
433
434 /* The following macros output the same octet sequences
435 on both BE and LE hosts: */
436
437 #define BOUT16(o, x) { *(o++) = x >> 8; *(o++) = x; }
438 #define BOUT24(o, x) { *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }
439 #define BOUT32(o, x) { *(o++) = x >> 24; *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }
440 #define LOUT16(o, x) { *(o++) = x; *(o++) = x >> 8; }
441 #define LOUT24(o, x) { *(o++) = x; *(o++) = x >> 8; *(o++) = x >> 16; }
442 #define LOUT32(o, x) { *(o++) = x; *(o++) = x >> 8; *(o++) = x >> 16; *(o++) = x >> 24; }
443
444 static uint32
translate_colour(RDPCLIENT * This,uint32 colour)445 translate_colour(RDPCLIENT * This, uint32 colour)
446 {
447 PixelColour pc;
448 switch (This->server_depth)
449 {
450 case 15:
451 SPLITCOLOUR15(colour, pc);
452 break;
453 case 16:
454 SPLITCOLOUR16(colour, pc);
455 break;
456 case 24:
457 SPLITCOLOUR24(colour, pc);
458 break;
459 default:
460 /* Avoid warning */
461 pc.red = 0;
462 pc.green = 0;
463 pc.blue = 0;
464 break;
465 }
466 return MAKECOLOUR(pc);
467 }
468
469 /* indent is confused by UNROLL8 */
470 /* *INDENT-OFF* */
471
472 /* repeat and unroll, similar to bitmap.c */
473 /* potentialy any of the following translate */
474 /* functions can use repeat but just doing */
475 /* the most common ones */
476
477 #define UNROLL8(stm) { stm stm stm stm stm stm stm stm }
478 /* 2 byte output repeat */
479 #define REPEAT2(stm) \
480 { \
481 while (out <= end - 8 * 2) \
482 UNROLL8(stm) \
483 while (out < end) \
484 { stm } \
485 }
486 /* 3 byte output repeat */
487 #define REPEAT3(stm) \
488 { \
489 while (out <= end - 8 * 3) \
490 UNROLL8(stm) \
491 while (out < end) \
492 { stm } \
493 }
494 /* 4 byte output repeat */
495 #define REPEAT4(stm) \
496 { \
497 while (out <= end - 8 * 4) \
498 UNROLL8(stm) \
499 while (out < end) \
500 { stm } \
501 }
502 /* *INDENT-ON* */
503
504 static void
translate8to8(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)505 translate8to8(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
506 {
507 while (out < end)
508 *(out++) = (uint8) This->xwin.colmap[*(data++)];
509 }
510
511 static void
translate8to16(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)512 translate8to16(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
513 {
514 uint16 value;
515
516 if (This->xwin.compatible_arch)
517 {
518 /* *INDENT-OFF* */
519 REPEAT2
520 (
521 *((uint16 *) out) = This->xwin.colmap[*(data++)];
522 out += 2;
523 )
524 /* *INDENT-ON* */
525 }
526 else if (This->xwin.xserver_be)
527 {
528 while (out < end)
529 {
530 value = (uint16) This->xwin.colmap[*(data++)];
531 BOUT16(out, value);
532 }
533 }
534 else
535 {
536 while (out < end)
537 {
538 value = (uint16) This->xwin.colmap[*(data++)];
539 LOUT16(out, value);
540 }
541 }
542 }
543
544 /* little endian - conversion happens when colourmap is built */
545 static void
translate8to24(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)546 translate8to24(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
547 {
548 uint32 value;
549
550 if (This->xwin.compatible_arch)
551 {
552 while (out < end)
553 {
554 value = This->xwin.colmap[*(data++)];
555 BOUT24(out, value);
556 }
557 }
558 else
559 {
560 while (out < end)
561 {
562 value = This->xwin.colmap[*(data++)];
563 LOUT24(out, value);
564 }
565 }
566 }
567
568 static void
translate8to32(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)569 translate8to32(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
570 {
571 uint32 value;
572
573 if (This->xwin.compatible_arch)
574 {
575 /* *INDENT-OFF* */
576 REPEAT4
577 (
578 *((uint32 *) out) = This->xwin.colmap[*(data++)];
579 out += 4;
580 )
581 /* *INDENT-ON* */
582 }
583 else if (This->xwin.xserver_be)
584 {
585 while (out < end)
586 {
587 value = This->xwin.colmap[*(data++)];
588 BOUT32(out, value);
589 }
590 }
591 else
592 {
593 while (out < end)
594 {
595 value = This->xwin.colmap[*(data++)];
596 LOUT32(out, value);
597 }
598 }
599 }
600
601 static void
translate15to16(RDPCLIENT * This,const uint16 * data,uint8 * out,uint8 * end)602 translate15to16(RDPCLIENT * This, const uint16 * data, uint8 * out, uint8 * end)
603 {
604 uint16 pixel;
605 uint16 value;
606 PixelColour pc;
607
608 if (This->xwin.xserver_be)
609 {
610 while (out < end)
611 {
612 pixel = *(data++);
613 if (This->xwin.host_be)
614 {
615 BSWAP16(pixel);
616 }
617 SPLITCOLOUR15(pixel, pc);
618 value = MAKECOLOUR(pc);
619 BOUT16(out, value);
620 }
621 }
622 else
623 {
624 while (out < end)
625 {
626 pixel = *(data++);
627 if (This->xwin.host_be)
628 {
629 BSWAP16(pixel);
630 }
631 SPLITCOLOUR15(pixel, pc);
632 value = MAKECOLOUR(pc);
633 LOUT16(out, value);
634 }
635 }
636 }
637
638 static void
translate15to24(RDPCLIENT * This,const uint16 * data,uint8 * out,uint8 * end)639 translate15to24(RDPCLIENT * This, const uint16 * data, uint8 * out, uint8 * end)
640 {
641 uint32 value;
642 uint16 pixel;
643 PixelColour pc;
644
645 if (This->xwin.compatible_arch)
646 {
647 /* *INDENT-OFF* */
648 REPEAT3
649 (
650 pixel = *(data++);
651 SPLITCOLOUR15(pixel, pc);
652 *(out++) = pc.blue;
653 *(out++) = pc.green;
654 *(out++) = pc.red;
655 )
656 /* *INDENT-ON* */
657 }
658 else if (This->xwin.xserver_be)
659 {
660 while (out < end)
661 {
662 pixel = *(data++);
663 if (This->xwin.host_be)
664 {
665 BSWAP16(pixel);
666 }
667 SPLITCOLOUR15(pixel, pc);
668 value = MAKECOLOUR(pc);
669 BOUT24(out, value);
670 }
671 }
672 else
673 {
674 while (out < end)
675 {
676 pixel = *(data++);
677 if (This->xwin.host_be)
678 {
679 BSWAP16(pixel);
680 }
681 SPLITCOLOUR15(pixel, pc);
682 value = MAKECOLOUR(pc);
683 LOUT24(out, value);
684 }
685 }
686 }
687
688 static void
translate15to32(RDPCLIENT * This,const uint16 * data,uint8 * out,uint8 * end)689 translate15to32(RDPCLIENT * This, const uint16 * data, uint8 * out, uint8 * end)
690 {
691 uint16 pixel;
692 uint32 value;
693 PixelColour pc;
694
695 if (This->xwin.compatible_arch)
696 {
697 /* *INDENT-OFF* */
698 REPEAT4
699 (
700 pixel = *(data++);
701 SPLITCOLOUR15(pixel, pc);
702 *(out++) = pc.blue;
703 *(out++) = pc.green;
704 *(out++) = pc.red;
705 *(out++) = 0;
706 )
707 /* *INDENT-ON* */
708 }
709 else if (This->xwin.xserver_be)
710 {
711 while (out < end)
712 {
713 pixel = *(data++);
714 if (This->xwin.host_be)
715 {
716 BSWAP16(pixel);
717 }
718 SPLITCOLOUR15(pixel, pc);
719 value = MAKECOLOUR(pc);
720 BOUT32(out, value);
721 }
722 }
723 else
724 {
725 while (out < end)
726 {
727 pixel = *(data++);
728 if (This->xwin.host_be)
729 {
730 BSWAP16(pixel);
731 }
732 SPLITCOLOUR15(pixel, pc);
733 value = MAKECOLOUR(pc);
734 LOUT32(out, value);
735 }
736 }
737 }
738
739 static void
translate16to16(RDPCLIENT * This,const uint16 * data,uint8 * out,uint8 * end)740 translate16to16(RDPCLIENT * This, const uint16 * data, uint8 * out, uint8 * end)
741 {
742 uint16 pixel;
743 uint16 value;
744 PixelColour pc;
745
746 if (This->xwin.xserver_be)
747 {
748 if (This->xwin.host_be)
749 {
750 while (out < end)
751 {
752 pixel = *(data++);
753 BSWAP16(pixel);
754 SPLITCOLOUR16(pixel, pc);
755 value = MAKECOLOUR(pc);
756 BOUT16(out, value);
757 }
758 }
759 else
760 {
761 while (out < end)
762 {
763 pixel = *(data++);
764 SPLITCOLOUR16(pixel, pc);
765 value = MAKECOLOUR(pc);
766 BOUT16(out, value);
767 }
768 }
769 }
770 else
771 {
772 if (This->xwin.host_be)
773 {
774 while (out < end)
775 {
776 pixel = *(data++);
777 BSWAP16(pixel);
778 SPLITCOLOUR16(pixel, pc);
779 value = MAKECOLOUR(pc);
780 LOUT16(out, value);
781 }
782 }
783 else
784 {
785 while (out < end)
786 {
787 pixel = *(data++);
788 SPLITCOLOUR16(pixel, pc);
789 value = MAKECOLOUR(pc);
790 LOUT16(out, value);
791 }
792 }
793 }
794 }
795
796 static void
translate16to24(RDPCLIENT * This,const uint16 * data,uint8 * out,uint8 * end)797 translate16to24(RDPCLIENT * This, const uint16 * data, uint8 * out, uint8 * end)
798 {
799 uint32 value;
800 uint16 pixel;
801 PixelColour pc;
802
803 if (This->xwin.compatible_arch)
804 {
805 /* *INDENT-OFF* */
806 REPEAT3
807 (
808 pixel = *(data++);
809 SPLITCOLOUR16(pixel, pc);
810 *(out++) = pc.blue;
811 *(out++) = pc.green;
812 *(out++) = pc.red;
813 )
814 /* *INDENT-ON* */
815 }
816 else if (This->xwin.xserver_be)
817 {
818 if (This->xwin.host_be)
819 {
820 while (out < end)
821 {
822 pixel = *(data++);
823 BSWAP16(pixel);
824 SPLITCOLOUR16(pixel, pc);
825 value = MAKECOLOUR(pc);
826 BOUT24(out, value);
827 }
828 }
829 else
830 {
831 while (out < end)
832 {
833 pixel = *(data++);
834 SPLITCOLOUR16(pixel, pc);
835 value = MAKECOLOUR(pc);
836 BOUT24(out, value);
837 }
838 }
839 }
840 else
841 {
842 if (This->xwin.host_be)
843 {
844 while (out < end)
845 {
846 pixel = *(data++);
847 BSWAP16(pixel);
848 SPLITCOLOUR16(pixel, pc);
849 value = MAKECOLOUR(pc);
850 LOUT24(out, value);
851 }
852 }
853 else
854 {
855 while (out < end)
856 {
857 pixel = *(data++);
858 SPLITCOLOUR16(pixel, pc);
859 value = MAKECOLOUR(pc);
860 LOUT24(out, value);
861 }
862 }
863 }
864 }
865
866 static void
translate16to32(RDPCLIENT * This,const uint16 * data,uint8 * out,uint8 * end)867 translate16to32(RDPCLIENT * This, const uint16 * data, uint8 * out, uint8 * end)
868 {
869 uint16 pixel;
870 uint32 value;
871 PixelColour pc;
872
873 if (This->xwin.compatible_arch)
874 {
875 /* *INDENT-OFF* */
876 REPEAT4
877 (
878 pixel = *(data++);
879 SPLITCOLOUR16(pixel, pc);
880 *(out++) = pc.blue;
881 *(out++) = pc.green;
882 *(out++) = pc.red;
883 *(out++) = 0;
884 )
885 /* *INDENT-ON* */
886 }
887 else if (This->xwin.xserver_be)
888 {
889 if (This->xwin.host_be)
890 {
891 while (out < end)
892 {
893 pixel = *(data++);
894 BSWAP16(pixel);
895 SPLITCOLOUR16(pixel, pc);
896 value = MAKECOLOUR(pc);
897 BOUT32(out, value);
898 }
899 }
900 else
901 {
902 while (out < end)
903 {
904 pixel = *(data++);
905 SPLITCOLOUR16(pixel, pc);
906 value = MAKECOLOUR(pc);
907 BOUT32(out, value);
908 }
909 }
910 }
911 else
912 {
913 if (This->xwin.host_be)
914 {
915 while (out < end)
916 {
917 pixel = *(data++);
918 BSWAP16(pixel);
919 SPLITCOLOUR16(pixel, pc);
920 value = MAKECOLOUR(pc);
921 LOUT32(out, value);
922 }
923 }
924 else
925 {
926 while (out < end)
927 {
928 pixel = *(data++);
929 SPLITCOLOUR16(pixel, pc);
930 value = MAKECOLOUR(pc);
931 LOUT32(out, value);
932 }
933 }
934 }
935 }
936
937 static void
translate24to16(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)938 translate24to16(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
939 {
940 uint32 pixel = 0;
941 uint16 value;
942 PixelColour pc;
943
944 while (out < end)
945 {
946 pixel = *(data++) << 16;
947 pixel |= *(data++) << 8;
948 pixel |= *(data++);
949 SPLITCOLOUR24(pixel, pc);
950 value = MAKECOLOUR(pc);
951 if (This->xwin.xserver_be)
952 {
953 BOUT16(out, value);
954 }
955 else
956 {
957 LOUT16(out, value);
958 }
959 }
960 }
961
962 static void
translate24to24(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)963 translate24to24(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
964 {
965 uint32 pixel;
966 uint32 value;
967 PixelColour pc;
968
969 if (This->xwin.xserver_be)
970 {
971 while (out < end)
972 {
973 pixel = *(data++) << 16;
974 pixel |= *(data++) << 8;
975 pixel |= *(data++);
976 SPLITCOLOUR24(pixel, pc);
977 value = MAKECOLOUR(pc);
978 BOUT24(out, value);
979 }
980 }
981 else
982 {
983 while (out < end)
984 {
985 pixel = *(data++) << 16;
986 pixel |= *(data++) << 8;
987 pixel |= *(data++);
988 SPLITCOLOUR24(pixel, pc);
989 value = MAKECOLOUR(pc);
990 LOUT24(out, value);
991 }
992 }
993 }
994
995 static void
translate24to32(RDPCLIENT * This,const uint8 * data,uint8 * out,uint8 * end)996 translate24to32(RDPCLIENT * This, const uint8 * data, uint8 * out, uint8 * end)
997 {
998 uint32 pixel;
999 uint32 value;
1000 PixelColour pc;
1001
1002 if (This->xwin.compatible_arch)
1003 {
1004 /* *INDENT-OFF* */
1005 #ifdef NEED_ALIGN
1006 REPEAT4
1007 (
1008 *(out++) = *(data++);
1009 *(out++) = *(data++);
1010 *(out++) = *(data++);
1011 *(out++) = 0;
1012 )
1013 #else
1014 REPEAT4
1015 (
1016 /* Only read 3 bytes. Reading 4 bytes means reading beyond buffer. */
1017 *((uint32 *) out) = *((uint16 *) data) + (*((uint8 *) data + 2) << 16);
1018 out += 4;
1019 data += 3;
1020 )
1021 #endif
1022 /* *INDENT-ON* */
1023 }
1024 else if (This->xwin.xserver_be)
1025 {
1026 while (out < end)
1027 {
1028 pixel = *(data++) << 16;
1029 pixel |= *(data++) << 8;
1030 pixel |= *(data++);
1031 SPLITCOLOUR24(pixel, pc);
1032 value = MAKECOLOUR(pc);
1033 BOUT32(out, value);
1034 }
1035 }
1036 else
1037 {
1038 while (out < end)
1039 {
1040 pixel = *(data++) << 16;
1041 pixel |= *(data++) << 8;
1042 pixel |= *(data++);
1043 SPLITCOLOUR24(pixel, pc);
1044 value = MAKECOLOUR(pc);
1045 LOUT32(out, value);
1046 }
1047 }
1048 }
1049
1050 static uint8 *
translate_image(RDPCLIENT * This,int width,int height,uint8 * data)1051 translate_image(RDPCLIENT * This, int width, int height, uint8 * data)
1052 {
1053 int size;
1054 uint8 *out;
1055 uint8 *end;
1056
1057 /*
1058 If RDP depth and X Visual depths match,
1059 and arch(endian) matches, no need to translate:
1060 just return data.
1061 Note: select_visual should've already ensured g_no_translate
1062 is only set for compatible depths, but the RDP depth might've
1063 changed during connection negotiations.
1064 */
1065 if (This->xwin.no_translate_image)
1066 {
1067 if ((This->xwin.depth == 15 && This->server_depth == 15) ||
1068 (This->xwin.depth == 16 && This->server_depth == 16) ||
1069 (This->xwin.depth == 24 && This->server_depth == 24))
1070 return data;
1071 }
1072
1073 size = width * height * (This->xwin.bpp / 8);
1074 out = (uint8 *) xmalloc(size);
1075 end = out + size;
1076
1077 switch (This->server_depth)
1078 {
1079 case 24:
1080 switch (This->xwin.bpp)
1081 {
1082 case 32:
1083 translate24to32(This, data, out, end);
1084 break;
1085 case 24:
1086 translate24to24(This, data, out, end);
1087 break;
1088 case 16:
1089 translate24to16(This, data, out, end);
1090 break;
1091 }
1092 break;
1093 case 16:
1094 switch (This->xwin.bpp)
1095 {
1096 case 32:
1097 translate16to32(This, (uint16 *) data, out, end);
1098 break;
1099 case 24:
1100 translate16to24(This, (uint16 *) data, out, end);
1101 break;
1102 case 16:
1103 translate16to16(This, (uint16 *) data, out, end);
1104 break;
1105 }
1106 break;
1107 case 15:
1108 switch (This->xwin.bpp)
1109 {
1110 case 32:
1111 translate15to32(This, (uint16 *) data, out, end);
1112 break;
1113 case 24:
1114 translate15to24(This, (uint16 *) data, out, end);
1115 break;
1116 case 16:
1117 translate15to16(This, (uint16 *) data, out, end);
1118 break;
1119 }
1120 break;
1121 case 8:
1122 switch (This->xwin.bpp)
1123 {
1124 case 8:
1125 translate8to8(This, data, out, end);
1126 break;
1127 case 16:
1128 translate8to16(This, data, out, end);
1129 break;
1130 case 24:
1131 translate8to24(This, data, out, end);
1132 break;
1133 case 32:
1134 translate8to32(This, data, out, end);
1135 break;
1136 }
1137 break;
1138 }
1139 return out;
1140 }
1141
1142 BOOL
get_key_state(RDPCLIENT * This,unsigned int state,uint32 keysym)1143 get_key_state(RDPCLIENT * This, unsigned int state, uint32 keysym)
1144 {
1145 int modifierpos, key, keysymMask = 0;
1146 int offset;
1147
1148 KeyCode keycode = XKeysymToKeycode(This->display, keysym);
1149
1150 if (keycode == NoSymbol)
1151 return False;
1152
1153 for (modifierpos = 0; modifierpos < 8; modifierpos++)
1154 {
1155 offset = This->xwin.mod_map->max_keypermod * modifierpos;
1156
1157 for (key = 0; key < This->xwin.mod_map->max_keypermod; key++)
1158 {
1159 if (This->xwin.mod_map->modifiermap[offset + key] == keycode)
1160 keysymMask |= 1 << modifierpos;
1161 }
1162 }
1163
1164 return (state & keysymMask) ? True : False;
1165 }
1166
1167 static void
calculate_shifts(uint32 mask,int * shift_r,int * shift_l)1168 calculate_shifts(uint32 mask, int *shift_r, int *shift_l)
1169 {
1170 *shift_l = ffs(mask) - 1;
1171 mask >>= *shift_l;
1172 *shift_r = 8 - ffs(mask & ~(mask >> 1));
1173 }
1174
1175 /* Given a mask of a colour channel (e.g. XVisualInfo.red_mask),
1176 calculates the bits-per-pixel of this channel (a.k.a. colour weight).
1177 */
1178 static unsigned
calculate_mask_weight(uint32 mask)1179 calculate_mask_weight(uint32 mask)
1180 {
1181 unsigned weight = 0;
1182 do
1183 {
1184 weight += (mask & 1);
1185 }
1186 while (mask >>= 1);
1187 return weight;
1188 }
1189
1190 static BOOL
select_visual(RDPCLIENT * This)1191 select_visual(RDPCLIENT * This)
1192 {
1193 XPixmapFormatValues *pfm;
1194 int pixmap_formats_count, visuals_count;
1195 XVisualInfo *vmatches = NULL;
1196 XVisualInfo template;
1197 int i;
1198 unsigned red_weight, blue_weight, green_weight;
1199
1200 red_weight = blue_weight = green_weight = 0;
1201
1202 if (This->server_depth == -1)
1203 {
1204 This->server_depth = DisplayPlanes(This->display, DefaultScreen(This->display));
1205 }
1206
1207 pfm = XListPixmapFormats(This->display, &pixmap_formats_count);
1208 if (pfm == NULL)
1209 {
1210 error("Unable to get list of pixmap formats from display.\n");
1211 XCloseDisplay(This->display);
1212 return False;
1213 }
1214
1215 /* Search for best TrueColor visual */
1216 template.class = TrueColor;
1217 vmatches = XGetVisualInfo(This->display, VisualClassMask, &template, &visuals_count);
1218 This->xwin.visual = NULL;
1219 This->xwin.no_translate_image = False;
1220 This->xwin.compatible_arch = False;
1221 if (vmatches != NULL)
1222 {
1223 for (i = 0; i < visuals_count; ++i)
1224 {
1225 XVisualInfo *visual_info = &vmatches[i];
1226 BOOL can_translate_to_bpp = False;
1227 int j;
1228
1229 /* Try to find a no-translation visual that'll
1230 allow us to use RDP bitmaps directly as ZPixmaps. */
1231 if (!This->xwin.xserver_be && (((visual_info->depth == 15) &&
1232 /* R5G5B5 */
1233 (visual_info->red_mask == 0x7c00) &&
1234 (visual_info->green_mask == 0x3e0) &&
1235 (visual_info->blue_mask == 0x1f)) ||
1236 ((visual_info->depth == 16) &&
1237 /* R5G6B5 */
1238 (visual_info->red_mask == 0xf800) &&
1239 (visual_info->green_mask == 0x7e0) &&
1240 (visual_info->blue_mask == 0x1f)) ||
1241 ((visual_info->depth == 24) &&
1242 /* R8G8B8 */
1243 (visual_info->red_mask == 0xff0000) &&
1244 (visual_info->green_mask == 0xff00) &&
1245 (visual_info->blue_mask == 0xff))))
1246 {
1247 This->xwin.visual = visual_info->visual;
1248 This->xwin.depth = visual_info->depth;
1249 This->xwin.compatible_arch = !This->xwin.host_be;
1250 This->xwin.no_translate_image = (visual_info->depth == This->server_depth);
1251 if (This->xwin.no_translate_image)
1252 /* We found the best visual */
1253 break;
1254 }
1255 else
1256 {
1257 This->xwin.compatible_arch = False;
1258 }
1259
1260 if (visual_info->depth > 24)
1261 {
1262 /* Avoid 32-bit visuals and likes like the plague.
1263 They're either untested or proven to work bad
1264 (e.g. nvidia's Composite 32-bit visual).
1265 Most implementation offer a 24-bit visual anyway. */
1266 continue;
1267 }
1268
1269 /* Only care for visuals, for whose BPPs (not depths!)
1270 we have a translateXtoY function. */
1271 for (j = 0; j < pixmap_formats_count; ++j)
1272 {
1273 if (pfm[j].depth == visual_info->depth)
1274 {
1275 if ((pfm[j].bits_per_pixel == 16) ||
1276 (pfm[j].bits_per_pixel == 24) ||
1277 (pfm[j].bits_per_pixel == 32))
1278 {
1279 can_translate_to_bpp = True;
1280 }
1281 break;
1282 }
1283 }
1284
1285 /* Prefer formats which have the most colour depth.
1286 We're being truly aristocratic here, minding each
1287 weight on its own. */
1288 if (can_translate_to_bpp)
1289 {
1290 unsigned vis_red_weight =
1291 calculate_mask_weight(visual_info->red_mask);
1292 unsigned vis_green_weight =
1293 calculate_mask_weight(visual_info->green_mask);
1294 unsigned vis_blue_weight =
1295 calculate_mask_weight(visual_info->blue_mask);
1296 if ((vis_red_weight >= red_weight)
1297 && (vis_green_weight >= green_weight)
1298 && (vis_blue_weight >= blue_weight))
1299 {
1300 red_weight = vis_red_weight;
1301 green_weight = vis_green_weight;
1302 blue_weight = vis_blue_weight;
1303 This->xwin.visual = visual_info->visual;
1304 This->xwin.depth = visual_info->depth;
1305 }
1306 }
1307 }
1308 XFree(vmatches);
1309 }
1310
1311 if (This->xwin.visual != NULL)
1312 {
1313 This->owncolmap = False;
1314 calculate_shifts(This->xwin.visual->red_mask, &This->xwin.red_shift_r, &This->xwin.red_shift_l);
1315 calculate_shifts(This->xwin.visual->green_mask, &This->xwin.green_shift_r, &This->xwin.green_shift_l);
1316 calculate_shifts(This->xwin.visual->blue_mask, &This->xwin.blue_shift_r, &This->xwin.blue_shift_l);
1317 }
1318 else
1319 {
1320 template.class = PseudoColor;
1321 template.depth = 8;
1322 template.colormap_size = 256;
1323 vmatches =
1324 XGetVisualInfo(This->display,
1325 VisualClassMask | VisualDepthMask | VisualColormapSizeMask,
1326 &template, &visuals_count);
1327 if (vmatches == NULL)
1328 {
1329 error("No usable TrueColor or PseudoColor visuals on this display.\n");
1330 XCloseDisplay(This->display);
1331 XFree(pfm);
1332 return False;
1333 }
1334
1335 /* we use a colourmap, so the default visual should do */
1336 This->owncolmap = True;
1337 This->xwin.visual = vmatches[0].visual;
1338 This->xwin.depth = vmatches[0].depth;
1339 }
1340
1341 This->xwin.bpp = 0;
1342 for (i = 0; i < pixmap_formats_count; ++i)
1343 {
1344 XPixmapFormatValues *pf = &pfm[i];
1345 if (pf->depth == This->xwin.depth)
1346 {
1347 This->xwin.bpp = pf->bits_per_pixel;
1348
1349 if (This->xwin.no_translate_image)
1350 {
1351 switch (This->server_depth)
1352 {
1353 case 15:
1354 case 16:
1355 if (This->xwin.bpp != 16)
1356 This->xwin.no_translate_image = False;
1357 break;
1358 case 24:
1359 /* Yes, this will force image translation
1360 on most modern servers which use 32 bits
1361 for R8G8B8. */
1362 if (This->xwin.bpp != 24)
1363 This->xwin.no_translate_image = False;
1364 break;
1365 default:
1366 This->xwin.no_translate_image = False;
1367 break;
1368 }
1369 }
1370
1371 /* Pixmap formats list is a depth-to-bpp mapping --
1372 there's just a single entry for every depth,
1373 so we can safely break here */
1374 break;
1375 }
1376 }
1377 XFree(pfm);
1378 pfm = NULL;
1379 return True;
1380 }
1381
1382 /*
1383 static int
1384 error_handler(RDPCLIENT * This, Display * dpy, XErrorEvent * eev)
1385 {
1386 if ((eev->error_code == BadMatch) && (eev->request_code == X_ConfigureWindow))
1387 {
1388 fprintf(stderr, "Got \"BadMatch\" when trying to restack windows.\n");
1389 fprintf(stderr,
1390 "This is most likely caused by a broken window manager (commonly KWin).\n");
1391 return 0;
1392 }
1393
1394 return This->xwin.old_error_handler(dpy, eev);
1395 }
1396 */
1397
1398 BOOL
ui_init(RDPCLIENT * This)1399 ui_init(RDPCLIENT * This)
1400 {
1401 int screen_num;
1402
1403 This->display = XOpenDisplay(NULL);
1404 if (This->display == NULL)
1405 {
1406 error("Failed to open display: %s\n", XDisplayName(NULL));
1407 return False;
1408 }
1409
1410 {
1411 uint16 endianess_test = 1;
1412 This->xwin.host_be = !(BOOL) (*(uint8 *) (&endianess_test));
1413 }
1414
1415 /*This->xwin.old_error_handler = XSetErrorHandler(error_handler);*/
1416 This->xwin.xserver_be = (ImageByteOrder(This->display) == MSBFirst);
1417 screen_num = DefaultScreen(This->display);
1418 This->xwin.x_socket = ConnectionNumber(This->display);
1419 This->xwin.screen = ScreenOfDisplay(This->display, screen_num);
1420 This->xwin.depth = DefaultDepthOfScreen(This->xwin.screen);
1421
1422 if (!select_visual(This))
1423 return False;
1424
1425 if (This->xwin.no_translate_image)
1426 {
1427 DEBUG(("Performance optimization possible: avoiding image translation (colour depth conversion).\n"));
1428 }
1429
1430 if (This->server_depth > This->xwin.bpp)
1431 {
1432 warning("Remote desktop colour depth %d higher than display colour depth %d.\n",
1433 This->server_depth, This->xwin.bpp);
1434 }
1435
1436 DEBUG(("RDP depth: %d, display depth: %d, display bpp: %d, X server BE: %d, host BE: %d\n",
1437 This->server_depth, This->xwin.depth, This->xwin.bpp, This->xwin.xserver_be, This->xwin.host_be));
1438
1439 if (!This->owncolmap)
1440 {
1441 This->xwin.xcolmap =
1442 XCreateColormap(This->display, RootWindowOfScreen(This->xwin.screen), This->xwin.visual,
1443 AllocNone);
1444 if (This->xwin.depth <= 8)
1445 warning("Display colour depth is %d bit: you may want to use -C for a private colourmap.\n", This->xwin.depth);
1446 }
1447
1448 if ((!This->ownbackstore) && (DoesBackingStore(This->xwin.screen) != Always))
1449 {
1450 warning("External BackingStore not available. Using internal.\n");
1451 This->ownbackstore = True;
1452 }
1453
1454 /*
1455 * Determine desktop size
1456 */
1457 if (This->fullscreen)
1458 {
1459 This->width = WidthOfScreen(This->xwin.screen);
1460 This->height = HeightOfScreen(This->xwin.screen);
1461 This->xwin.using_full_workarea = True;
1462 }
1463 else if (This->width < 0)
1464 {
1465 /* Percent of screen */
1466 if (-This->width >= 100)
1467 This->xwin.using_full_workarea = True;
1468 This->height = HeightOfScreen(This->xwin.screen) * (-This->width) / 100;
1469 This->width = WidthOfScreen(This->xwin.screen) * (-This->width) / 100;
1470 }
1471 else if (This->width == 0)
1472 {
1473 /* Fetch geometry from _NET_WORKAREA */
1474 uint32 x, y, cx, cy;
1475 if (get_current_workarea(This, &x, &y, &cx, &cy) == 0)
1476 {
1477 This->width = cx;
1478 This->height = cy;
1479 This->xwin.using_full_workarea = True;
1480 }
1481 else
1482 {
1483 warning("Failed to get workarea: probably your window manager does not support extended hints\n");
1484 This->width = WidthOfScreen(This->xwin.screen);
1485 This->height = HeightOfScreen(This->xwin.screen);
1486 }
1487 }
1488
1489 /* make sure width is a multiple of 4 */
1490 This->width = (This->width + 3) & ~3;
1491
1492 This->xwin.mod_map = XGetModifierMapping(This->display);
1493
1494 xkeymap_init(This);
1495
1496 if (This->enable_compose)
1497 This->xwin.IM = XOpenIM(This->display, NULL, NULL, NULL);
1498
1499 xclip_init(This);
1500 ewmh_init(This);
1501 if (This->seamless_rdp)
1502 seamless_init(This);
1503
1504 DEBUG_RDP5(("server bpp %d client bpp %d depth %d\n", This->server_depth, This->xwin.bpp, This->xwin.depth));
1505
1506 return True;
1507 }
1508
1509 void
ui_deinit(RDPCLIENT * This)1510 ui_deinit(RDPCLIENT * This)
1511 {
1512 while (This->xwin.seamless_windows)
1513 {
1514 XDestroyWindow(This->display, This->xwin.seamless_windows->wnd);
1515 sw_remove_window(This, This->xwin.seamless_windows);
1516 }
1517
1518 xclip_deinit(This);
1519
1520 if (This->xwin.IM != NULL)
1521 XCloseIM(This->xwin.IM);
1522
1523 if (This->xwin.null_cursor != NULL)
1524 ui_destroy_cursor(This, This->xwin.null_cursor);
1525
1526 XFreeModifiermap(This->xwin.mod_map);
1527
1528 if (This->ownbackstore)
1529 XFreePixmap(This->display, This->xwin.backstore);
1530
1531 XFreeGC(This->display, This->xwin.gc);
1532 XCloseDisplay(This->display);
1533 This->display = NULL;
1534 }
1535
1536
1537 static void
get_window_attribs(RDPCLIENT * This,XSetWindowAttributes * attribs)1538 get_window_attribs(RDPCLIENT * This, XSetWindowAttributes * attribs)
1539 {
1540 attribs->background_pixel = BlackPixelOfScreen(This->xwin.screen);
1541 attribs->background_pixel = WhitePixelOfScreen(This->xwin.screen);
1542 attribs->border_pixel = WhitePixelOfScreen(This->xwin.screen);
1543 attribs->backing_store = This->ownbackstore ? NotUseful : Always;
1544 attribs->override_redirect = This->fullscreen;
1545 attribs->colormap = This->xwin.xcolmap;
1546 }
1547
1548 static void
get_input_mask(RDPCLIENT * This,long * input_mask)1549 get_input_mask(RDPCLIENT * This, long *input_mask)
1550 {
1551 *input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
1552 VisibilityChangeMask | FocusChangeMask | StructureNotifyMask;
1553
1554 if (This->sendmotion)
1555 *input_mask |= PointerMotionMask;
1556 if (This->ownbackstore)
1557 *input_mask |= ExposureMask;
1558 if (This->fullscreen || This->grab_keyboard)
1559 *input_mask |= EnterWindowMask;
1560 if (This->grab_keyboard)
1561 *input_mask |= LeaveWindowMask;
1562 }
1563
1564 BOOL
ui_create_window(RDPCLIENT * This)1565 ui_create_window(RDPCLIENT * This)
1566 {
1567 uint8 null_pointer_mask[1] = { 0x80 };
1568 uint8 null_pointer_data[24] = { 0x00 };
1569
1570 XSetWindowAttributes attribs;
1571 XClassHint *classhints;
1572 XSizeHints *sizehints;
1573 int wndwidth, wndheight;
1574 long input_mask, ic_input_mask;
1575 XEvent xevent;
1576
1577 wndwidth = This->fullscreen ? WidthOfScreen(This->xwin.screen) : This->width;
1578 wndheight = This->fullscreen ? HeightOfScreen(This->xwin.screen) : This->height;
1579
1580 /* Handle -x-y portion of geometry string */
1581 if (This->xpos < 0 || (This->xpos == 0 && (This->pos & 2)))
1582 This->xpos = WidthOfScreen(This->xwin.screen) + This->xpos - This->width;
1583 if (This->ypos < 0 || (This->ypos == 0 && (This->pos & 4)))
1584 This->ypos = HeightOfScreen(This->xwin.screen) + This->ypos - This->height;
1585
1586 get_window_attribs(This, &attribs);
1587
1588 This->wnd = XCreateWindow(This->display, RootWindowOfScreen(This->xwin.screen), This->xpos, This->ypos, wndwidth,
1589 wndheight, 0, This->xwin.depth, InputOutput, This->xwin.visual,
1590 CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
1591 CWBorderPixel, &attribs);
1592
1593 if (This->xwin.gc == NULL)
1594 {
1595 This->xwin.gc = XCreateGC(This->display, This->wnd, 0, NULL);
1596 ui_reset_clip(This);
1597 }
1598
1599 if (This->xwin.create_bitmap_gc == NULL)
1600 This->xwin.create_bitmap_gc = XCreateGC(This->display, This->wnd, 0, NULL);
1601
1602 if ((This->ownbackstore) && (This->xwin.backstore == 0))
1603 {
1604 This->xwin.backstore = XCreatePixmap(This->display, This->wnd, This->width, This->height, This->xwin.depth);
1605
1606 /* clear to prevent rubbish being exposed at startup */
1607 XSetForeground(This->display, This->xwin.gc, BlackPixelOfScreen(This->xwin.screen));
1608 XFillRectangle(This->display, This->xwin.backstore, This->xwin.gc, 0, 0, This->width, This->height);
1609 }
1610
1611 XStoreName(This->display, This->wnd, This->title);
1612
1613 if (This->hide_decorations)
1614 mwm_hide_decorations(This, This->wnd);
1615
1616 classhints = XAllocClassHint();
1617 if (classhints != NULL)
1618 {
1619 classhints->res_name = classhints->res_class = "rdesktop";
1620 XSetClassHint(This->display, This->wnd, classhints);
1621 XFree(classhints);
1622 }
1623
1624 sizehints = XAllocSizeHints();
1625 if (sizehints)
1626 {
1627 sizehints->flags = PMinSize | PMaxSize;
1628 if (This->pos)
1629 sizehints->flags |= PPosition;
1630 sizehints->min_width = sizehints->max_width = This->width;
1631 sizehints->min_height = sizehints->max_height = This->height;
1632 XSetWMNormalHints(This->display, This->wnd, sizehints);
1633 XFree(sizehints);
1634 }
1635
1636 if (This->embed_wnd)
1637 {
1638 XReparentWindow(This->display, This->wnd, (Window) This->embed_wnd, 0, 0);
1639 }
1640
1641 get_input_mask(This, &input_mask);
1642
1643 if (This->xwin.IM != NULL)
1644 {
1645 This->xwin.IC = XCreateIC(This->xwin.IM, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
1646 XNClientWindow, This->wnd, XNFocusWindow, This->wnd, NULL);
1647
1648 if ((This->xwin.IC != NULL)
1649 && (XGetICValues(This->xwin.IC, XNFilterEvents, &ic_input_mask, NULL) == NULL))
1650 input_mask |= ic_input_mask;
1651 }
1652
1653 XSelectInput(This->display, This->wnd, input_mask);
1654 XMapWindow(This->display, This->wnd);
1655
1656 /* wait for VisibilityNotify */
1657 do
1658 {
1659 XMaskEvent(This->display, VisibilityChangeMask, &xevent);
1660 }
1661 while (xevent.type != VisibilityNotify);
1662 This->Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
1663
1664 This->xwin.focused = False;
1665 This->xwin.mouse_in_wnd = False;
1666
1667 /* handle the WM_DELETE_WINDOW protocol */
1668 This->xwin.protocol_atom = XInternAtom(This->display, "WM_PROTOCOLS", True);
1669 This->xwin.kill_atom = XInternAtom(This->display, "WM_DELETE_WINDOW", True);
1670 XSetWMProtocols(This->display, This->wnd, &This->xwin.kill_atom, 1);
1671
1672 /* create invisible 1x1 cursor to be used as null cursor */
1673 if (This->xwin.null_cursor == NULL)
1674 This->xwin.null_cursor = ui_create_cursor(This, 0, 0, 1, 1, null_pointer_mask, null_pointer_data);
1675
1676 return True;
1677 }
1678
1679 void
ui_resize_window(RDPCLIENT * This)1680 ui_resize_window(RDPCLIENT * This)
1681 {
1682 XSizeHints *sizehints;
1683 Pixmap bs;
1684
1685 sizehints = XAllocSizeHints();
1686 if (sizehints)
1687 {
1688 sizehints->flags = PMinSize | PMaxSize;
1689 sizehints->min_width = sizehints->max_width = This->width;
1690 sizehints->min_height = sizehints->max_height = This->height;
1691 XSetWMNormalHints(This->display, This->wnd, sizehints);
1692 XFree(sizehints);
1693 }
1694
1695 if (!(This->fullscreen || This->embed_wnd))
1696 {
1697 XResizeWindow(This->display, This->wnd, This->width, This->height);
1698 }
1699
1700 /* create new backstore pixmap */
1701 if (This->xwin.backstore != 0)
1702 {
1703 bs = XCreatePixmap(This->display, This->wnd, This->width, This->height, This->xwin.depth);
1704 XSetForeground(This->display, This->xwin.gc, BlackPixelOfScreen(This->xwin.screen));
1705 XFillRectangle(This->display, bs, This->xwin.gc, 0, 0, This->width, This->height);
1706 XCopyArea(This->display, This->xwin.backstore, bs, This->xwin.gc, 0, 0, This->width, This->height, 0, 0);
1707 XFreePixmap(This->display, This->xwin.backstore);
1708 This->xwin.backstore = bs;
1709 }
1710 }
1711
1712 void
ui_destroy_window(RDPCLIENT * This)1713 ui_destroy_window(RDPCLIENT * This)
1714 {
1715 if (This->xwin.IC != NULL)
1716 XDestroyIC(This->xwin.IC);
1717
1718 XDestroyWindow(This->display, This->wnd);
1719 }
1720
1721 void
xwin_toggle_fullscreen(RDPCLIENT * This)1722 xwin_toggle_fullscreen(RDPCLIENT * This)
1723 {
1724 Pixmap contents = 0;
1725
1726 if (This->xwin.seamless_active)
1727 /* Turn off SeamlessRDP mode */
1728 ui_seamless_toggle(This);
1729
1730 if (!This->ownbackstore)
1731 {
1732 /* need to save contents of window */
1733 contents = XCreatePixmap(This->display, This->wnd, This->width, This->height, This->xwin.depth);
1734 XCopyArea(This->display, This->wnd, contents, This->xwin.gc, 0, 0, This->width, This->height, 0, 0);
1735 }
1736
1737 ui_destroy_window(This);
1738 This->fullscreen = !This->fullscreen;
1739 ui_create_window(This);
1740
1741 XDefineCursor(This->display, This->wnd, This->xwin.current_cursor);
1742
1743 if (!This->ownbackstore)
1744 {
1745 XCopyArea(This->display, contents, This->wnd, This->xwin.gc, 0, 0, This->width, This->height, 0, 0);
1746 XFreePixmap(This->display, contents);
1747 }
1748 }
1749
1750 static void
handle_button_event(RDPCLIENT * This,XEvent xevent,BOOL down)1751 handle_button_event(RDPCLIENT * This, XEvent xevent, BOOL down)
1752 {
1753 uint16 button, flags = 0;
1754 This->last_gesturetime = xevent.xbutton.time;
1755 button = xkeymap_translate_button(xevent.xbutton.button);
1756 if (button == 0)
1757 return;
1758
1759 if (down)
1760 flags = MOUSE_FLAG_DOWN;
1761
1762 /* Stop moving window when button is released, regardless of cursor position */
1763 if (This->xwin.moving_wnd && (xevent.type == ButtonRelease))
1764 This->xwin.moving_wnd = False;
1765
1766 /* If win_button_sizee is nonzero, enable single app mode */
1767 if (xevent.xbutton.y < This->win_button_size)
1768 {
1769 /* Check from right to left: */
1770 if (xevent.xbutton.x >= This->width - This->win_button_size)
1771 {
1772 /* The close button, continue */
1773 ;
1774 }
1775 else if (xevent.xbutton.x >= This->width - This->win_button_size * 2)
1776 {
1777 /* The maximize/restore button. Do not send to
1778 server. It might be a good idea to change the
1779 cursor or give some other visible indication
1780 that rdesktop inhibited this click */
1781 if (xevent.type == ButtonPress)
1782 return;
1783 }
1784 else if (xevent.xbutton.x >= This->width - This->win_button_size * 3)
1785 {
1786 /* The minimize button. Iconify window. */
1787 if (xevent.type == ButtonRelease)
1788 {
1789 /* Release the mouse button outside the minimize button, to prevent the
1790 actual minimazation to happen */
1791 rdp_send_input(This, time(NULL), RDP_INPUT_MOUSE, button, 1, 1);
1792 XIconifyWindow(This->display, This->wnd, DefaultScreen(This->display));
1793 return;
1794 }
1795 }
1796 else if (xevent.xbutton.x <= This->win_button_size)
1797 {
1798 /* The system menu. Ignore. */
1799 if (xevent.type == ButtonPress)
1800 return;
1801 }
1802 else
1803 {
1804 /* The title bar. */
1805 if (xevent.type == ButtonPress)
1806 {
1807 if (!This->fullscreen && This->hide_decorations && !This->xwin.using_full_workarea)
1808 {
1809 This->xwin.moving_wnd = True;
1810 This->xwin.move_x_offset = xevent.xbutton.x;
1811 This->xwin.move_y_offset = xevent.xbutton.y;
1812 }
1813 return;
1814 }
1815 }
1816 }
1817
1818 if (xevent.xmotion.window == This->wnd)
1819 {
1820 rdp_send_input(This, time(NULL), RDP_INPUT_MOUSE,
1821 flags | button, xevent.xbutton.x, xevent.xbutton.y);
1822 }
1823 else
1824 {
1825 /* SeamlessRDP */
1826 rdp_send_input(This, time(NULL), RDP_INPUT_MOUSE,
1827 flags | button, xevent.xbutton.x_root, xevent.xbutton.y_root);
1828 }
1829 }
1830
1831
1832 /* Process events in Xlib queue
1833 Returns 0 after user quit, 1 otherwise */
1834 static int
xwin_process_events(RDPCLIENT * This)1835 xwin_process_events(RDPCLIENT * This)
1836 {
1837 XEvent xevent;
1838 KeySym keysym;
1839 uint32 ev_time;
1840 char str[256];
1841 Status status;
1842 int events = 0;
1843 seamless_window *sw;
1844
1845 while ((XPending(This->display) > 0) && events++ < 20)
1846 {
1847 XNextEvent(This->display, &xevent);
1848
1849 if ((This->xwin.IC != NULL) && (XFilterEvent(&xevent, None) == True))
1850 {
1851 DEBUG_KBD(("Filtering event\n"));
1852 continue;
1853 }
1854
1855 switch (xevent.type)
1856 {
1857 case VisibilityNotify:
1858 if (xevent.xvisibility.window == This->wnd)
1859 This->Unobscured =
1860 xevent.xvisibility.state == VisibilityUnobscured;
1861
1862 break;
1863 case ClientMessage:
1864 /* the window manager told us to quit */
1865 if ((xevent.xclient.message_type == This->xwin.protocol_atom)
1866 && ((Atom) xevent.xclient.data.l[0] == This->xwin.kill_atom))
1867 /* Quit */
1868 return 0;
1869 break;
1870
1871 case KeyPress:
1872 This->last_gesturetime = xevent.xkey.time;
1873 if (This->xwin.IC != NULL)
1874 /* Multi_key compatible version */
1875 {
1876 XmbLookupString(This->xwin.IC,
1877 &xevent.xkey, str, sizeof(str), &keysym,
1878 &status);
1879 if (!((status == XLookupKeySym) || (status == XLookupBoth)))
1880 {
1881 error("XmbLookupString failed with status 0x%x\n",
1882 status);
1883 break;
1884 }
1885 }
1886 else
1887 {
1888 /* Plain old XLookupString */
1889 DEBUG_KBD(("\nNo input context, using XLookupString\n"));
1890 XLookupString((XKeyEvent *) & xevent,
1891 str, sizeof(str), &keysym, NULL);
1892 }
1893
1894 DEBUG_KBD(("KeyPress for keysym (0x%lx, %s)\n", keysym,
1895 get_ksname(keysym)));
1896
1897 ev_time = time(NULL);
1898 if (handle_special_keys(This, keysym, xevent.xkey.state, ev_time, True))
1899 break;
1900
1901 xkeymap_send_keys(This, keysym, xevent.xkey.keycode, xevent.xkey.state,
1902 ev_time, True, 0);
1903 break;
1904
1905 case KeyRelease:
1906 This->last_gesturetime = xevent.xkey.time;
1907 XLookupString((XKeyEvent *) & xevent, str,
1908 sizeof(str), &keysym, NULL);
1909
1910 DEBUG_KBD(("\nKeyRelease for keysym (0x%lx, %s)\n", keysym,
1911 get_ksname(keysym)));
1912
1913 ev_time = time(NULL);
1914 if (handle_special_keys(This, keysym, xevent.xkey.state, ev_time, False))
1915 break;
1916
1917 xkeymap_send_keys(This, keysym, xevent.xkey.keycode, xevent.xkey.state,
1918 ev_time, False, 0);
1919 break;
1920
1921 case ButtonPress:
1922 handle_button_event(This, xevent, True);
1923 break;
1924
1925 case ButtonRelease:
1926 handle_button_event(This, xevent, False);
1927 break;
1928
1929 case MotionNotify:
1930 if (This->xwin.moving_wnd)
1931 {
1932 XMoveWindow(This->display, This->wnd,
1933 xevent.xmotion.x_root - This->xwin.move_x_offset,
1934 xevent.xmotion.y_root - This->xwin.move_y_offset);
1935 break;
1936 }
1937
1938 if (This->fullscreen && !This->xwin.focused)
1939 XSetInputFocus(This->display, This->wnd, RevertToPointerRoot,
1940 CurrentTime);
1941
1942 if (xevent.xmotion.window == This->wnd)
1943 {
1944 rdp_send_input(This, time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
1945 xevent.xmotion.x, xevent.xmotion.y);
1946 }
1947 else
1948 {
1949 /* SeamlessRDP */
1950 rdp_send_input(This, time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
1951 xevent.xmotion.x_root,
1952 xevent.xmotion.y_root);
1953 }
1954 break;
1955
1956 case FocusIn:
1957 if (xevent.xfocus.mode == NotifyGrab)
1958 break;
1959 This->xwin.focused = True;
1960 reset_modifier_keys(This);
1961 if (This->grab_keyboard && This->xwin.mouse_in_wnd)
1962 XGrabKeyboard(This->display, This->wnd, True,
1963 GrabModeAsync, GrabModeAsync, CurrentTime);
1964
1965 sw = sw_get_window_by_wnd(This, xevent.xfocus.window);
1966 if (!sw)
1967 break;
1968
1969 if (sw->id != This->xwin.seamless_focused)
1970 {
1971 seamless_send_focus(This, sw->id, 0);
1972 This->xwin.seamless_focused = sw->id;
1973 }
1974 break;
1975
1976 case FocusOut:
1977 if (xevent.xfocus.mode == NotifyUngrab)
1978 break;
1979 This->xwin.focused = False;
1980 if (xevent.xfocus.mode == NotifyWhileGrabbed)
1981 XUngrabKeyboard(This->display, CurrentTime);
1982 break;
1983
1984 case EnterNotify:
1985 /* we only register for this event when in fullscreen mode */
1986 /* or grab_keyboard */
1987 This->xwin.mouse_in_wnd = True;
1988 if (This->fullscreen)
1989 {
1990 XSetInputFocus(This->display, This->wnd, RevertToPointerRoot,
1991 CurrentTime);
1992 break;
1993 }
1994 if (This->xwin.focused)
1995 XGrabKeyboard(This->display, This->wnd, True,
1996 GrabModeAsync, GrabModeAsync, CurrentTime);
1997 break;
1998
1999 case LeaveNotify:
2000 /* we only register for this event when grab_keyboard */
2001 This->xwin.mouse_in_wnd = False;
2002 XUngrabKeyboard(This->display, CurrentTime);
2003 break;
2004
2005 case Expose:
2006 if (xevent.xexpose.window == This->wnd)
2007 {
2008 XCopyArea(This->display, This->xwin.backstore, xevent.xexpose.window,
2009 This->xwin.gc,
2010 xevent.xexpose.x, xevent.xexpose.y,
2011 xevent.xexpose.width, xevent.xexpose.height,
2012 xevent.xexpose.x, xevent.xexpose.y);
2013 }
2014 else
2015 {
2016 sw = sw_get_window_by_wnd(This, xevent.xexpose.window);
2017 if (!sw)
2018 break;
2019 XCopyArea(This->display, This->xwin.backstore,
2020 xevent.xexpose.window, This->xwin.gc,
2021 xevent.xexpose.x + sw->xoffset,
2022 xevent.xexpose.y + sw->yoffset,
2023 xevent.xexpose.width,
2024 xevent.xexpose.height, xevent.xexpose.x,
2025 xevent.xexpose.y);
2026 }
2027
2028 break;
2029
2030 case MappingNotify:
2031 /* Refresh keyboard mapping if it has changed. This is important for
2032 Xvnc, since it allocates keycodes dynamically */
2033 if (xevent.xmapping.request == MappingKeyboard
2034 || xevent.xmapping.request == MappingModifier)
2035 XRefreshKeyboardMapping(&xevent.xmapping);
2036
2037 if (xevent.xmapping.request == MappingModifier)
2038 {
2039 XFreeModifiermap(This->xwin.mod_map);
2040 This->xwin.mod_map = XGetModifierMapping(This->display);
2041 }
2042 break;
2043
2044 /* clipboard stuff */
2045 case SelectionNotify:
2046 xclip_handle_SelectionNotify(This, &xevent.xselection);
2047 break;
2048 case SelectionRequest:
2049 xclip_handle_SelectionRequest(This, &xevent.xselectionrequest);
2050 break;
2051 case SelectionClear:
2052 xclip_handle_SelectionClear(This);
2053 break;
2054 case PropertyNotify:
2055 xclip_handle_PropertyNotify(This, &xevent.xproperty);
2056 if (xevent.xproperty.window == This->wnd)
2057 break;
2058 if (xevent.xproperty.window == DefaultRootWindow(This->display))
2059 break;
2060
2061 /* seamless */
2062 sw = sw_get_window_by_wnd(This, xevent.xproperty.window);
2063 if (!sw)
2064 break;
2065
2066 if ((xevent.xproperty.atom == This->net_wm_state_atom)
2067 && (xevent.xproperty.state == PropertyNewValue))
2068 {
2069 sw->state = ewmh_get_window_state(This, sw->wnd);
2070 seamless_send_state(This, sw->id, sw->state, 0);
2071 }
2072
2073 if ((xevent.xproperty.atom == This->net_wm_desktop_atom)
2074 && (xevent.xproperty.state == PropertyNewValue))
2075 {
2076 sw->desktop = ewmh_get_window_desktop(This, sw->wnd);
2077 sw_all_to_desktop(This, sw->wnd, sw->desktop);
2078 }
2079
2080 break;
2081 case MapNotify:
2082 if (!This->xwin.seamless_active)
2083 rdp_send_client_window_status(This, 1);
2084 break;
2085 case UnmapNotify:
2086 if (!This->xwin.seamless_active)
2087 rdp_send_client_window_status(This, 0);
2088 break;
2089 case ConfigureNotify:
2090 if (!This->xwin.seamless_active)
2091 break;
2092
2093 sw = sw_get_window_by_wnd(This, xevent.xconfigure.window);
2094 if (!sw)
2095 break;
2096
2097 gettimeofday(sw->position_timer, NULL);
2098 if (sw->position_timer->tv_usec + SEAMLESSRDP_POSITION_TIMER >=
2099 1000000)
2100 {
2101 sw->position_timer->tv_usec +=
2102 SEAMLESSRDP_POSITION_TIMER - 1000000;
2103 sw->position_timer->tv_sec += 1;
2104 }
2105 else
2106 {
2107 sw->position_timer->tv_usec += SEAMLESSRDP_POSITION_TIMER;
2108 }
2109
2110 sw_handle_restack(This, sw);
2111 break;
2112 }
2113 }
2114 /* Keep going */
2115 return 1;
2116 }
2117
2118 /* Returns 0 after user quit, 1 otherwise */
2119 int
ui_select(RDPCLIENT * This,int rdp_socket)2120 ui_select(RDPCLIENT * This, int rdp_socket)
2121 {
2122 int n;
2123 fd_set rfds, wfds;
2124 struct timeval tv;
2125 BOOL s_timeout = False;
2126
2127 while (True)
2128 {
2129 n = (rdp_socket > This->xwin.x_socket) ? rdp_socket : This->xwin.x_socket;
2130 /* Process any events already waiting */
2131 if (!xwin_process_events(This))
2132 /* User quit */
2133 return 0;
2134
2135 if (This->xwin.seamless_active)
2136 sw_check_timers(This);
2137
2138 FD_ZERO(&rfds);
2139 FD_ZERO(&wfds);
2140 FD_SET(rdp_socket, &rfds);
2141 FD_SET(This->xwin.x_socket, &rfds);
2142
2143 #ifdef WITH_RDPSND
2144 /* FIXME: there should be an API for registering fds */
2145 if (This->dsp_busy)
2146 {
2147 FD_SET(This->dsp_fd, &wfds);
2148 n = (This->dsp_fd > n) ? This->dsp_fd : n;
2149 }
2150 #endif
2151 /* default timeout */
2152 tv.tv_sec = 60;
2153 tv.tv_usec = 0;
2154
2155 /* add redirection handles */
2156 rdpdr_add_fds(This, &n, &rfds, &wfds, &tv, &s_timeout);
2157 seamless_select_timeout(This, &tv);
2158
2159 n++;
2160
2161 switch (select(n, &rfds, &wfds, NULL, &tv))
2162 {
2163 case -1:
2164 error("select: %s\n", strerror(errno));
2165
2166 case 0:
2167 /* Abort serial read calls */
2168 if (s_timeout)
2169 rdpdr_check_fds(This, &rfds, &wfds, (BOOL) True);
2170 continue;
2171 }
2172
2173 rdpdr_check_fds(This, &rfds, &wfds, (BOOL) False);
2174
2175 if (FD_ISSET(rdp_socket, &rfds))
2176 return 1;
2177
2178 #ifdef WITH_RDPSND
2179 if (This->dsp_busy && FD_ISSET(This->dsp_fd, &wfds))
2180 wave_out_play();
2181 #endif
2182 }
2183 }
2184
2185 void
ui_move_pointer(RDPCLIENT * This,int x,int y)2186 ui_move_pointer(RDPCLIENT * This, int x, int y)
2187 {
2188 XWarpPointer(This->display, This->wnd, This->wnd, 0, 0, 0, 0, x, y);
2189 }
2190
2191 HBITMAP
ui_create_bitmap(RDPCLIENT * This,int width,int height,uint8 * data)2192 ui_create_bitmap(RDPCLIENT * This, int width, int height, uint8 * data)
2193 {
2194 XImage *image;
2195 Pixmap bitmap;
2196 uint8 *tdata;
2197 int bitmap_pad;
2198
2199 if (This->server_depth == 8)
2200 {
2201 bitmap_pad = 8;
2202 }
2203 else
2204 {
2205 bitmap_pad = This->xwin.bpp;
2206
2207 if (This->xwin.bpp == 24)
2208 bitmap_pad = 32;
2209 }
2210
2211 tdata = (This->owncolmap ? data : translate_image(This, width, height, data));
2212 bitmap = XCreatePixmap(This->display, This->wnd, width, height, This->xwin.depth);
2213 image = XCreateImage(This->display, This->xwin.visual, This->xwin.depth, ZPixmap, 0,
2214 (char *) tdata, width, height, bitmap_pad, 0);
2215
2216 XPutImage(This->display, bitmap, This->xwin.create_bitmap_gc, image, 0, 0, 0, 0, width, height);
2217
2218 XFree(image);
2219 if (tdata != data)
2220 xfree(tdata);
2221 return (HBITMAP) bitmap;
2222 }
2223
2224 void
ui_paint_bitmap(RDPCLIENT * This,int x,int y,int cx,int cy,int width,int height,uint8 * data)2225 ui_paint_bitmap(RDPCLIENT * This, int x, int y, int cx, int cy, int width, int height, uint8 * data)
2226 {
2227 XImage *image;
2228 uint8 *tdata;
2229 int bitmap_pad;
2230
2231 if (This->server_depth == 8)
2232 {
2233 bitmap_pad = 8;
2234 }
2235 else
2236 {
2237 bitmap_pad = This->xwin.bpp;
2238
2239 if (This->xwin.bpp == 24)
2240 bitmap_pad = 32;
2241 }
2242
2243 tdata = (This->owncolmap ? data : translate_image(This, width, height, data));
2244 image = XCreateImage(This->display, This->xwin.visual, This->xwin.depth, ZPixmap, 0,
2245 (char *) tdata, width, height, bitmap_pad, 0);
2246
2247 if (This->ownbackstore)
2248 {
2249 XPutImage(This->display, This->xwin.backstore, This->xwin.gc, image, 0, 0, x, y, cx, cy);
2250 XCopyArea(This->display, This->xwin.backstore, This->wnd, This->xwin.gc, x, y, cx, cy, x, y);
2251 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2252 (This->display, This->xwin.backstore, sw->wnd, This->xwin.gc, x, y, cx, cy,
2253 x - sw->xoffset, y - sw->yoffset));
2254 }
2255 else
2256 {
2257 XPutImage(This->display, This->wnd, This->xwin.gc, image, 0, 0, x, y, cx, cy);
2258 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2259 (This->display, This->wnd, sw->wnd, This->xwin.gc, x, y, cx, cy,
2260 x - sw->xoffset, y - sw->yoffset));
2261 }
2262
2263 XFree(image);
2264 if (tdata != data)
2265 xfree(tdata);
2266 }
2267
2268 void
ui_destroy_bitmap(RDPCLIENT * This,HBITMAP bmp)2269 ui_destroy_bitmap(RDPCLIENT * This, HBITMAP bmp)
2270 {
2271 XFreePixmap(This->display, (Pixmap) bmp);
2272 }
2273
2274 HGLYPH
ui_create_glyph(RDPCLIENT * This,int width,int height,const uint8 * data)2275 ui_create_glyph(RDPCLIENT * This, int width, int height, const uint8 * data)
2276 {
2277 XImage *image;
2278 Pixmap bitmap;
2279 int scanline;
2280
2281 scanline = (width + 7) / 8;
2282
2283 bitmap = XCreatePixmap(This->display, This->wnd, width, height, 1);
2284 if (This->xwin.create_glyph_gc == 0)
2285 This->xwin.create_glyph_gc = XCreateGC(This->display, bitmap, 0, NULL);
2286
2287 image = XCreateImage(This->display, This->xwin.visual, 1, ZPixmap, 0, (char *) data,
2288 width, height, 8, scanline);
2289 image->byte_order = MSBFirst;
2290 image->bitmap_bit_order = MSBFirst;
2291 XInitImage(image);
2292
2293 XPutImage(This->display, bitmap, This->xwin.create_glyph_gc, image, 0, 0, 0, 0, width, height);
2294
2295 XFree(image);
2296 return (HGLYPH) bitmap;
2297 }
2298
2299 void
ui_destroy_glyph(RDPCLIENT * This,HGLYPH glyph)2300 ui_destroy_glyph(RDPCLIENT * This, HGLYPH glyph)
2301 {
2302 XFreePixmap(This->display, (Pixmap) glyph);
2303 }
2304
2305 HCURSOR
ui_create_cursor(RDPCLIENT * This,unsigned int x,unsigned int y,int width,int height,uint8 * andmask,uint8 * xormask)2306 ui_create_cursor(RDPCLIENT * This, unsigned int x, unsigned int y, int width, int height,
2307 uint8 * andmask, uint8 * xormask)
2308 {
2309 HGLYPH maskglyph, cursorglyph;
2310 XColor bg, fg;
2311 Cursor xcursor;
2312 uint8 *cursor, *pcursor;
2313 uint8 *mask, *pmask;
2314 uint8 nextbit;
2315 int scanline, offset;
2316 int i, j;
2317
2318 scanline = (width + 7) / 8;
2319 offset = scanline * height;
2320
2321 cursor = (uint8 *) xmalloc(offset);
2322 memset(cursor, 0, offset);
2323
2324 mask = (uint8 *) xmalloc(offset);
2325 memset(mask, 0, offset);
2326
2327 /* approximate AND and XOR masks with a monochrome X pointer */
2328 for (i = 0; i < height; i++)
2329 {
2330 offset -= scanline;
2331 pcursor = &cursor[offset];
2332 pmask = &mask[offset];
2333
2334 for (j = 0; j < scanline; j++)
2335 {
2336 for (nextbit = 0x80; nextbit != 0; nextbit >>= 1)
2337 {
2338 if (xormask[0] || xormask[1] || xormask[2])
2339 {
2340 *pcursor |= (~(*andmask) & nextbit);
2341 *pmask |= nextbit;
2342 }
2343 else
2344 {
2345 *pcursor |= ((*andmask) & nextbit);
2346 *pmask |= (~(*andmask) & nextbit);
2347 }
2348
2349 xormask += 3;
2350 }
2351
2352 andmask++;
2353 pcursor++;
2354 pmask++;
2355 }
2356 }
2357
2358 fg.red = fg.blue = fg.green = 0xffff;
2359 bg.red = bg.blue = bg.green = 0x0000;
2360 fg.flags = bg.flags = DoRed | DoBlue | DoGreen;
2361
2362 cursorglyph = ui_create_glyph(This, width, height, cursor);
2363 maskglyph = ui_create_glyph(This, width, height, mask);
2364
2365 xcursor =
2366 XCreatePixmapCursor(This->display, (Pixmap) cursorglyph,
2367 (Pixmap) maskglyph, &fg, &bg, x, y);
2368
2369 ui_destroy_glyph(This, maskglyph);
2370 ui_destroy_glyph(This, cursorglyph);
2371 xfree(mask);
2372 xfree(cursor);
2373 return (HCURSOR) xcursor;
2374 }
2375
2376 void
ui_set_cursor(RDPCLIENT * This,HCURSOR cursor)2377 ui_set_cursor(RDPCLIENT * This, HCURSOR cursor)
2378 {
2379 This->xwin.current_cursor = (Cursor) cursor;
2380 XDefineCursor(This->display, This->wnd, This->xwin.current_cursor);
2381 ON_ALL_SEAMLESS_WINDOWS(XDefineCursor, (This->display, sw->wnd, This->xwin.current_cursor));
2382 }
2383
2384 void
ui_destroy_cursor(RDPCLIENT * This,HCURSOR cursor)2385 ui_destroy_cursor(RDPCLIENT * This, HCURSOR cursor)
2386 {
2387 XFreeCursor(This->display, (Cursor) cursor);
2388 }
2389
2390 void
ui_set_null_cursor(RDPCLIENT * This)2391 ui_set_null_cursor(RDPCLIENT * This)
2392 {
2393 ui_set_cursor(This, This->xwin.null_cursor);
2394 }
2395
2396 #define MAKE_XCOLOR(xc,c) \
2397 (xc)->red = ((c)->red << 8) | (c)->red; \
2398 (xc)->green = ((c)->green << 8) | (c)->green; \
2399 (xc)->blue = ((c)->blue << 8) | (c)->blue; \
2400 (xc)->flags = DoRed | DoGreen | DoBlue;
2401
2402
2403 HCOLOURMAP
ui_create_colourmap(RDPCLIENT * This,COLOURMAP * colours)2404 ui_create_colourmap(RDPCLIENT * This, COLOURMAP * colours)
2405 {
2406 COLOURENTRY *entry;
2407 int i, ncolours = colours->ncolours;
2408 if (!This->owncolmap)
2409 {
2410 uint32 *map = (uint32 *) xmalloc(sizeof(*This->xwin.colmap) * ncolours);
2411 XColor xentry;
2412 XColor xc_cache[256];
2413 uint32 colour;
2414 int colLookup = 256;
2415 for (i = 0; i < ncolours; i++)
2416 {
2417 entry = &colours->colours[i];
2418 MAKE_XCOLOR(&xentry, entry);
2419
2420 if (XAllocColor(This->display, This->xwin.xcolmap, &xentry) == 0)
2421 {
2422 /* Allocation failed, find closest match. */
2423 int j = 256;
2424 int nMinDist = 3 * 256 * 256;
2425 long nDist = nMinDist;
2426
2427 /* only get the colors once */
2428 while (colLookup--)
2429 {
2430 xc_cache[colLookup].pixel = colLookup;
2431 xc_cache[colLookup].red = xc_cache[colLookup].green =
2432 xc_cache[colLookup].blue = 0;
2433 xc_cache[colLookup].flags = 0;
2434 XQueryColor(This->display,
2435 DefaultColormap(This->display,
2436 DefaultScreen(This->display)),
2437 &xc_cache[colLookup]);
2438 }
2439 colLookup = 0;
2440
2441 /* approximate the pixel */
2442 while (j--)
2443 {
2444 if (xc_cache[j].flags)
2445 {
2446 nDist = ((long) (xc_cache[j].red >> 8) -
2447 (long) (xentry.red >> 8)) *
2448 ((long) (xc_cache[j].red >> 8) -
2449 (long) (xentry.red >> 8)) +
2450 ((long) (xc_cache[j].green >> 8) -
2451 (long) (xentry.green >> 8)) *
2452 ((long) (xc_cache[j].green >> 8) -
2453 (long) (xentry.green >> 8)) +
2454 ((long) (xc_cache[j].blue >> 8) -
2455 (long) (xentry.blue >> 8)) *
2456 ((long) (xc_cache[j].blue >> 8) -
2457 (long) (xentry.blue >> 8));
2458 }
2459 if (nDist < nMinDist)
2460 {
2461 nMinDist = nDist;
2462 xentry.pixel = j;
2463 }
2464 }
2465 }
2466 colour = xentry.pixel;
2467
2468 /* update our cache */
2469 if (xentry.pixel < 256)
2470 {
2471 xc_cache[xentry.pixel].red = xentry.red;
2472 xc_cache[xentry.pixel].green = xentry.green;
2473 xc_cache[xentry.pixel].blue = xentry.blue;
2474
2475 }
2476
2477 map[i] = colour;
2478 }
2479 return map;
2480 }
2481 else
2482 {
2483 XColor *xcolours, *xentry;
2484 Colormap map;
2485
2486 xcolours = (XColor *) xmalloc(sizeof(XColor) * ncolours);
2487 for (i = 0; i < ncolours; i++)
2488 {
2489 entry = &colours->colours[i];
2490 xentry = &xcolours[i];
2491 xentry->pixel = i;
2492 MAKE_XCOLOR(xentry, entry);
2493 }
2494
2495 map = XCreateColormap(This->display, This->wnd, This->xwin.visual, AllocAll);
2496 XStoreColors(This->display, map, xcolours, ncolours);
2497
2498 xfree(xcolours);
2499 return (HCOLOURMAP) map;
2500 }
2501 }
2502
2503 void
ui_destroy_colourmap(RDPCLIENT * This,HCOLOURMAP map)2504 ui_destroy_colourmap(RDPCLIENT * This, HCOLOURMAP map)
2505 {
2506 if (!This->owncolmap)
2507 xfree(map);
2508 else
2509 XFreeColormap(This->display, (Colormap) map);
2510 }
2511
2512 void
ui_set_colourmap(RDPCLIENT * This,HCOLOURMAP map)2513 ui_set_colourmap(RDPCLIENT * This, HCOLOURMAP map)
2514 {
2515 if (!This->owncolmap)
2516 {
2517 if (This->xwin.colmap)
2518 xfree(This->xwin.colmap);
2519
2520 This->xwin.colmap = (uint32 *) map;
2521 }
2522 else
2523 {
2524 XSetWindowColormap(This->display, This->wnd, (Colormap) map);
2525 ON_ALL_SEAMLESS_WINDOWS(XSetWindowColormap, (This->display, sw->wnd, (Colormap) map));
2526 }
2527 }
2528
2529 void
ui_set_clip(RDPCLIENT * This,int x,int y,int cx,int cy)2530 ui_set_clip(RDPCLIENT * This, int x, int y, int cx, int cy)
2531 {
2532 This->xwin.clip_rectangle.x = x;
2533 This->xwin.clip_rectangle.y = y;
2534 This->xwin.clip_rectangle.width = cx;
2535 This->xwin.clip_rectangle.height = cy;
2536 XSetClipRectangles(This->display, This->xwin.gc, 0, 0, &This->xwin.clip_rectangle, 1, YXBanded);
2537 }
2538
2539 void
ui_reset_clip(RDPCLIENT * This)2540 ui_reset_clip(RDPCLIENT * This)
2541 {
2542 This->xwin.clip_rectangle.x = 0;
2543 This->xwin.clip_rectangle.y = 0;
2544 This->xwin.clip_rectangle.width = This->width;
2545 This->xwin.clip_rectangle.height = This->height;
2546 XSetClipRectangles(This->display, This->xwin.gc, 0, 0, &This->xwin.clip_rectangle, 1, YXBanded);
2547 }
2548
2549 void
ui_bell(RDPCLIENT * This)2550 ui_bell(RDPCLIENT * This)
2551 {
2552 XBell(This->display, 0);
2553 }
2554
2555 void
ui_destblt(RDPCLIENT * This,uint8 opcode,int x,int y,int cx,int cy)2556 ui_destblt(RDPCLIENT * This, uint8 opcode,
2557 /* dest */ int x, int y, int cx, int cy)
2558 {
2559 SET_FUNCTION(opcode);
2560 FILL_RECTANGLE(x, y, cx, cy);
2561 RESET_FUNCTION(opcode);
2562 }
2563
2564 static const uint8 hatch_patterns[] = {
2565 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, /* 0 - bsHorizontal */
2566 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, /* 1 - bsVertical */
2567 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, /* 2 - bsFDiagonal */
2568 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /* 3 - bsBDiagonal */
2569 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08, /* 4 - bsCross */
2570 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 /* 5 - bsDiagCross */
2571 };
2572
2573 void
ui_patblt(RDPCLIENT * This,uint8 opcode,int x,int y,int cx,int cy,BRUSH * brush,int bgcolour,int fgcolour)2574 ui_patblt(RDPCLIENT * This, uint8 opcode,
2575 /* dest */ int x, int y, int cx, int cy,
2576 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2577 {
2578 Pixmap fill;
2579 uint8 i, ipattern[8];
2580
2581 SET_FUNCTION(opcode);
2582
2583 switch (brush->style)
2584 {
2585 case 0: /* Solid */
2586 SET_FOREGROUND(fgcolour);
2587 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2588 break;
2589
2590 case 2: /* Hatch */
2591 fill = (Pixmap) ui_create_glyph(This, 8, 8,
2592 hatch_patterns + brush->pattern[0] * 8);
2593 SET_FOREGROUND(fgcolour);
2594 SET_BACKGROUND(bgcolour);
2595 XSetFillStyle(This->display, This->xwin.gc, FillOpaqueStippled);
2596 XSetStipple(This->display, This->xwin.gc, fill);
2597 XSetTSOrigin(This->display, This->xwin.gc, brush->xorigin, brush->yorigin);
2598 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2599 XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2600 XSetTSOrigin(This->display, This->xwin.gc, 0, 0);
2601 ui_destroy_glyph(This, (HGLYPH) fill);
2602 break;
2603
2604 case 3: /* Pattern */
2605 for (i = 0; i != 8; i++)
2606 ipattern[7 - i] = brush->pattern[i];
2607 fill = (Pixmap) ui_create_glyph(This, 8, 8, ipattern);
2608 SET_FOREGROUND(bgcolour);
2609 SET_BACKGROUND(fgcolour);
2610 XSetFillStyle(This->display, This->xwin.gc, FillOpaqueStippled);
2611 XSetStipple(This->display, This->xwin.gc, fill);
2612 XSetTSOrigin(This->display, This->xwin.gc, brush->xorigin, brush->yorigin);
2613 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2614 XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2615 XSetTSOrigin(This->display, This->xwin.gc, 0, 0);
2616 ui_destroy_glyph(This, (HGLYPH) fill);
2617 break;
2618
2619 default:
2620 unimpl("brush %d\n", brush->style);
2621 }
2622
2623 RESET_FUNCTION(opcode);
2624
2625 if (This->ownbackstore)
2626 XCopyArea(This->display, This->xwin.backstore, This->wnd, This->xwin.gc, x, y, cx, cy, x, y);
2627 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2628 (This->display, This->ownbackstore ? This->xwin.backstore : This->wnd, sw->wnd, This->xwin.gc,
2629 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2630 }
2631
2632 void
ui_screenblt(RDPCLIENT * This,uint8 opcode,int x,int y,int cx,int cy,int srcx,int srcy)2633 ui_screenblt(RDPCLIENT * This, uint8 opcode,
2634 /* dest */ int x, int y, int cx, int cy,
2635 /* src */ int srcx, int srcy)
2636 {
2637 SET_FUNCTION(opcode);
2638 if (This->ownbackstore)
2639 {
2640 XCopyArea(This->display, This->Unobscured ? This->wnd : This->xwin.backstore,
2641 This->wnd, This->xwin.gc, srcx, srcy, cx, cy, x, y);
2642 XCopyArea(This->display, This->xwin.backstore, This->xwin.backstore, This->xwin.gc, srcx, srcy, cx, cy, x, y);
2643 }
2644 else
2645 {
2646 XCopyArea(This->display, This->wnd, This->wnd, This->xwin.gc, srcx, srcy, cx, cy, x, y);
2647 }
2648
2649 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2650 (This->display, This->ownbackstore ? This->xwin.backstore : This->wnd,
2651 sw->wnd, This->xwin.gc, x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2652
2653 RESET_FUNCTION(opcode);
2654 }
2655
2656 void
ui_memblt(RDPCLIENT * This,uint8 opcode,int x,int y,int cx,int cy,HBITMAP src,int srcx,int srcy)2657 ui_memblt(RDPCLIENT * This, uint8 opcode,
2658 /* dest */ int x, int y, int cx, int cy,
2659 /* src */ HBITMAP src, int srcx, int srcy)
2660 {
2661 SET_FUNCTION(opcode);
2662 XCopyArea(This->display, (Pixmap) src, This->wnd, This->xwin.gc, srcx, srcy, cx, cy, x, y);
2663 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2664 (This->display, (Pixmap) src, sw->wnd, This->xwin.gc,
2665 srcx, srcy, cx, cy, x - sw->xoffset, y - sw->yoffset));
2666 if (This->ownbackstore)
2667 XCopyArea(This->display, (Pixmap) src, This->xwin.backstore, This->xwin.gc, srcx, srcy, cx, cy, x, y);
2668 RESET_FUNCTION(opcode);
2669 }
2670
2671 void
ui_triblt(RDPCLIENT * This,uint8 opcode,int x,int y,int cx,int cy,HBITMAP src,int srcx,int srcy,BRUSH * brush,int bgcolour,int fgcolour)2672 ui_triblt(RDPCLIENT * This, uint8 opcode,
2673 /* dest */ int x, int y, int cx, int cy,
2674 /* src */ HBITMAP src, int srcx, int srcy,
2675 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2676 {
2677 /* This is potentially difficult to do in general. Until someone
2678 comes up with a more efficient way of doing it I am using cases. */
2679
2680 switch (opcode)
2681 {
2682 case 0x69: /* PDSxxn */
2683 ui_memblt(This, ROP2_XOR, x, y, cx, cy, src, srcx, srcy);
2684 ui_patblt(This, ROP2_NXOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2685 break;
2686
2687 case 0xb8: /* PSDPxax */
2688 ui_patblt(This, ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2689 ui_memblt(This, ROP2_AND, x, y, cx, cy, src, srcx, srcy);
2690 ui_patblt(This, ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2691 break;
2692
2693 case 0xc0: /* PSa */
2694 ui_memblt(This, ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2695 ui_patblt(This, ROP2_AND, x, y, cx, cy, brush, bgcolour, fgcolour);
2696 break;
2697
2698 default:
2699 unimpl("triblt 0x%x\n", opcode);
2700 ui_memblt(This, ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2701 }
2702 }
2703
2704 void
ui_line(RDPCLIENT * This,uint8 opcode,int startx,int starty,int endx,int endy,PEN * pen)2705 ui_line(RDPCLIENT * This, uint8 opcode,
2706 /* dest */ int startx, int starty, int endx, int endy,
2707 /* pen */ PEN * pen)
2708 {
2709 SET_FUNCTION(opcode);
2710 SET_FOREGROUND(pen->colour);
2711 XDrawLine(This->display, This->wnd, This->xwin.gc, startx, starty, endx, endy);
2712 ON_ALL_SEAMLESS_WINDOWS(XDrawLine, (This->display, sw->wnd, This->xwin.gc,
2713 startx - sw->xoffset, starty - sw->yoffset,
2714 endx - sw->xoffset, endy - sw->yoffset));
2715 if (This->ownbackstore)
2716 XDrawLine(This->display, This->xwin.backstore, This->xwin.gc, startx, starty, endx, endy);
2717 RESET_FUNCTION(opcode);
2718 }
2719
2720 void
ui_rect(RDPCLIENT * This,int x,int y,int cx,int cy,int colour)2721 ui_rect(RDPCLIENT * This,
2722 /* dest */ int x, int y, int cx, int cy,
2723 /* brush */ int colour)
2724 {
2725 SET_FOREGROUND(colour);
2726 FILL_RECTANGLE(x, y, cx, cy);
2727 }
2728
2729 void
ui_polygon(RDPCLIENT * This,uint8 opcode,uint8 fillmode,POINT * point,int npoints,BRUSH * brush,int bgcolour,int fgcolour)2730 ui_polygon(RDPCLIENT * This, uint8 opcode,
2731 /* mode */ uint8 fillmode,
2732 /* dest */ POINT * point, int npoints,
2733 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2734 {
2735 uint8 style, i, ipattern[8];
2736 Pixmap fill;
2737
2738 SET_FUNCTION(opcode);
2739
2740 switch (fillmode)
2741 {
2742 case ALTERNATE:
2743 XSetFillRule(This->display, This->xwin.gc, EvenOddRule);
2744 break;
2745 case WINDING:
2746 XSetFillRule(This->display, This->xwin.gc, WindingRule);
2747 break;
2748 default:
2749 unimpl("fill mode %d\n", fillmode);
2750 }
2751
2752 if (brush)
2753 style = brush->style;
2754 else
2755 style = 0;
2756
2757 switch (style)
2758 {
2759 case 0: /* Solid */
2760 SET_FOREGROUND(fgcolour);
2761 FILL_POLYGON((XPoint *) point, npoints);
2762 break;
2763
2764 case 2: /* Hatch */
2765 fill = (Pixmap) ui_create_glyph(This, 8, 8,
2766 hatch_patterns + brush->pattern[0] * 8);
2767 SET_FOREGROUND(fgcolour);
2768 SET_BACKGROUND(bgcolour);
2769 XSetFillStyle(This->display, This->xwin.gc, FillOpaqueStippled);
2770 XSetStipple(This->display, This->xwin.gc, fill);
2771 XSetTSOrigin(This->display, This->xwin.gc, brush->xorigin, brush->yorigin);
2772 FILL_POLYGON((XPoint *) point, npoints);
2773 XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2774 XSetTSOrigin(This->display, This->xwin.gc, 0, 0);
2775 ui_destroy_glyph(This, (HGLYPH) fill);
2776 break;
2777
2778 case 3: /* Pattern */
2779 for (i = 0; i != 8; i++)
2780 ipattern[7 - i] = brush->pattern[i];
2781 fill = (Pixmap) ui_create_glyph(This, 8, 8, ipattern);
2782 SET_FOREGROUND(bgcolour);
2783 SET_BACKGROUND(fgcolour);
2784 XSetFillStyle(This->display, This->xwin.gc, FillOpaqueStippled);
2785 XSetStipple(This->display, This->xwin.gc, fill);
2786 XSetTSOrigin(This->display, This->xwin.gc, brush->xorigin, brush->yorigin);
2787 FILL_POLYGON((XPoint *) point, npoints);
2788 XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2789 XSetTSOrigin(This->display, This->xwin.gc, 0, 0);
2790 ui_destroy_glyph(This, (HGLYPH) fill);
2791 break;
2792
2793 default:
2794 unimpl("brush %d\n", brush->style);
2795 }
2796
2797 RESET_FUNCTION(opcode);
2798 }
2799
2800 void
ui_polyline(RDPCLIENT * This,uint8 opcode,POINT * points,int npoints,PEN * pen)2801 ui_polyline(RDPCLIENT * This, uint8 opcode,
2802 /* dest */ POINT * points, int npoints,
2803 /* pen */ PEN * pen)
2804 {
2805 /* TODO: set join style */
2806 SET_FUNCTION(opcode);
2807 SET_FOREGROUND(pen->colour);
2808 XDrawLines(This->display, This->wnd, This->xwin.gc, (XPoint *) points, npoints, CoordModePrevious);
2809 if (This->ownbackstore)
2810 XDrawLines(This->display, This->xwin.backstore, This->xwin.gc, (XPoint *) points, npoints,
2811 CoordModePrevious);
2812
2813 ON_ALL_SEAMLESS_WINDOWS(seamless_XDrawLines,
2814 (This, sw->wnd, (XPoint *) points, npoints, sw->xoffset, sw->yoffset));
2815
2816 RESET_FUNCTION(opcode);
2817 }
2818
2819 void
ui_ellipse(RDPCLIENT * This,uint8 opcode,uint8 fillmode,int x,int y,int cx,int cy,BRUSH * brush,int bgcolour,int fgcolour)2820 ui_ellipse(RDPCLIENT * This, uint8 opcode,
2821 /* mode */ uint8 fillmode,
2822 /* dest */ int x, int y, int cx, int cy,
2823 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2824 {
2825 uint8 style, i, ipattern[8];
2826 Pixmap fill;
2827
2828 SET_FUNCTION(opcode);
2829
2830 if (brush)
2831 style = brush->style;
2832 else
2833 style = 0;
2834
2835 switch (style)
2836 {
2837 case 0: /* Solid */
2838 SET_FOREGROUND(fgcolour);
2839 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2840 break;
2841
2842 case 2: /* Hatch */
2843 fill = (Pixmap) ui_create_glyph(This, 8, 8,
2844 hatch_patterns + brush->pattern[0] * 8);
2845 SET_FOREGROUND(fgcolour);
2846 SET_BACKGROUND(bgcolour);
2847 XSetFillStyle(This->display, This->xwin.gc, FillOpaqueStippled);
2848 XSetStipple(This->display, This->xwin.gc, fill);
2849 XSetTSOrigin(This->display, This->xwin.gc, brush->xorigin, brush->yorigin);
2850 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2851 XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2852 XSetTSOrigin(This->display, This->xwin.gc, 0, 0);
2853 ui_destroy_glyph(This, (HGLYPH) fill);
2854 break;
2855
2856 case 3: /* Pattern */
2857 for (i = 0; i != 8; i++)
2858 ipattern[7 - i] = brush->pattern[i];
2859 fill = (Pixmap) ui_create_glyph(This, 8, 8, ipattern);
2860 SET_FOREGROUND(bgcolour);
2861 SET_BACKGROUND(fgcolour);
2862 XSetFillStyle(This->display, This->xwin.gc, FillOpaqueStippled);
2863 XSetStipple(This->display, This->xwin.gc, fill);
2864 XSetTSOrigin(This->display, This->xwin.gc, brush->xorigin, brush->yorigin);
2865 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2866 XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2867 XSetTSOrigin(This->display, This->xwin.gc, 0, 0);
2868 ui_destroy_glyph(This, (HGLYPH) fill);
2869 break;
2870
2871 default:
2872 unimpl("brush %d\n", brush->style);
2873 }
2874
2875 RESET_FUNCTION(opcode);
2876 }
2877
2878 /* warning, this function only draws on wnd or backstore, not both */
2879 void
ui_draw_glyph(RDPCLIENT * This,int mixmode,int x,int y,int cx,int cy,HGLYPH glyph,int srcx,int srcy,int bgcolour,int fgcolour)2880 ui_draw_glyph(RDPCLIENT * This, int mixmode,
2881 /* dest */ int x, int y, int cx, int cy,
2882 /* src */ HGLYPH glyph, int srcx, int srcy,
2883 int bgcolour, int fgcolour)
2884 {
2885 SET_FOREGROUND(fgcolour);
2886 SET_BACKGROUND(bgcolour);
2887
2888 XSetFillStyle(This->display, This->xwin.gc,
2889 (mixmode == MIX_TRANSPARENT) ? FillStippled : FillOpaqueStippled);
2890 XSetStipple(This->display, This->xwin.gc, (Pixmap) glyph);
2891 XSetTSOrigin(This->display, This->xwin.gc, x, y);
2892
2893 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2894
2895 XSetFillStyle(This->display, This->xwin.gc, FillSolid);
2896 }
2897
2898 #define DO_GLYPH(ttext,idx) \
2899 {\
2900 glyph = cache_get_font (This, font, ttext[idx]);\
2901 if (!(flags & TEXT2_IMPLICIT_X))\
2902 {\
2903 xyoffset = ttext[++idx];\
2904 if ((xyoffset & 0x80))\
2905 {\
2906 if (flags & TEXT2_VERTICAL)\
2907 y += ttext[idx+1] | (ttext[idx+2] << 8);\
2908 else\
2909 x += ttext[idx+1] | (ttext[idx+2] << 8);\
2910 idx += 2;\
2911 }\
2912 else\
2913 {\
2914 if (flags & TEXT2_VERTICAL)\
2915 y += xyoffset;\
2916 else\
2917 x += xyoffset;\
2918 }\
2919 }\
2920 if (glyph != NULL)\
2921 {\
2922 x1 = x + glyph->offset;\
2923 y1 = y + glyph->baseline;\
2924 XSetStipple(This->display, This->xwin.gc, (Pixmap) glyph->pixmap);\
2925 XSetTSOrigin(This->display, This->xwin.gc, x1, y1);\
2926 FILL_RECTANGLE_BACKSTORE(x1, y1, glyph->width, glyph->height);\
2927 if (flags & TEXT2_IMPLICIT_X)\
2928 x += glyph->width;\
2929 }\
2930 }
2931
2932 void
ui_draw_text(RDPCLIENT * This,uint8 font,uint8 flags,uint8 opcode,int mixmode,int x,int y,int clipx,int clipy,int clipcx,int clipcy,int boxx,int boxy,int boxcx,int boxcy,BRUSH * brush,int bgcolour,int fgcolour,uint8 * text,uint8 length)2933 ui_draw_text(RDPCLIENT * This, uint8 font, uint8 flags, uint8 opcode, int mixmode, int x, int y,
2934 int clipx, int clipy, int clipcx, int clipcy,
2935 int boxx, int boxy, int boxcx, int boxcy, BRUSH * brush,
2936 int bgcolour, int fgcolour, uint8 * text, uint8 length)
2937 {
2938 /* TODO: use brush appropriately */
2939
2940 FONTGLYPH *glyph;
2941 int i, j, xyoffset, x1, y1;
2942 DATABLOB *entry;
2943
2944 SET_FOREGROUND(bgcolour);
2945
2946 /* Sometimes, the boxcx value is something really large, like
2947 32691. This makes XCopyArea fail with Xvnc. The code below
2948 is a quick fix. */
2949 if (boxx + boxcx > This->width)
2950 boxcx = This->width - boxx;
2951
2952 if (boxcx > 1)
2953 {
2954 FILL_RECTANGLE_BACKSTORE(boxx, boxy, boxcx, boxcy);
2955 }
2956 else if (mixmode == MIX_OPAQUE)
2957 {
2958 FILL_RECTANGLE_BACKSTORE(clipx, clipy, clipcx, clipcy);
2959 }
2960
2961 SET_FOREGROUND(fgcolour);
2962 SET_BACKGROUND(bgcolour);
2963 XSetFillStyle(This->display, This->xwin.gc, FillStippled);
2964
2965 /* Paint text, character by character */
2966 for (i = 0; i < length;)
2967 {
2968 switch (text[i])
2969 {
2970 case 0xff:
2971 /* At least two bytes needs to follow */
2972 if (i + 3 > length)
2973 {
2974 warning("Skipping short 0xff command:");
2975 for (j = 0; j < length; j++)
2976 fprintf(stderr, "%02x ", text[j]);
2977 fprintf(stderr, "\n");
2978 i = length = 0;
2979 break;
2980 }
2981 cache_put_text(This, text[i + 1], text, text[i + 2]);
2982 i += 3;
2983 length -= i;
2984 /* this will move pointer from start to first character after FF command */
2985 text = &(text[i]);
2986 i = 0;
2987 break;
2988
2989 case 0xfe:
2990 /* At least one byte needs to follow */
2991 if (i + 2 > length)
2992 {
2993 warning("Skipping short 0xfe command:");
2994 for (j = 0; j < length; j++)
2995 fprintf(stderr, "%02x ", text[j]);
2996 fprintf(stderr, "\n");
2997 i = length = 0;
2998 break;
2999 }
3000 entry = cache_get_text(This, text[i + 1]);
3001 if (entry->data != NULL)
3002 {
3003 if ((((uint8 *) (entry->data))[1] == 0)
3004 && (!(flags & TEXT2_IMPLICIT_X)) && (i + 2 < length))
3005 {
3006 if (flags & TEXT2_VERTICAL)
3007 y += text[i + 2];
3008 else
3009 x += text[i + 2];
3010 }
3011 for (j = 0; j < entry->size; j++)
3012 DO_GLYPH(((uint8 *) (entry->data)), j);
3013 }
3014 if (i + 2 < length)
3015 i += 3;
3016 else
3017 i += 2;
3018 length -= i;
3019 /* this will move pointer from start to first character after FE command */
3020 text = &(text[i]);
3021 i = 0;
3022 break;
3023
3024 default:
3025 DO_GLYPH(text, i);
3026 i++;
3027 break;
3028 }
3029 }
3030
3031 XSetFillStyle(This->display, This->xwin.gc, FillSolid);
3032
3033 if (This->ownbackstore)
3034 {
3035 if (boxcx > 1)
3036 {
3037 XCopyArea(This->display, This->xwin.backstore, This->wnd, This->xwin.gc, boxx,
3038 boxy, boxcx, boxcy, boxx, boxy);
3039 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3040 (This->display, This->xwin.backstore, sw->wnd, This->xwin.gc,
3041 boxx, boxy,
3042 boxcx, boxcy,
3043 boxx - sw->xoffset, boxy - sw->yoffset));
3044 }
3045 else
3046 {
3047 XCopyArea(This->display, This->xwin.backstore, This->wnd, This->xwin.gc, clipx,
3048 clipy, clipcx, clipcy, clipx, clipy);
3049 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3050 (This->display, This->xwin.backstore, sw->wnd, This->xwin.gc,
3051 clipx, clipy,
3052 clipcx, clipcy, clipx - sw->xoffset,
3053 clipy - sw->yoffset));
3054 }
3055 }
3056 }
3057
3058 void
ui_desktop_save(RDPCLIENT * This,uint32 offset,int x,int y,int cx,int cy)3059 ui_desktop_save(RDPCLIENT * This, uint32 offset, int x, int y, int cx, int cy)
3060 {
3061 Pixmap pix;
3062 XImage *image;
3063
3064 if (This->ownbackstore)
3065 {
3066 image = XGetImage(This->display, This->xwin.backstore, x, y, cx, cy, AllPlanes, ZPixmap);
3067 }
3068 else
3069 {
3070 pix = XCreatePixmap(This->display, This->wnd, cx, cy, This->xwin.depth);
3071 XCopyArea(This->display, This->wnd, pix, This->xwin.gc, x, y, cx, cy, 0, 0);
3072 image = XGetImage(This->display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap);
3073 XFreePixmap(This->display, pix);
3074 }
3075
3076 offset *= This->xwin.bpp / 8;
3077 cache_put_desktop(This, offset, cx, cy, image->bytes_per_line, This->xwin.bpp / 8, (uint8 *) image->data);
3078
3079 XDestroyImage(image);
3080 }
3081
3082 void
ui_desktop_restore(RDPCLIENT * This,uint32 offset,int x,int y,int cx,int cy)3083 ui_desktop_restore(RDPCLIENT * This, uint32 offset, int x, int y, int cx, int cy)
3084 {
3085 XImage *image;
3086 uint8 *data;
3087
3088 offset *= This->xwin.bpp / 8;
3089 data = cache_get_desktop(This, offset, cx, cy, This->xwin.bpp / 8);
3090 if (data == NULL)
3091 return;
3092
3093 image = XCreateImage(This->display, This->xwin.visual, This->xwin.depth, ZPixmap, 0,
3094 (char *) data, cx, cy, BitmapPad(This->display), cx * This->xwin.bpp / 8);
3095
3096 if (This->ownbackstore)
3097 {
3098 XPutImage(This->display, This->xwin.backstore, This->xwin.gc, image, 0, 0, x, y, cx, cy);
3099 XCopyArea(This->display, This->xwin.backstore, This->wnd, This->xwin.gc, x, y, cx, cy, x, y);
3100 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3101 (This->display, This->xwin.backstore, sw->wnd, This->xwin.gc,
3102 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
3103 }
3104 else
3105 {
3106 XPutImage(This->display, This->wnd, This->xwin.gc, image, 0, 0, x, y, cx, cy);
3107 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
3108 (This->display, This->wnd, sw->wnd, This->xwin.gc, x, y, cx, cy,
3109 x - sw->xoffset, y - sw->yoffset));
3110 }
3111
3112 XFree(image);
3113 }
3114
3115 /* these do nothing here but are used in uiports */
3116 void
ui_begin_update(RDPCLIENT * This)3117 ui_begin_update(RDPCLIENT * This)
3118 {
3119 }
3120
3121 void
ui_end_update(RDPCLIENT * This)3122 ui_end_update(RDPCLIENT * This)
3123 {
3124 }
3125
3126
3127 void
ui_seamless_begin(RDPCLIENT * This,BOOL hidden)3128 ui_seamless_begin(RDPCLIENT * This, BOOL hidden)
3129 {
3130 if (!This->seamless_rdp)
3131 return;
3132
3133 if (This->xwin.seamless_started)
3134 return;
3135
3136 This->xwin.seamless_started = True;
3137 This->xwin.seamless_hidden = hidden;
3138
3139 if (!hidden)
3140 ui_seamless_toggle(This);
3141 }
3142
3143
3144 void
ui_seamless_hide_desktop(RDPCLIENT * This)3145 ui_seamless_hide_desktop(RDPCLIENT * This)
3146 {
3147 if (!This->seamless_rdp)
3148 return;
3149
3150 if (!This->xwin.seamless_started)
3151 return;
3152
3153 if (This->xwin.seamless_active)
3154 ui_seamless_toggle(This);
3155
3156 This->xwin.seamless_hidden = True;
3157 }
3158
3159
3160 void
ui_seamless_unhide_desktop(RDPCLIENT * This)3161 ui_seamless_unhide_desktop(RDPCLIENT * This)
3162 {
3163 if (!This->seamless_rdp)
3164 return;
3165
3166 if (!This->xwin.seamless_started)
3167 return;
3168
3169 This->xwin.seamless_hidden = False;
3170
3171 ui_seamless_toggle(This);
3172 }
3173
3174
3175 void
ui_seamless_toggle(RDPCLIENT * This)3176 ui_seamless_toggle(RDPCLIENT * This)
3177 {
3178 if (!This->seamless_rdp)
3179 return;
3180
3181 if (!This->xwin.seamless_started)
3182 return;
3183
3184 if (This->xwin.seamless_hidden)
3185 return;
3186
3187 if (This->xwin.seamless_active)
3188 {
3189 /* Deactivate */
3190 while (This->xwin.seamless_windows)
3191 {
3192 XDestroyWindow(This->display, This->xwin.seamless_windows->wnd);
3193 sw_remove_window(This, This->xwin.seamless_windows);
3194 }
3195 XMapWindow(This->display, This->wnd);
3196 }
3197 else
3198 {
3199 /* Activate */
3200 XUnmapWindow(This->display, This->wnd);
3201 seamless_send_sync(This);
3202 }
3203
3204 This->xwin.seamless_active = !This->xwin.seamless_active;
3205 }
3206
3207
3208 void
ui_seamless_create_window(RDPCLIENT * This,unsigned long id,unsigned long group,unsigned long parent,unsigned long flags)3209 ui_seamless_create_window(RDPCLIENT * This, unsigned long id, unsigned long group, unsigned long parent,
3210 unsigned long flags)
3211 {
3212 Window wnd;
3213 XSetWindowAttributes attribs;
3214 XClassHint *classhints;
3215 XSizeHints *sizehints;
3216 XWMHints *wmhints;
3217 long input_mask;
3218 seamless_window *sw, *sw_parent;
3219
3220 if (!This->xwin.seamless_active)
3221 return;
3222
3223 /* Ignore CREATEs for existing windows */
3224 sw = sw_get_window_by_id(This, id);
3225 if (sw)
3226 return;
3227
3228 get_window_attribs(This, &attribs);
3229 wnd = XCreateWindow(This->display, RootWindowOfScreen(This->xwin.screen), -1, -1, 1, 1, 0, This->xwin.depth,
3230 InputOutput, This->xwin.visual,
3231 CWBackPixel | CWBackingStore | CWColormap | CWBorderPixel, &attribs);
3232
3233 XStoreName(This->display, wnd, "SeamlessRDP");
3234 ewmh_set_wm_name(This, wnd, "SeamlessRDP");
3235
3236 mwm_hide_decorations(This, wnd);
3237
3238 classhints = XAllocClassHint();
3239 if (classhints != NULL)
3240 {
3241 classhints->res_name = "rdesktop";
3242 classhints->res_class = "SeamlessRDP";
3243 XSetClassHint(This->display, wnd, classhints);
3244 XFree(classhints);
3245 }
3246
3247 /* WM_NORMAL_HINTS */
3248 sizehints = XAllocSizeHints();
3249 if (sizehints != NULL)
3250 {
3251 sizehints->flags = USPosition;
3252 XSetWMNormalHints(This->display, wnd, sizehints);
3253 XFree(sizehints);
3254 }
3255
3256 /* Parent-less transient windows */
3257 if (parent == 0xFFFFFFFF)
3258 {
3259 XSetTransientForHint(This->display, wnd, RootWindowOfScreen(This->xwin.screen));
3260 /* Some buggy wm:s (kwin) do not handle the above, so fake it
3261 using some other hints. */
3262 ewmh_set_window_popup(This, wnd);
3263 }
3264 /* Normal transient windows */
3265 else if (parent != 0x00000000)
3266 {
3267 sw_parent = sw_get_window_by_id(This, parent);
3268 if (sw_parent)
3269 XSetTransientForHint(This->display, wnd, sw_parent->wnd);
3270 else
3271 warning("ui_seamless_create_window: No parent window 0x%lx\n", parent);
3272 }
3273
3274 if (flags & SEAMLESSRDP_CREATE_MODAL)
3275 {
3276 /* We do this to support buggy wm:s (*cough* metacity *cough*)
3277 somewhat at least */
3278 if (parent == 0x00000000)
3279 XSetTransientForHint(This->display, wnd, RootWindowOfScreen(This->xwin.screen));
3280 ewmh_set_window_modal(This, wnd);
3281 }
3282
3283 /* FIXME: Support for Input Context:s */
3284
3285 get_input_mask(This, &input_mask);
3286 input_mask |= PropertyChangeMask;
3287
3288 XSelectInput(This->display, wnd, input_mask);
3289
3290 /* handle the WM_DELETE_WINDOW protocol. FIXME: When killing a
3291 seamless window, we could try to close the window on the
3292 serverside, instead of terminating rdesktop */
3293 XSetWMProtocols(This->display, wnd, &This->xwin.kill_atom, 1);
3294
3295 sw = xmalloc(sizeof(seamless_window));
3296 sw->wnd = wnd;
3297 sw->id = id;
3298 sw->behind = 0;
3299 sw->group = sw_find_group(This, group, False);
3300 sw->group->refcnt++;
3301 sw->xoffset = 0;
3302 sw->yoffset = 0;
3303 sw->width = 0;
3304 sw->height = 0;
3305 sw->state = SEAMLESSRDP_NOTYETMAPPED;
3306 sw->desktop = 0;
3307 sw->position_timer = xmalloc(sizeof(struct timeval));
3308 timerclear(sw->position_timer);
3309
3310 sw->outstanding_position = False;
3311 sw->outpos_serial = 0;
3312 sw->outpos_xoffset = sw->outpos_yoffset = 0;
3313 sw->outpos_width = sw->outpos_height = 0;
3314
3315 sw->next = This->xwin.seamless_windows;
3316 This->xwin.seamless_windows = sw;
3317
3318 /* WM_HINTS */
3319 wmhints = XAllocWMHints();
3320 if (wmhints)
3321 {
3322 wmhints->flags = WindowGroupHint;
3323 wmhints->window_group = sw->group->wnd;
3324 XSetWMHints(This->display, sw->wnd, wmhints);
3325 XFree(wmhints);
3326 }
3327 }
3328
3329
3330 void
ui_seamless_destroy_window(RDPCLIENT * This,unsigned long id,unsigned long flags)3331 ui_seamless_destroy_window(RDPCLIENT * This, unsigned long id, unsigned long flags)
3332 {
3333 seamless_window *sw;
3334
3335 if (!This->xwin.seamless_active)
3336 return;
3337
3338 sw = sw_get_window_by_id(This, id);
3339 if (!sw)
3340 {
3341 warning("ui_seamless_destroy_window: No information for window 0x%lx\n", id);
3342 return;
3343 }
3344
3345 XDestroyWindow(This->display, sw->wnd);
3346 sw_remove_window(This, sw);
3347 }
3348
3349
3350 void
ui_seamless_destroy_group(RDPCLIENT * This,unsigned long id,unsigned long flags)3351 ui_seamless_destroy_group(RDPCLIENT * This, unsigned long id, unsigned long flags)
3352 {
3353 seamless_window *sw, *sw_next;
3354
3355 if (!This->xwin.seamless_active)
3356 return;
3357
3358 for (sw = This->xwin.seamless_windows; sw; sw = sw_next)
3359 {
3360 sw_next = sw->next;
3361
3362 if (sw->group->id == id)
3363 {
3364 XDestroyWindow(This->display, sw->wnd);
3365 sw_remove_window(This, sw);
3366 }
3367 }
3368 }
3369
3370
3371 void
ui_seamless_move_window(RDPCLIENT * This,unsigned long id,int x,int y,int width,int height,unsigned long flags)3372 ui_seamless_move_window(RDPCLIENT * This, unsigned long id, int x, int y, int width, int height, unsigned long flags)
3373 {
3374 seamless_window *sw;
3375
3376 if (!This->xwin.seamless_active)
3377 return;
3378
3379 sw = sw_get_window_by_id(This, id);
3380 if (!sw)
3381 {
3382 warning("ui_seamless_move_window: No information for window 0x%lx\n", id);
3383 return;
3384 }
3385
3386 /* We ignore server updates until it has handled our request. */
3387 if (sw->outstanding_position)
3388 return;
3389
3390 if (!width || !height)
3391 /* X11 windows must be at least 1x1 */
3392 return;
3393
3394 sw->xoffset = x;
3395 sw->yoffset = y;
3396 sw->width = width;
3397 sw->height = height;
3398
3399 /* If we move the window in a maximized state, then KDE won't
3400 accept restoration */
3401 switch (sw->state)
3402 {
3403 case SEAMLESSRDP_MINIMIZED:
3404 case SEAMLESSRDP_MAXIMIZED:
3405 return;
3406 }
3407
3408 /* FIXME: Perhaps use ewmh_net_moveresize_window instead */
3409 XMoveResizeWindow(This->display, sw->wnd, sw->xoffset, sw->yoffset, sw->width, sw->height);
3410 }
3411
3412
3413 void
ui_seamless_restack_window(RDPCLIENT * This,unsigned long id,unsigned long behind,unsigned long flags)3414 ui_seamless_restack_window(RDPCLIENT * This, unsigned long id, unsigned long behind, unsigned long flags)
3415 {
3416 seamless_window *sw;
3417
3418 if (!This->xwin.seamless_active)
3419 return;
3420
3421 sw = sw_get_window_by_id(This, id);
3422 if (!sw)
3423 {
3424 warning("ui_seamless_restack_window: No information for window 0x%lx\n", id);
3425 return;
3426 }
3427
3428 if (behind)
3429 {
3430 seamless_window *sw_behind;
3431 Window wnds[2];
3432
3433 sw_behind = sw_get_window_by_id(This, behind);
3434 if (!sw_behind)
3435 {
3436 warning("ui_seamless_restack_window: No information for window 0x%lx\n",
3437 behind);
3438 return;
3439 }
3440
3441 wnds[1] = sw_behind->wnd;
3442 wnds[0] = sw->wnd;
3443
3444 XRestackWindows(This->display, wnds, 2);
3445 }
3446 else
3447 {
3448 XRaiseWindow(This->display, sw->wnd);
3449 }
3450
3451 sw_restack_window(This, sw, behind);
3452 }
3453
3454
3455 void
ui_seamless_settitle(RDPCLIENT * This,unsigned long id,const char * title,unsigned long flags)3456 ui_seamless_settitle(RDPCLIENT * This, unsigned long id, const char *title, unsigned long flags)
3457 {
3458 seamless_window *sw;
3459
3460 if (!This->xwin.seamless_active)
3461 return;
3462
3463 sw = sw_get_window_by_id(This, id);
3464 if (!sw)
3465 {
3466 warning("ui_seamless_settitle: No information for window 0x%lx\n", id);
3467 return;
3468 }
3469
3470 /* FIXME: Might want to convert the name for non-EWMH WMs */
3471 XStoreName(This->display, sw->wnd, title);
3472 ewmh_set_wm_name(This, sw->wnd, title);
3473 }
3474
3475
3476 void
ui_seamless_setstate(RDPCLIENT * This,unsigned long id,unsigned int state,unsigned long flags)3477 ui_seamless_setstate(RDPCLIENT * This, unsigned long id, unsigned int state, unsigned long flags)
3478 {
3479 seamless_window *sw;
3480
3481 if (!This->xwin.seamless_active)
3482 return;
3483
3484 sw = sw_get_window_by_id(This, id);
3485 if (!sw)
3486 {
3487 warning("ui_seamless_setstate: No information for window 0x%lx\n", id);
3488 return;
3489 }
3490
3491 switch (state)
3492 {
3493 case SEAMLESSRDP_NORMAL:
3494 case SEAMLESSRDP_MAXIMIZED:
3495 ewmh_change_state(This, sw->wnd, state);
3496 XMapWindow(This->display, sw->wnd);
3497 break;
3498 case SEAMLESSRDP_MINIMIZED:
3499 /* EWMH says: "if an Application asks to toggle _NET_WM_STATE_HIDDEN
3500 the Window Manager should probably just ignore the request, since
3501 _NET_WM_STATE_HIDDEN is a function of some other aspect of the window
3502 such as minimization, rather than an independent state." Besides,
3503 XIconifyWindow is easier. */
3504 if (sw->state == SEAMLESSRDP_NOTYETMAPPED)
3505 {
3506 XWMHints *hints;
3507 hints = XGetWMHints(This->display, sw->wnd);
3508 if (hints)
3509 {
3510 hints->flags |= StateHint;
3511 hints->initial_state = IconicState;
3512 XSetWMHints(This->display, sw->wnd, hints);
3513 XFree(hints);
3514 }
3515 XMapWindow(This->display, sw->wnd);
3516 }
3517 else
3518 XIconifyWindow(This->display, sw->wnd, DefaultScreen(This->display));
3519 break;
3520 default:
3521 warning("SeamlessRDP: Invalid state %d\n", state);
3522 break;
3523 }
3524
3525 sw->state = state;
3526 }
3527
3528
3529 void
ui_seamless_syncbegin(RDPCLIENT * This,unsigned long flags)3530 ui_seamless_syncbegin(RDPCLIENT * This, unsigned long flags)
3531 {
3532 if (!This->xwin.seamless_active)
3533 return;
3534
3535 /* Destroy all seamless windows */
3536 while (This->xwin.seamless_windows)
3537 {
3538 XDestroyWindow(This->display, This->xwin.seamless_windows->wnd);
3539 sw_remove_window(This, This->xwin.seamless_windows);
3540 }
3541 }
3542
3543
3544 void
ui_seamless_ack(RDPCLIENT * This,unsigned int serial)3545 ui_seamless_ack(RDPCLIENT * This, unsigned int serial)
3546 {
3547 seamless_window *sw;
3548 for (sw = This->xwin.seamless_windows; sw; sw = sw->next)
3549 {
3550 if (sw->outstanding_position && (sw->outpos_serial == serial))
3551 {
3552 sw->xoffset = sw->outpos_xoffset;
3553 sw->yoffset = sw->outpos_yoffset;
3554 sw->width = sw->outpos_width;
3555 sw->height = sw->outpos_height;
3556 sw->outstanding_position = False;
3557
3558 /* Do a complete redraw of the window as part of the
3559 completion of the move. This is to remove any
3560 artifacts caused by our lack of synchronization. */
3561 XCopyArea(This->display, This->xwin.backstore,
3562 sw->wnd, This->xwin.gc,
3563 sw->xoffset, sw->yoffset, sw->width, sw->height, 0, 0);
3564
3565 break;
3566 }
3567 }
3568 }
3569