/[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 1149 - (show annotations)
Thu Mar 16 15:27:59 2006 UTC (18 years, 2 months ago) by ossman_
File MIME type: text/plain
File size: 76514 byte(s)
Don't activate seamless mode until we have a working connection with the
remote server (i.e. when we get a HELLO).

Also change g_seamless_rdp to mean that -A was specified on the command line.
g_seamless_active now indicates if we're in seamless or "normal" mode.

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 /* Process events in Xlib queue
1748 Returns 0 after user quit, 1 otherwise */
1749 static int
1750 xwin_process_events(void)
1751 {
1752 XEvent xevent;
1753 KeySym keysym;
1754 uint32 ev_time;
1755 char str[256];
1756 Status status;
1757 int events = 0;
1758 seamless_window *sw;
1759
1760 while ((XPending(g_display) > 0) && events++ < 20)
1761 {
1762 XNextEvent(g_display, &xevent);
1763
1764 if ((g_IC != NULL) && (XFilterEvent(&xevent, None) == True))
1765 {
1766 DEBUG_KBD(("Filtering event\n"));
1767 continue;
1768 }
1769
1770 switch (xevent.type)
1771 {
1772 case VisibilityNotify:
1773 if (xevent.xvisibility.window == g_wnd)
1774 g_Unobscured =
1775 xevent.xvisibility.state == VisibilityUnobscured;
1776
1777 break;
1778 case ClientMessage:
1779 /* the window manager told us to quit */
1780 if ((xevent.xclient.message_type == g_protocol_atom)
1781 && ((Atom) xevent.xclient.data.l[0] == g_kill_atom))
1782 /* Quit */
1783 return 0;
1784 break;
1785
1786 case KeyPress:
1787 g_last_gesturetime = xevent.xkey.time;
1788 if (g_IC != NULL)
1789 /* Multi_key compatible version */
1790 {
1791 XmbLookupString(g_IC,
1792 &xevent.xkey, str, sizeof(str), &keysym,
1793 &status);
1794 if (!((status == XLookupKeySym) || (status == XLookupBoth)))
1795 {
1796 error("XmbLookupString failed with status 0x%x\n",
1797 status);
1798 break;
1799 }
1800 }
1801 else
1802 {
1803 /* Plain old XLookupString */
1804 DEBUG_KBD(("\nNo input context, using XLookupString\n"));
1805 XLookupString((XKeyEvent *) & xevent,
1806 str, sizeof(str), &keysym, NULL);
1807 }
1808
1809 DEBUG_KBD(("KeyPress for keysym (0x%lx, %s)\n", keysym,
1810 get_ksname(keysym)));
1811
1812 ev_time = time(NULL);
1813 if (handle_special_keys(keysym, xevent.xkey.state, ev_time, True))
1814 break;
1815
1816 xkeymap_send_keys(keysym, xevent.xkey.keycode, xevent.xkey.state,
1817 ev_time, True, 0);
1818 break;
1819
1820 case KeyRelease:
1821 g_last_gesturetime = xevent.xkey.time;
1822 XLookupString((XKeyEvent *) & xevent, str,
1823 sizeof(str), &keysym, NULL);
1824
1825 DEBUG_KBD(("\nKeyRelease for keysym (0x%lx, %s)\n", keysym,
1826 get_ksname(keysym)));
1827
1828 ev_time = time(NULL);
1829 if (handle_special_keys(keysym, xevent.xkey.state, ev_time, False))
1830 break;
1831
1832 xkeymap_send_keys(keysym, xevent.xkey.keycode, xevent.xkey.state,
1833 ev_time, False, 0);
1834 break;
1835
1836 case ButtonPress:
1837 handle_button_event(xevent, True);
1838 break;
1839
1840 case ButtonRelease:
1841 handle_button_event(xevent, False);
1842 break;
1843
1844 case MotionNotify:
1845 if (g_moving_wnd)
1846 {
1847 XMoveWindow(g_display, g_wnd,
1848 xevent.xmotion.x_root - g_move_x_offset,
1849 xevent.xmotion.y_root - g_move_y_offset);
1850 break;
1851 }
1852
1853 if (g_fullscreen && !g_focused)
1854 XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
1855 CurrentTime);
1856
1857 if (xevent.xmotion.window == g_wnd)
1858 {
1859 rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
1860 xevent.xmotion.x, xevent.xmotion.y);
1861 }
1862 else
1863 {
1864 /* SeamlessRDP */
1865 rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
1866 xevent.xmotion.x_root,
1867 xevent.xmotion.y_root);
1868 }
1869 break;
1870
1871 case FocusIn:
1872 if (xevent.xfocus.mode == NotifyGrab)
1873 break;
1874 g_focused = True;
1875 reset_modifier_keys();
1876 if (g_grab_keyboard && g_mouse_in_wnd)
1877 XGrabKeyboard(g_display, g_wnd, True,
1878 GrabModeAsync, GrabModeAsync, CurrentTime);
1879 break;
1880
1881 case FocusOut:
1882 if (xevent.xfocus.mode == NotifyUngrab)
1883 break;
1884 g_focused = False;
1885 if (xevent.xfocus.mode == NotifyWhileGrabbed)
1886 XUngrabKeyboard(g_display, CurrentTime);
1887 break;
1888
1889 case EnterNotify:
1890 /* we only register for this event when in fullscreen mode */
1891 /* or grab_keyboard */
1892 g_mouse_in_wnd = True;
1893 if (g_fullscreen)
1894 {
1895 XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
1896 CurrentTime);
1897 break;
1898 }
1899 if (g_focused)
1900 XGrabKeyboard(g_display, g_wnd, True,
1901 GrabModeAsync, GrabModeAsync, CurrentTime);
1902 break;
1903
1904 case LeaveNotify:
1905 /* we only register for this event when grab_keyboard */
1906 g_mouse_in_wnd = False;
1907 XUngrabKeyboard(g_display, CurrentTime);
1908 break;
1909
1910 case Expose:
1911 if (xevent.xexpose.window == g_wnd)
1912 {
1913 XCopyArea(g_display, g_backstore, xevent.xexpose.window,
1914 g_gc,
1915 xevent.xexpose.x, xevent.xexpose.y,
1916 xevent.xexpose.width, xevent.xexpose.height,
1917 xevent.xexpose.x, xevent.xexpose.y);
1918 }
1919 else
1920 {
1921 sw = seamless_get_window_by_wnd(xevent.xexpose.window);
1922 if (sw)
1923 XCopyArea(g_display, g_backstore,
1924 xevent.xexpose.window, g_gc,
1925 xevent.xexpose.x + sw->xoffset,
1926 xevent.xexpose.y + sw->yoffset,
1927 xevent.xexpose.width,
1928 xevent.xexpose.height, xevent.xexpose.x,
1929 xevent.xexpose.y);
1930 else
1931 {
1932 error("Expose for unknown window 0x%lx\n",
1933 xevent.xexpose.window);
1934 }
1935 }
1936
1937 break;
1938
1939 case MappingNotify:
1940 /* Refresh keyboard mapping if it has changed. This is important for
1941 Xvnc, since it allocates keycodes dynamically */
1942 if (xevent.xmapping.request == MappingKeyboard
1943 || xevent.xmapping.request == MappingModifier)
1944 XRefreshKeyboardMapping(&xevent.xmapping);
1945
1946 if (xevent.xmapping.request == MappingModifier)
1947 {
1948 XFreeModifiermap(g_mod_map);
1949 g_mod_map = XGetModifierMapping(g_display);
1950 }
1951 break;
1952
1953 /* clipboard stuff */
1954 case SelectionNotify:
1955 xclip_handle_SelectionNotify(&xevent.xselection);
1956 break;
1957 case SelectionRequest:
1958 xclip_handle_SelectionRequest(&xevent.xselectionrequest);
1959 break;
1960 case SelectionClear:
1961 xclip_handle_SelectionClear();
1962 break;
1963 case PropertyNotify:
1964 xclip_handle_PropertyNotify(&xevent.xproperty);
1965 if (xevent.xproperty.window == g_wnd)
1966 break;
1967 if (xevent.xproperty.window == DefaultRootWindow(g_display))
1968 break;
1969
1970 /* seamless */
1971 sw = seamless_get_window_by_wnd(xevent.xproperty.window);
1972 if (!sw)
1973 break;
1974
1975 if ((xevent.xproperty.atom == g_net_wm_state_atom)
1976 && (xevent.xproperty.state == PropertyNewValue))
1977 {
1978 sw->state = ewmh_get_window_state(sw->wnd);
1979 seamless_send_state(sw->id, sw->state, 0);
1980 }
1981
1982 if ((xevent.xproperty.atom == g_net_wm_desktop_atom)
1983 && (xevent.xproperty.state == PropertyNewValue))
1984 {
1985 sw->desktop = ewmh_get_window_desktop(sw->wnd);
1986 seamless_all_to_desktop(sw->wnd, sw->desktop);
1987 }
1988
1989 break;
1990 case MapNotify:
1991 if (!g_seamless_active)
1992 rdp_send_client_window_status(1);
1993 break;
1994 case UnmapNotify:
1995 if (!g_seamless_active)
1996 rdp_send_client_window_status(0);
1997 break;
1998 }
1999 }
2000 /* Keep going */
2001 return 1;
2002 }
2003
2004 /* Returns 0 after user quit, 1 otherwise */
2005 int
2006 ui_select(int rdp_socket)
2007 {
2008 int n;
2009 fd_set rfds, wfds;
2010 struct timeval tv;
2011 BOOL s_timeout = False;
2012
2013 while (True)
2014 {
2015 n = (rdp_socket > g_x_socket) ? rdp_socket : g_x_socket;
2016 /* Process any events already waiting */
2017 if (!xwin_process_events())
2018 /* User quit */
2019 return 0;
2020
2021 FD_ZERO(&rfds);
2022 FD_ZERO(&wfds);
2023 FD_SET(rdp_socket, &rfds);
2024 FD_SET(g_x_socket, &rfds);
2025
2026 #ifdef WITH_RDPSND
2027 /* FIXME: there should be an API for registering fds */
2028 if (g_dsp_busy)
2029 {
2030 FD_SET(g_dsp_fd, &wfds);
2031 n = (g_dsp_fd > n) ? g_dsp_fd : n;
2032 }
2033 #endif
2034 /* default timeout */
2035 tv.tv_sec = 60;
2036 tv.tv_usec = 0;
2037
2038 /* add redirection handles */
2039 rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);
2040
2041 n++;
2042
2043 switch (select(n, &rfds, &wfds, NULL, &tv))
2044 {
2045 case -1:
2046 error("select: %s\n", strerror(errno));
2047
2048 case 0:
2049 /* Abort serial read calls */
2050 if (s_timeout)
2051 rdpdr_check_fds(&rfds, &wfds, (BOOL) True);
2052 continue;
2053 }
2054
2055 rdpdr_check_fds(&rfds, &wfds, (BOOL) False);
2056
2057 if (FD_ISSET(rdp_socket, &rfds))
2058 return 1;
2059
2060 #ifdef WITH_RDPSND
2061 if (g_dsp_busy && FD_ISSET(g_dsp_fd, &wfds))
2062 wave_out_play();
2063 #endif
2064 }
2065 }
2066
2067 void
2068 ui_move_pointer(int x, int y)
2069 {
2070 XWarpPointer(g_display, g_wnd, g_wnd, 0, 0, 0, 0, x, y);
2071 }
2072
2073 HBITMAP
2074 ui_create_bitmap(int width, int height, uint8 * data)
2075 {
2076 XImage *image;
2077 Pixmap bitmap;
2078 uint8 *tdata;
2079 int bitmap_pad;
2080
2081 if (g_server_depth == 8)
2082 {
2083 bitmap_pad = 8;
2084 }
2085 else
2086 {
2087 bitmap_pad = g_bpp;
2088
2089 if (g_bpp == 24)
2090 bitmap_pad = 32;
2091 }
2092
2093 tdata = (g_owncolmap ? data : translate_image(width, height, data));
2094 bitmap = XCreatePixmap(g_display, g_wnd, width, height, g_depth);
2095 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
2096 (char *) tdata, width, height, bitmap_pad, 0);
2097
2098 XPutImage(g_display, bitmap, g_create_bitmap_gc, image, 0, 0, 0, 0, width, height);
2099
2100 XFree(image);
2101 if (tdata != data)
2102 xfree(tdata);
2103 return (HBITMAP) bitmap;
2104 }
2105
2106 void
2107 ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height, uint8 * data)
2108 {
2109 XImage *image;
2110 uint8 *tdata;
2111 int bitmap_pad;
2112
2113 if (g_server_depth == 8)
2114 {
2115 bitmap_pad = 8;
2116 }
2117 else
2118 {
2119 bitmap_pad = g_bpp;
2120
2121 if (g_bpp == 24)
2122 bitmap_pad = 32;
2123 }
2124
2125 tdata = (g_owncolmap ? data : translate_image(width, height, data));
2126 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
2127 (char *) tdata, width, height, bitmap_pad, 0);
2128
2129 if (g_ownbackstore)
2130 {
2131 XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
2132 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2133 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2134 (g_display, g_backstore, sw->wnd, g_gc, x, y, cx, cy,
2135 x - sw->xoffset, y - sw->yoffset));
2136 }
2137 else
2138 {
2139 XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
2140 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2141 (g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy,
2142 x - sw->xoffset, y - sw->yoffset));
2143 }
2144
2145 XFree(image);
2146 if (tdata != data)
2147 xfree(tdata);
2148 }
2149
2150 void
2151 ui_destroy_bitmap(HBITMAP bmp)
2152 {
2153 XFreePixmap(g_display, (Pixmap) bmp);
2154 }
2155
2156 HGLYPH
2157 ui_create_glyph(int width, int height, uint8 * data)
2158 {
2159 XImage *image;
2160 Pixmap bitmap;
2161 int scanline;
2162
2163 scanline = (width + 7) / 8;
2164
2165 bitmap = XCreatePixmap(g_display, g_wnd, width, height, 1);
2166 if (g_create_glyph_gc == 0)
2167 g_create_glyph_gc = XCreateGC(g_display, bitmap, 0, NULL);
2168
2169 image = XCreateImage(g_display, g_visual, 1, ZPixmap, 0, (char *) data,
2170 width, height, 8, scanline);
2171 image->byte_order = MSBFirst;
2172 image->bitmap_bit_order = MSBFirst;
2173 XInitImage(image);
2174
2175 XPutImage(g_display, bitmap, g_create_glyph_gc, image, 0, 0, 0, 0, width, height);
2176
2177 XFree(image);
2178 return (HGLYPH) bitmap;
2179 }
2180
2181 void
2182 ui_destroy_glyph(HGLYPH glyph)
2183 {
2184 XFreePixmap(g_display, (Pixmap) glyph);
2185 }
2186
2187 HCURSOR
2188 ui_create_cursor(unsigned int x, unsigned int y, int width, int height,
2189 uint8 * andmask, uint8 * xormask)
2190 {
2191 HGLYPH maskglyph, cursorglyph;
2192 XColor bg, fg;
2193 Cursor xcursor;
2194 uint8 *cursor, *pcursor;
2195 uint8 *mask, *pmask;
2196 uint8 nextbit;
2197 int scanline, offset;
2198 int i, j;
2199
2200 scanline = (width + 7) / 8;
2201 offset = scanline * height;
2202
2203 cursor = (uint8 *) xmalloc(offset);
2204 memset(cursor, 0, offset);
2205
2206 mask = (uint8 *) xmalloc(offset);
2207 memset(mask, 0, offset);
2208
2209 /* approximate AND and XOR masks with a monochrome X pointer */
2210 for (i = 0; i < height; i++)
2211 {
2212 offset -= scanline;
2213 pcursor = &cursor[offset];
2214 pmask = &mask[offset];
2215
2216 for (j = 0; j < scanline; j++)
2217 {
2218 for (nextbit = 0x80; nextbit != 0; nextbit >>= 1)
2219 {
2220 if (xormask[0] || xormask[1] || xormask[2])
2221 {
2222 *pcursor |= (~(*andmask) & nextbit);
2223 *pmask |= nextbit;
2224 }
2225 else
2226 {
2227 *pcursor |= ((*andmask) & nextbit);
2228 *pmask |= (~(*andmask) & nextbit);
2229 }
2230
2231 xormask += 3;
2232 }
2233
2234 andmask++;
2235 pcursor++;
2236 pmask++;
2237 }
2238 }
2239
2240 fg.red = fg.blue = fg.green = 0xffff;
2241 bg.red = bg.blue = bg.green = 0x0000;
2242 fg.flags = bg.flags = DoRed | DoBlue | DoGreen;
2243
2244 cursorglyph = ui_create_glyph(width, height, cursor);
2245 maskglyph = ui_create_glyph(width, height, mask);
2246
2247 xcursor =
2248 XCreatePixmapCursor(g_display, (Pixmap) cursorglyph,
2249 (Pixmap) maskglyph, &fg, &bg, x, y);
2250
2251 ui_destroy_glyph(maskglyph);
2252 ui_destroy_glyph(cursorglyph);
2253 xfree(mask);
2254 xfree(cursor);
2255 return (HCURSOR) xcursor;
2256 }
2257
2258 void
2259 ui_set_cursor(HCURSOR cursor)
2260 {
2261 g_current_cursor = (Cursor) cursor;
2262 XDefineCursor(g_display, g_wnd, g_current_cursor);
2263 ON_ALL_SEAMLESS_WINDOWS(XDefineCursor, (g_display, sw->wnd, g_current_cursor));
2264 }
2265
2266 void
2267 ui_destroy_cursor(HCURSOR cursor)
2268 {
2269 XFreeCursor(g_display, (Cursor) cursor);
2270 }
2271
2272 void
2273 ui_set_null_cursor(void)
2274 {
2275 ui_set_cursor(g_null_cursor);
2276 }
2277
2278 #define MAKE_XCOLOR(xc,c) \
2279 (xc)->red = ((c)->red << 8) | (c)->red; \
2280 (xc)->green = ((c)->green << 8) | (c)->green; \
2281 (xc)->blue = ((c)->blue << 8) | (c)->blue; \
2282 (xc)->flags = DoRed | DoGreen | DoBlue;
2283
2284
2285 HCOLOURMAP
2286 ui_create_colourmap(COLOURMAP * colours)
2287 {
2288 COLOURENTRY *entry;
2289 int i, ncolours = colours->ncolours;
2290 if (!g_owncolmap)
2291 {
2292 uint32 *map = (uint32 *) xmalloc(sizeof(*g_colmap) * ncolours);
2293 XColor xentry;
2294 XColor xc_cache[256];
2295 uint32 colour;
2296 int colLookup = 256;
2297 for (i = 0; i < ncolours; i++)
2298 {
2299 entry = &colours->colours[i];
2300 MAKE_XCOLOR(&xentry, entry);
2301
2302 if (XAllocColor(g_display, g_xcolmap, &xentry) == 0)
2303 {
2304 /* Allocation failed, find closest match. */
2305 int j = 256;
2306 int nMinDist = 3 * 256 * 256;
2307 long nDist = nMinDist;
2308
2309 /* only get the colors once */
2310 while (colLookup--)
2311 {
2312 xc_cache[colLookup].pixel = colLookup;
2313 xc_cache[colLookup].red = xc_cache[colLookup].green =
2314 xc_cache[colLookup].blue = 0;
2315 xc_cache[colLookup].flags = 0;
2316 XQueryColor(g_display,
2317 DefaultColormap(g_display,
2318 DefaultScreen(g_display)),
2319 &xc_cache[colLookup]);
2320 }
2321 colLookup = 0;
2322
2323 /* approximate the pixel */
2324 while (j--)
2325 {
2326 if (xc_cache[j].flags)
2327 {
2328 nDist = ((long) (xc_cache[j].red >> 8) -
2329 (long) (xentry.red >> 8)) *
2330 ((long) (xc_cache[j].red >> 8) -
2331 (long) (xentry.red >> 8)) +
2332 ((long) (xc_cache[j].green >> 8) -
2333 (long) (xentry.green >> 8)) *
2334 ((long) (xc_cache[j].green >> 8) -
2335 (long) (xentry.green >> 8)) +
2336 ((long) (xc_cache[j].blue >> 8) -
2337 (long) (xentry.blue >> 8)) *
2338 ((long) (xc_cache[j].blue >> 8) -
2339 (long) (xentry.blue >> 8));
2340 }
2341 if (nDist < nMinDist)
2342 {
2343 nMinDist = nDist;
2344 xentry.pixel = j;
2345 }
2346 }
2347 }
2348 colour = xentry.pixel;
2349
2350 /* update our cache */
2351 if (xentry.pixel < 256)
2352 {
2353 xc_cache[xentry.pixel].red = xentry.red;
2354 xc_cache[xentry.pixel].green = xentry.green;
2355 xc_cache[xentry.pixel].blue = xentry.blue;
2356
2357 }
2358
2359 map[i] = colour;
2360 }
2361 return map;
2362 }
2363 else
2364 {
2365 XColor *xcolours, *xentry;
2366 Colormap map;
2367
2368 xcolours = (XColor *) xmalloc(sizeof(XColor) * ncolours);
2369 for (i = 0; i < ncolours; i++)
2370 {
2371 entry = &colours->colours[i];
2372 xentry = &xcolours[i];
2373 xentry->pixel = i;
2374 MAKE_XCOLOR(xentry, entry);
2375 }
2376
2377 map = XCreateColormap(g_display, g_wnd, g_visual, AllocAll);
2378 XStoreColors(g_display, map, xcolours, ncolours);
2379
2380 xfree(xcolours);
2381 return (HCOLOURMAP) map;
2382 }
2383 }
2384
2385 void
2386 ui_destroy_colourmap(HCOLOURMAP map)
2387 {
2388 if (!g_owncolmap)
2389 xfree(map);
2390 else
2391 XFreeColormap(g_display, (Colormap) map);
2392 }
2393
2394 void
2395 ui_set_colourmap(HCOLOURMAP map)
2396 {
2397 if (!g_owncolmap)
2398 {
2399 if (g_colmap)
2400 xfree(g_colmap);
2401
2402 g_colmap = (uint32 *) map;
2403 }
2404 else
2405 {
2406 XSetWindowColormap(g_display, g_wnd, (Colormap) map);
2407 ON_ALL_SEAMLESS_WINDOWS(XSetWindowColormap, (g_display, sw->wnd, (Colormap) map));
2408 }
2409 }
2410
2411 void
2412 ui_set_clip(int x, int y, int cx, int cy)
2413 {
2414 g_clip_rectangle.x = x;
2415 g_clip_rectangle.y = y;
2416 g_clip_rectangle.width = cx;
2417 g_clip_rectangle.height = cy;
2418 XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
2419 }
2420
2421 void
2422 ui_reset_clip(void)
2423 {
2424 g_clip_rectangle.x = 0;
2425 g_clip_rectangle.y = 0;
2426 g_clip_rectangle.width = g_width;
2427 g_clip_rectangle.height = g_height;
2428 XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
2429 }
2430
2431 void
2432 ui_bell(void)
2433 {
2434 XBell(g_display, 0);
2435 }
2436
2437 void
2438 ui_destblt(uint8 opcode,
2439 /* dest */ int x, int y, int cx, int cy)
2440 {
2441 SET_FUNCTION(opcode);
2442 FILL_RECTANGLE(x, y, cx, cy);
2443 RESET_FUNCTION(opcode);
2444 }
2445
2446 static uint8 hatch_patterns[] = {
2447 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, /* 0 - bsHorizontal */
2448 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, /* 1 - bsVertical */
2449 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, /* 2 - bsFDiagonal */
2450 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /* 3 - bsBDiagonal */
2451 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08, /* 4 - bsCross */
2452 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 /* 5 - bsDiagCross */
2453 };
2454
2455 void
2456 ui_patblt(uint8 opcode,
2457 /* dest */ int x, int y, int cx, int cy,
2458 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2459 {
2460 Pixmap fill;
2461 uint8 i, ipattern[8];
2462
2463 SET_FUNCTION(opcode);
2464
2465 switch (brush->style)
2466 {
2467 case 0: /* Solid */
2468 SET_FOREGROUND(fgcolour);
2469 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2470 break;
2471
2472 case 2: /* Hatch */
2473 fill = (Pixmap) ui_create_glyph(8, 8,
2474 hatch_patterns + brush->pattern[0] * 8);
2475 SET_FOREGROUND(fgcolour);
2476 SET_BACKGROUND(bgcolour);
2477 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2478 XSetStipple(g_display, g_gc, fill);
2479 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2480 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2481 XSetFillStyle(g_display, g_gc, FillSolid);
2482 XSetTSOrigin(g_display, g_gc, 0, 0);
2483 ui_destroy_glyph((HGLYPH) fill);
2484 break;
2485
2486 case 3: /* Pattern */
2487 for (i = 0; i != 8; i++)
2488 ipattern[7 - i] = brush->pattern[i];
2489 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
2490 SET_FOREGROUND(bgcolour);
2491 SET_BACKGROUND(fgcolour);
2492 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2493 XSetStipple(g_display, g_gc, fill);
2494 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2495 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2496 XSetFillStyle(g_display, g_gc, FillSolid);
2497 XSetTSOrigin(g_display, g_gc, 0, 0);
2498 ui_destroy_glyph((HGLYPH) fill);
2499 break;
2500
2501 default:
2502 unimpl("brush %d\n", brush->style);
2503 }
2504
2505 RESET_FUNCTION(opcode);
2506
2507 if (g_ownbackstore)
2508 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2509 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2510 (g_display, g_ownbackstore ? g_backstore : g_wnd, sw->wnd, g_gc,
2511 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2512 }
2513
2514 void
2515 ui_screenblt(uint8 opcode,
2516 /* dest */ int x, int y, int cx, int cy,
2517 /* src */ int srcx, int srcy)
2518 {
2519 SET_FUNCTION(opcode);
2520 if (g_ownbackstore)
2521 {
2522 XCopyArea(g_display, g_Unobscured ? g_wnd : g_backstore,
2523 g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2524 XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
2525 }
2526 else
2527 {
2528 XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2529 }
2530
2531 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2532 (g_display, g_ownbackstore ? g_backstore : g_wnd,
2533 sw->wnd, g_gc, x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2534
2535 RESET_FUNCTION(opcode);
2536 }
2537
2538 void
2539 ui_memblt(uint8 opcode,
2540 /* dest */ int x, int y, int cx, int cy,
2541 /* src */ HBITMAP src, int srcx, int srcy)
2542 {
2543 SET_FUNCTION(opcode);
2544 XCopyArea(g_display, (Pixmap) src, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2545 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2546 (g_display, (Pixmap) src, sw->wnd, g_gc,
2547 srcx, srcy, cx, cy, x - sw->xoffset, y - sw->yoffset));
2548 if (g_ownbackstore)
2549 XCopyArea(g_display, (Pixmap) src, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
2550 RESET_FUNCTION(opcode);
2551 }
2552
2553 void
2554 ui_triblt(uint8 opcode,
2555 /* dest */ int x, int y, int cx, int cy,
2556 /* src */ HBITMAP src, int srcx, int srcy,
2557 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2558 {
2559 /* This is potentially difficult to do in general. Until someone
2560 comes up with a more efficient way of doing it I am using cases. */
2561
2562 switch (opcode)
2563 {
2564 case 0x69: /* PDSxxn */
2565 ui_memblt(ROP2_XOR, x, y, cx, cy, src, srcx, srcy);
2566 ui_patblt(ROP2_NXOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2567 break;
2568
2569 case 0xb8: /* PSDPxax */
2570 ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2571 ui_memblt(ROP2_AND, x, y, cx, cy, src, srcx, srcy);
2572 ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2573 break;
2574
2575 case 0xc0: /* PSa */
2576 ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2577 ui_patblt(ROP2_AND, x, y, cx, cy, brush, bgcolour, fgcolour);
2578 break;
2579
2580 default:
2581 unimpl("triblt 0x%x\n", opcode);
2582 ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2583 }
2584 }
2585
2586 void
2587 ui_line(uint8 opcode,
2588 /* dest */ int startx, int starty, int endx, int endy,
2589 /* pen */ PEN * pen)
2590 {
2591 SET_FUNCTION(opcode);
2592 SET_FOREGROUND(pen->colour);
2593 XDrawLine(g_display, g_wnd, g_gc, startx, starty, endx, endy);
2594 ON_ALL_SEAMLESS_WINDOWS(XDrawLine, (g_display, sw->wnd, g_gc,
2595 startx - sw->xoffset, starty - sw->yoffset,
2596 endx - sw->xoffset, endy - sw->yoffset));
2597 if (g_ownbackstore)
2598 XDrawLine(g_display, g_backstore, g_gc, startx, starty, endx, endy);
2599 RESET_FUNCTION(opcode);
2600 }
2601
2602 void
2603 ui_rect(
2604 /* dest */ int x, int y, int cx, int cy,
2605 /* brush */ int colour)
2606 {
2607 SET_FOREGROUND(colour);
2608 FILL_RECTANGLE(x, y, cx, cy);
2609 }
2610
2611 void
2612 ui_polygon(uint8 opcode,
2613 /* mode */ uint8 fillmode,
2614 /* dest */ POINT * point, int npoints,
2615 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2616 {
2617 uint8 style, i, ipattern[8];
2618 Pixmap fill;
2619
2620 SET_FUNCTION(opcode);
2621
2622 switch (fillmode)
2623 {
2624 case ALTERNATE:
2625 XSetFillRule(g_display, g_gc, EvenOddRule);
2626 break;
2627 case WINDING:
2628 XSetFillRule(g_display, g_gc, WindingRule);
2629 break;
2630 default:
2631 unimpl("fill mode %d\n", fillmode);
2632 }
2633
2634 if (brush)
2635 style = brush->style;
2636 else
2637 style = 0;
2638
2639 switch (style)
2640 {
2641 case 0: /* Solid */
2642 SET_FOREGROUND(fgcolour);
2643 FILL_POLYGON((XPoint *) point, npoints);
2644 break;
2645
2646 case 2: /* Hatch */
2647 fill = (Pixmap) ui_create_glyph(8, 8,
2648 hatch_patterns + brush->pattern[0] * 8);
2649 SET_FOREGROUND(fgcolour);
2650 SET_BACKGROUND(bgcolour);
2651 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2652 XSetStipple(g_display, g_gc, fill);
2653 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2654 FILL_POLYGON((XPoint *) point, npoints);
2655 XSetFillStyle(g_display, g_gc, FillSolid);
2656 XSetTSOrigin(g_display, g_gc, 0, 0);
2657 ui_destroy_glyph((HGLYPH) fill);
2658 break;
2659
2660 case 3: /* Pattern */
2661 for (i = 0; i != 8; i++)
2662 ipattern[7 - i] = brush->pattern[i];
2663 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
2664 SET_FOREGROUND(bgcolour);
2665 SET_BACKGROUND(fgcolour);
2666 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2667 XSetStipple(g_display, g_gc, fill);
2668 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2669 FILL_POLYGON((XPoint *) point, npoints);
2670 XSetFillStyle(g_display, g_gc, FillSolid);
2671 XSetTSOrigin(g_display, g_gc, 0, 0);
2672 ui_destroy_glyph((HGLYPH) fill);
2673 break;
2674
2675 default:
2676 unimpl("brush %d\n", brush->style);
2677 }
2678
2679 RESET_FUNCTION(opcode);
2680 }
2681
2682 void
2683 ui_polyline(uint8 opcode,
2684 /* dest */ POINT * points, int npoints,
2685 /* pen */ PEN * pen)
2686 {
2687 /* TODO: set join style */
2688 SET_FUNCTION(opcode);
2689 SET_FOREGROUND(pen->colour);
2690 XDrawLines(g_display, g_wnd, g_gc, (XPoint *) points, npoints, CoordModePrevious);
2691 if (g_ownbackstore)
2692 XDrawLines(g_display, g_backstore, g_gc, (XPoint *) points, npoints,
2693 CoordModePrevious);
2694
2695 ON_ALL_SEAMLESS_WINDOWS(seamless_XDrawLines,
2696 (sw->wnd, (XPoint *) points, npoints, sw->xoffset, sw->yoffset));
2697
2698 RESET_FUNCTION(opcode);
2699 }
2700
2701 void
2702 ui_ellipse(uint8 opcode,
2703 /* mode */ uint8 fillmode,
2704 /* dest */ int x, int y, int cx, int cy,
2705 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2706 {
2707 uint8 style, i, ipattern[8];
2708 Pixmap fill;
2709
2710 SET_FUNCTION(opcode);
2711
2712 if (brush)
2713 style = brush->style;
2714 else
2715 style = 0;
2716
2717 switch (style)
2718 {
2719 case 0: /* Solid */
2720 SET_FOREGROUND(fgcolour);
2721 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2722 break;
2723
2724 case 2: /* Hatch */
2725 fill = (Pixmap) ui_create_glyph(8, 8,
2726 hatch_patterns + brush->pattern[0] * 8);
2727 SET_FOREGROUND(fgcolour);
2728 SET_BACKGROUND(bgcolour);
2729 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2730 XSetStipple(g_display, g_gc, fill);
2731 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2732 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2733 XSetFillStyle(g_display, g_gc, FillSolid);
2734 XSetTSOrigin(g_display, g_gc, 0, 0);
2735 ui_destroy_glyph((HGLYPH) fill);
2736 break;
2737
2738 case 3: /* Pattern */
2739 for (i = 0; i != 8; i++)
2740 ipattern[7 - i] = brush->pattern[i];
2741 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
2742 SET_FOREGROUND(bgcolour);
2743 SET_BACKGROUND(fgcolour);
2744 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2745 XSetStipple(g_display, g_gc, fill);
2746 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2747 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2748 XSetFillStyle(g_display, g_gc, FillSolid);
2749 XSetTSOrigin(g_display, g_gc, 0, 0);
2750 ui_destroy_glyph((HGLYPH) fill);
2751 break;
2752
2753 default:
2754 unimpl("brush %d\n", brush->style);
2755 }
2756
2757 RESET_FUNCTION(opcode);
2758 }
2759
2760 /* warning, this function only draws on wnd or backstore, not both */
2761 void
2762 ui_draw_glyph(int mixmode,
2763 /* dest */ int x, int y, int cx, int cy,
2764 /* src */ HGLYPH glyph, int srcx, int srcy,
2765 int bgcolour, int fgcolour)
2766 {
2767 SET_FOREGROUND(fgcolour);
2768 SET_BACKGROUND(bgcolour);
2769
2770 XSetFillStyle(g_display, g_gc,
2771 (mixmode == MIX_TRANSPARENT) ? FillStippled : FillOpaqueStippled);
2772 XSetStipple(g_display, g_gc, (Pixmap) glyph);
2773 XSetTSOrigin(g_display, g_gc, x, y);
2774
2775 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2776
2777 XSetFillStyle(g_display, g_gc, FillSolid);
2778 }
2779
2780 #define DO_GLYPH(ttext,idx) \
2781 {\
2782 glyph = cache_get_font (font, ttext[idx]);\
2783 if (!(flags & TEXT2_IMPLICIT_X))\
2784 {\
2785 xyoffset = ttext[++idx];\
2786 if ((xyoffset & 0x80))\
2787 {\
2788 if (flags & TEXT2_VERTICAL)\
2789 y += ttext[idx+1] | (ttext[idx+2] << 8);\
2790 else\
2791 x += ttext[idx+1] | (ttext[idx+2] << 8);\
2792 idx += 2;\
2793 }\
2794 else\
2795 {\
2796 if (flags & TEXT2_VERTICAL)\
2797 y += xyoffset;\
2798 else\
2799 x += xyoffset;\
2800 }\
2801 }\
2802 if (glyph != NULL)\
2803 {\
2804 x1 = x + glyph->offset;\
2805 y1 = y + glyph->baseline;\
2806 XSetStipple(g_display, g_gc, (Pixmap) glyph->pixmap);\
2807 XSetTSOrigin(g_display, g_gc, x1, y1);\
2808 FILL_RECTANGLE_BACKSTORE(x1, y1, glyph->width, glyph->height);\
2809 if (flags & TEXT2_IMPLICIT_X)\
2810 x += glyph->width;\
2811 }\
2812 }
2813
2814 void
2815 ui_draw_text(uint8 font, uint8 flags, uint8 opcode, int mixmode, int x, int y,
2816 int clipx, int clipy, int clipcx, int clipcy,
2817 int boxx, int boxy, int boxcx, int boxcy, BRUSH * brush,
2818 int bgcolour, int fgcolour, uint8 * text, uint8 length)
2819 {
2820 /* TODO: use brush appropriately */
2821
2822 FONTGLYPH *glyph;
2823 int i, j, xyoffset, x1, y1;
2824 DATABLOB *entry;
2825
2826 SET_FOREGROUND(bgcolour);
2827
2828 /* Sometimes, the boxcx value is something really large, like
2829 32691. This makes XCopyArea fail with Xvnc. The code below
2830 is a quick fix. */
2831 if (boxx + boxcx > g_width)
2832 boxcx = g_width - boxx;
2833
2834 if (boxcx > 1)
2835 {
2836 FILL_RECTANGLE_BACKSTORE(boxx, boxy, boxcx, boxcy);
2837 }
2838 else if (mixmode == MIX_OPAQUE)
2839 {
2840 FILL_RECTANGLE_BACKSTORE(clipx, clipy, clipcx, clipcy);
2841 }
2842
2843 SET_FOREGROUND(fgcolour);
2844 SET_BACKGROUND(bgcolour);
2845 XSetFillStyle(g_display, g_gc, FillStippled);
2846
2847 /* Paint text, character by character */
2848 for (i = 0; i < length;)
2849 {
2850 switch (text[i])
2851 {
2852 case 0xff:
2853 /* At least two bytes needs to follow */
2854 if (i + 3 > length)
2855 {
2856 warning("Skipping short 0xff command:");
2857 for (j = 0; j < length; j++)
2858 fprintf(stderr, "%02x ", text[j]);
2859 fprintf(stderr, "\n");
2860 i = length = 0;
2861 break;
2862 }
2863 cache_put_text(text[i + 1], text, text[i + 2]);
2864 i += 3;
2865 length -= i;
2866 /* this will move pointer from start to first character after FF command */
2867 text = &(text[i]);
2868 i = 0;
2869 break;
2870
2871 case 0xfe:
2872 /* At least one byte needs to follow */
2873 if (i + 2 > length)
2874 {
2875 warning("Skipping short 0xfe command:");
2876 for (j = 0; j < length; j++)
2877 fprintf(stderr, "%02x ", text[j]);
2878 fprintf(stderr, "\n");
2879 i = length = 0;
2880 break;
2881 }
2882 entry = cache_get_text(text[i + 1]);
2883 if (entry->data != NULL)
2884 {
2885 if ((((uint8 *) (entry->data))[1] == 0)
2886 && (!(flags & TEXT2_IMPLICIT_X)) && (i + 2 < length))
2887 {
2888 if (flags & TEXT2_VERTICAL)
2889 y += text[i + 2];
2890 else
2891 x += text[i + 2];
2892 }
2893 for (j = 0; j < entry->size; j++)
2894 DO_GLYPH(((uint8 *) (entry->data)), j);
2895 }
2896 if (i + 2 < length)
2897 i += 3;
2898 else
2899 i += 2;
2900 length -= i;
2901 /* this will move pointer from start to first character after FE command */
2902 text = &(text[i]);
2903 i = 0;
2904 break;
2905
2906 default:
2907 DO_GLYPH(text, i);
2908 i++;
2909 break;
2910 }
2911 }
2912
2913 XSetFillStyle(g_display, g_gc, FillSolid);
2914
2915 if (g_ownbackstore)
2916 {
2917 if (boxcx > 1)
2918 {
2919 XCopyArea(g_display, g_backstore, g_wnd, g_gc, boxx,
2920 boxy, boxcx, boxcy, boxx, boxy);
2921 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2922 (g_display, g_backstore, sw->wnd, g_gc,
2923 boxx, boxy,
2924 boxcx, boxcy,
2925 boxx - sw->xoffset, boxy - sw->yoffset));
2926 }
2927 else
2928 {
2929 XCopyArea(g_display, g_backstore, g_wnd, g_gc, clipx,
2930 clipy, clipcx, clipcy, clipx, clipy);
2931 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2932 (g_display, g_backstore, sw->wnd, g_gc,
2933 clipx, clipy,
2934 clipcx, clipcy, clipx - sw->xoffset,
2935 clipy - sw->yoffset));
2936 }
2937 }
2938 }
2939
2940 void
2941 ui_desktop_save(uint32 offset, int x, int y, int cx, int cy)
2942 {
2943 Pixmap pix;
2944 XImage *image;
2945
2946 if (g_ownbackstore)
2947 {
2948 image = XGetImage(g_display, g_backstore, x, y, cx, cy, AllPlanes, ZPixmap);
2949 }
2950 else
2951 {
2952 pix = XCreatePixmap(g_display, g_wnd, cx, cy, g_depth);
2953 XCopyArea(g_display, g_wnd, pix, g_gc, x, y, cx, cy, 0, 0);
2954 image = XGetImage(g_display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap);
2955 XFreePixmap(g_display, pix);
2956 }
2957
2958 offset *= g_bpp / 8;
2959 cache_put_desktop(offset, cx, cy, image->bytes_per_line, g_bpp / 8, (uint8 *) image->data);
2960
2961 XDestroyImage(image);
2962 }
2963
2964 void
2965 ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy)
2966 {
2967 XImage *image;
2968 uint8 *data;
2969
2970 offset *= g_bpp / 8;
2971 data = cache_get_desktop(offset, cx, cy, g_bpp / 8);
2972 if (data == NULL)
2973 return;
2974
2975 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
2976 (char *) data, cx, cy, BitmapPad(g_display), cx * g_bpp / 8);
2977
2978 if (g_ownbackstore)
2979 {
2980 XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
2981 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2982 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2983 (g_display, g_backstore, sw->wnd, g_gc,
2984 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2985 }
2986 else
2987 {
2988 XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
2989 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2990 (g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy,
2991 x - sw->xoffset, y - sw->yoffset));
2992 }
2993
2994 XFree(image);
2995 }
2996
2997 /* these do nothing here but are used in uiports */
2998 void
2999 ui_begin_update(void)
3000 {
3001 }
3002
3003 void
3004 ui_end_update(void)
3005 {
3006 }
3007
3008 void
3009 ui_seamless_begin()
3010 {
3011 if (!g_seamless_rdp)
3012 return;
3013
3014 if (g_seamless_started)
3015 return;
3016
3017 g_seamless_started = True;
3018
3019 ui_seamless_toggle();
3020 }
3021
3022 void
3023 ui_seamless_toggle()
3024 {
3025 if (!g_seamless_rdp)
3026 return;
3027
3028 if (!g_seamless_started)
3029 return;
3030
3031 if (g_seamless_active)
3032 {
3033 /* Deactivate */
3034 while (g_seamless_windows)
3035 {
3036 XDestroyWindow(g_display, g_seamless_windows->wnd);
3037 seamless_remove_window(g_seamless_windows);
3038 }
3039 XMapWindow(g_display, g_wnd);
3040 }
3041 else
3042 {
3043 /* Activate */
3044 XUnmapWindow(g_display, g_wnd);
3045 seamless_send_sync();
3046 }
3047
3048 g_seamless_active = !g_seamless_active;
3049 }
3050
3051 void
3052 ui_seamless_create_window(unsigned long id, unsigned long parent, unsigned long flags)
3053 {
3054 Window wnd;
3055 XSetWindowAttributes attribs;
3056 XClassHint *classhints;
3057 XSizeHints *sizehints;
3058 long input_mask;
3059 seamless_window *sw, *sw_parent;
3060
3061 if (!g_seamless_active)
3062 return;
3063
3064 /* Ignore CREATEs for existing windows */
3065 sw = seamless_get_window_by_id(id);
3066 if (sw)
3067 return;
3068
3069 get_window_attribs(&attribs);
3070 attribs.override_redirect = False;
3071
3072 wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), -1, -1, 1, 1, 0, g_depth,
3073 InputOutput, g_visual,
3074 CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
3075 CWBorderPixel, &attribs);
3076
3077 XStoreName(g_display, wnd, "SeamlessRDP");
3078 ewmh_set_wm_name(wnd, "SeamlessRDP");
3079
3080 mwm_hide_decorations(wnd);
3081
3082 classhints = XAllocClassHint();
3083 if (classhints != NULL)
3084 {
3085 classhints->res_name = "rdesktop";
3086 classhints->res_class = "SeamlessRDP";
3087 XSetClassHint(g_display, wnd, classhints);
3088 XFree(classhints);
3089 }
3090
3091 /* WM_NORMAL_HINTS */
3092 sizehints = XAllocSizeHints();
3093 if (sizehints != NULL)
3094 {
3095 sizehints->flags = USPosition;
3096 XSetWMNormalHints(g_display, wnd, sizehints);
3097 XFree(sizehints);
3098 }
3099
3100 /* Set WM_TRANSIENT_FOR, if necessary */
3101 if ((parent != 0x00000000) && (parent != 0xFFFFFFFF))
3102 {
3103 sw_parent = seamless_get_window_by_id(parent);
3104 if (sw_parent)
3105 XSetTransientForHint(g_display, wnd, sw_parent->wnd);
3106 else
3107 warning("ui_seamless_create_window: No parent window 0x%lx\n", parent);
3108 }
3109
3110
3111 /* FIXME: Support for Input Context:s */
3112
3113 get_input_mask(&input_mask);
3114 input_mask |= PropertyChangeMask;
3115
3116 XSelectInput(g_display, wnd, input_mask);
3117
3118 /* handle the WM_DELETE_WINDOW protocol. FIXME: When killing a
3119 seamless window, we could try to close the window on the
3120 serverside, instead of terminating rdesktop */
3121 XSetWMProtocols(g_display, wnd, &g_kill_atom, 1);
3122
3123 sw = malloc(sizeof(seamless_window));
3124 sw->wnd = wnd;
3125 sw->id = id;
3126 sw->parent = parent;
3127 sw->xoffset = 0;
3128 sw->yoffset = 0;
3129 sw->width = 0;
3130 sw->height = 0;
3131 sw->state = SEAMLESSRDP_NOTYETMAPPED;
3132 sw->desktop = 0;
3133 sw->next = g_seamless_windows;
3134 g_seamless_windows = sw;
3135 }
3136
3137
3138 void
3139 ui_seamless_destroy_window(unsigned long id, unsigned long flags)
3140 {
3141 seamless_window *sw;
3142
3143 if (!g_seamless_active)
3144 return;
3145
3146 sw = seamless_get_window_by_id(id);
3147 if (!sw)
3148 {
3149 warning("ui_seamless_destroy_window: No information for window 0x%lx\n", id);
3150 return;
3151 }
3152
3153 XDestroyWindow(g_display, sw->wnd);
3154 seamless_remove_window(sw);
3155 }
3156
3157
3158 void
3159 ui_seamless_move_window(unsigned long id, int x, int y, int width, int height, unsigned long flags)
3160 {
3161 seamless_window *sw;
3162
3163 if (!g_seamless_active)
3164 return;
3165
3166 sw = seamless_get_window_by_id(id);
3167 if (!sw)
3168 {
3169 warning("ui_seamless_move_window: No information for window 0x%lx\n", id);
3170 return;
3171 }
3172
3173 if (!width || !height)
3174 /* X11 windows must be at least 1x1 */
3175 return;
3176
3177 /* About MAX and MIN: Windows allows moving a window outside
3178 the desktop. This happens, for example, when maximizing an
3179 application. In this case, the position is set to something
3180 like -4,-4,1288,1032. Many WMs does not allow windows
3181 outside the desktop, however. Therefore, clip the window
3182 ourselves. */
3183 sw->xoffset = MAX(0, x);
3184 sw->yoffset = MAX(0, y);
3185 sw->width = MIN(MIN(width, width + x), g_width - sw->xoffset);
3186 sw->height = MIN(MIN(height, height + y), g_height - sw->yoffset);
3187
3188 /* If we move the window in a maximized state, then KDE won't
3189 accept restoration */
3190 switch (sw->state)
3191 {
3192 case SEAMLESSRDP_MINIMIZED:
3193 case SEAMLESSRDP_MAXIMIZED:
3194 return;
3195 }
3196
3197 /* FIXME: Perhaps use ewmh_net_moveresize_window instead */
3198 XMoveResizeWindow(g_display, sw->wnd, sw->xoffset, sw->yoffset, sw->width, sw->height);
3199 }
3200
3201
3202 void
3203 ui_seamless_settitle(unsigned long id, const char *title, unsigned long flags)
3204 {
3205 seamless_window *sw;
3206
3207 if (!g_seamless_active)
3208 return;
3209
3210 sw = seamless_get_window_by_id(id);
3211 if (!sw)
3212 {
3213 warning("ui_seamless_settitle: No information for window 0x%lx\n", id);
3214 return;
3215 }
3216
3217 /* FIXME: Might want to convert the name for non-EWMH WMs */
3218 XStoreName(g_display, sw->wnd, title);
3219 ewmh_set_wm_name(sw->wnd, title);
3220 }
3221
3222
3223 void
3224 ui_seamless_setstate(unsigned long id, unsigned int state, unsigned long flags)
3225 {
3226 seamless_window *sw;
3227
3228 if (!g_seamless_active)
3229 return;
3230
3231 sw = seamless_get_window_by_id(id);
3232 if (!sw)
3233 {
3234 warning("ui_seamless_setstate: No information for window 0x%lx\n", id);
3235 return;
3236 }
3237
3238 switch (state)
3239 {
3240 case SEAMLESSRDP_NORMAL:
3241 case SEAMLESSRDP_MAXIMIZED:
3242 ewmh_change_state(sw->wnd, state);
3243 XMapWindow(g_display, sw->wnd);
3244 break;
3245 case SEAMLESSRDP_MINIMIZED:
3246 /* EWMH says: "if an Application asks to toggle _NET_WM_STATE_HIDDEN
3247 the Window Manager should probably just ignore the request, since
3248 _NET_WM_STATE_HIDDEN is a function of some other aspect of the window
3249 such as minimization, rather than an independent state." Besides,
3250 XIconifyWindow is easier. */
3251 if (sw->state == SEAMLESSRDP_NOTYETMAPPED)
3252 {
3253 XWMHints *hints;
3254 hints = XAllocWMHints();
3255 hints->flags = StateHint;
3256 hints->initial_state = IconicState;
3257 XSetWMHints(g_display, sw->wnd, hints);
3258 XFree(hints);
3259 XMapWindow(g_display, sw->wnd);
3260 }
3261 else
3262 XIconifyWindow(g_display, sw->wnd, DefaultScreen(g_display));
3263 break;
3264 default:
3265 warning("SeamlessRDP: Invalid state %d\n", state);
3266 break;
3267 }
3268
3269 /* Handle popups without parents through some ewm hints */
3270 if ((sw->state == SEAMLESSRDP_NOTYETMAPPED) && (sw->parent == 0xFFFFFFFF))
3271 ewmh_set_window_popup(sw->wnd);
3272
3273 sw->state = state;
3274 }
3275
3276
3277 void
3278 ui_seamless_syncbegin(unsigned long flags)
3279 {
3280 if (!g_seamless_active)
3281 return;
3282
3283 /* Destroy all seamless windows */
3284 while (g_seamless_windows)
3285 {
3286 XDestroyWindow(g_display, g_seamless_windows->wnd);
3287 seamless_remove_window(g_seamless_windows);
3288 }
3289 }

  ViewVC Help
Powered by ViewVC 1.1.26