/[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 1142 - (show annotations)
Thu Mar 16 08:11:29 2006 UTC (18 years, 2 months ago) by ossman_
File MIME type: text/plain
File size: 76114 byte(s)
Handle initially minimized windows through WM_HINTS.

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

  ViewVC Help
Powered by ViewVC 1.1.26