/[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 1144 - (show annotations)
Thu Mar 16 10:14:18 2006 UTC (18 years, 2 months ago) by ossman_
File MIME type: text/plain
File size: 76199 byte(s)
Only allocate the XWMHints structure where we need it.

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

  ViewVC Help
Powered by ViewVC 1.1.26