/[rdesktop]/sourceforge.net/branches/seamlessrdp-branch/rdesktop/xwin.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /sourceforge.net/branches/seamlessrdp-branch/rdesktop/xwin.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1094 - (show annotations)
Fri Mar 10 10:40:50 2006 UTC (18 years, 2 months ago) by astrand
File MIME type: text/plain
File size: 72663 byte(s)
SETSTATE is not two ops: TITLE and STATE

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

  ViewVC Help
Powered by ViewVC 1.1.26