/[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 1151 - (show annotations)
Fri Mar 17 08:52:29 2006 UTC (18 years, 2 months ago) by ossman_
File MIME type: text/plain
File size: 78018 byte(s)
Support for seamless window restacking (ZCHANGE).

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

  ViewVC Help
Powered by ViewVC 1.1.26