/[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 1118 - (show annotations)
Tue Mar 14 13:56:50 2006 UTC (18 years, 2 months ago) by astrand
File MIME type: text/plain
File size: 74377 byte(s)
Implemented support for changing state normal/minimized/maximized via EWMH. Also, sending notifications to server when local state changes.

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

  ViewVC Help
Powered by ViewVC 1.1.26