/[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 1122 - (show annotations)
Wed Mar 15 08:35:13 2006 UTC (18 years, 1 month ago) by astrand
File MIME type: text/plain
File size: 75112 byte(s)
Ignoring CREATEs for existing windows - be idempotent.

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

  ViewVC Help
Powered by ViewVC 1.1.26