/[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 1143 - (show annotations)
Thu Mar 16 08:41:53 2006 UTC (18 years, 2 months ago) by ossman_
File MIME type: text/plain
File size: 76236 byte(s)
Set _NET_WM_STATE atoms via the window manager (as required by the spec.).
Unfortunately, this means we have to wait for the window to leave the
withdrawn state.

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 XWMHints *hints;
59 int xoffset, yoffset;
60 int width, height;
61 int state; /* normal/minimized/maximized. */
62 unsigned int desktop;
63 struct _seamless_window *next;
64 } seamless_window;
65 static seamless_window *g_seamless_windows = NULL;
66 extern BOOL g_seamless_rdp;
67
68 extern uint32 g_embed_wnd;
69 BOOL g_enable_compose = False;
70 BOOL g_Unobscured; /* used for screenblt */
71 static GC g_gc = NULL;
72 static GC g_create_bitmap_gc = NULL;
73 static GC g_create_glyph_gc = NULL;
74 static XRectangle g_clip_rectangle;
75 static Visual *g_visual;
76 /* Color depth of the X11 visual of our window (e.g. 24 for True Color R8G8B visual).
77 This may be 32 for R8G8B8 visuals, and then the rest of the bits are undefined
78 as far as we're concerned. */
79 static int g_depth;
80 /* Bits-per-Pixel of the pixmaps we'll be using to draw on our window.
81 This may be larger than g_depth, in which case some of the bits would
82 be kept solely for alignment (e.g. 32bpp pixmaps on a 24bpp visual). */
83 static int g_bpp;
84 static XIM g_IM;
85 static XIC g_IC;
86 static XModifierKeymap *g_mod_map;
87 static Cursor g_current_cursor;
88 static HCURSOR g_null_cursor = NULL;
89 static Atom g_protocol_atom, g_kill_atom;
90 extern Atom g_net_wm_state_atom;
91 extern Atom g_net_wm_desktop_atom;
92 static BOOL g_focused;
93 static BOOL g_mouse_in_wnd;
94 /* Indicates that:
95 1) visual has 15, 16 or 24 depth and the same color channel masks
96 as its RDP equivalent (implies X server is LE),
97 2) host is LE
98 This will trigger an optimization whose real value is questionable.
99 */
100 static BOOL g_compatible_arch;
101 /* Indicates whether RDP's bitmaps and our XImages have the same
102 binary format. If so, we can avoid an expensive translation.
103 Note that this can be true when g_compatible_arch is false,
104 e.g.:
105
106 RDP(LE) <-> host(BE) <-> X-Server(LE)
107
108 ('host' is the machine running rdesktop; the host simply memcpy's
109 so its endianess doesn't matter)
110 */
111 static BOOL g_no_translate_image = False;
112
113 /* endianness */
114 static BOOL g_host_be;
115 static BOOL g_xserver_be;
116 static int g_red_shift_r, g_blue_shift_r, g_green_shift_r;
117 static int g_red_shift_l, g_blue_shift_l, g_green_shift_l;
118
119 /* software backing store */
120 extern BOOL g_ownbackstore;
121 static Pixmap g_backstore = 0;
122
123 /* Moving in single app mode */
124 static BOOL g_moving_wnd;
125 static int g_move_x_offset = 0;
126 static int g_move_y_offset = 0;
127 static BOOL g_using_full_workarea = False;
128
129 #ifdef WITH_RDPSND
130 extern int g_dsp_fd;
131 extern BOOL g_dsp_busy;
132 extern BOOL g_rdpsnd;
133 #endif
134
135 /* MWM decorations */
136 #define MWM_HINTS_DECORATIONS (1L << 1)
137 #define PROP_MOTIF_WM_HINTS_ELEMENTS 5
138 typedef struct
139 {
140 unsigned long flags;
141 unsigned long functions;
142 unsigned long decorations;
143 long inputMode;
144 unsigned long status;
145 }
146 PropMotifWmHints;
147
148 typedef struct
149 {
150 uint32 red;
151 uint32 green;
152 uint32 blue;
153 }
154 PixelColour;
155
156 #define ON_ALL_SEAMLESS_WINDOWS(func, args) \
157 do { \
158 seamless_window *sw; \
159 XRectangle rect; \
160 for (sw = g_seamless_windows; sw; sw = sw->next) { \
161 rect.x = g_clip_rectangle.x - sw->xoffset; \
162 rect.y = g_clip_rectangle.y - sw->yoffset; \
163 rect.width = g_clip_rectangle.width; \
164 rect.height = g_clip_rectangle.height; \
165 XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded); \
166 func args; \
167 } \
168 XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded); \
169 } while (0)
170
171 static void
172 seamless_XFillPolygon(Drawable d, XPoint * points, int npoints, int xoffset, int yoffset)
173 {
174 points[0].x -= xoffset;
175 points[0].y -= yoffset;
176 XFillPolygon(g_display, d, g_gc, points, npoints, Complex, CoordModePrevious);
177 points[0].x += xoffset;
178 points[0].y += yoffset;
179 }
180
181 static void
182 seamless_XDrawLines(Drawable d, XPoint * points, int npoints, int xoffset, int yoffset)
183 {
184 points[0].x -= xoffset;
185 points[0].y -= yoffset;
186 XDrawLines(g_display, d, g_gc, points, npoints, CoordModePrevious);
187 points[0].x += xoffset;
188 points[0].y += yoffset;
189 }
190
191 #define FILL_RECTANGLE(x,y,cx,cy)\
192 { \
193 XFillRectangle(g_display, g_wnd, g_gc, x, y, cx, cy); \
194 ON_ALL_SEAMLESS_WINDOWS(XFillRectangle, (g_display, sw->wnd, g_gc, x-sw->xoffset, y-sw->yoffset, cx, cy)); \
195 if (g_ownbackstore) \
196 XFillRectangle(g_display, g_backstore, g_gc, x, y, cx, cy); \
197 }
198
199 #define FILL_RECTANGLE_BACKSTORE(x,y,cx,cy)\
200 { \
201 XFillRectangle(g_display, g_ownbackstore ? g_backstore : g_wnd, g_gc, x, y, cx, cy); \
202 }
203
204 #define FILL_POLYGON(p,np)\
205 { \
206 XFillPolygon(g_display, g_wnd, g_gc, p, np, Complex, CoordModePrevious); \
207 if (g_ownbackstore) \
208 XFillPolygon(g_display, g_backstore, g_gc, p, np, Complex, CoordModePrevious); \
209 ON_ALL_SEAMLESS_WINDOWS(seamless_XFillPolygon, (sw->wnd, p, np, sw->xoffset, sw->yoffset)); \
210 }
211
212 #define DRAW_ELLIPSE(x,y,cx,cy,m)\
213 { \
214 switch (m) \
215 { \
216 case 0: /* Outline */ \
217 XDrawArc(g_display, g_wnd, g_gc, x, y, cx, cy, 0, 360*64); \
218 ON_ALL_SEAMLESS_WINDOWS(XDrawArc, (g_display, sw->wnd, g_gc, x-sw->xoffset, y-sw->yoffset, cx, cy, 0, 360*64)); \
219 if (g_ownbackstore) \
220 XDrawArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \
221 break; \
222 case 1: /* Filled */ \
223 XFillArc(g_display, g_wnd, g_gc, x, y, \
224 cx, cy, 0, 360*64); \
225 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)); \
226 if (g_ownbackstore) \
227 XFillArc(g_display, g_backstore, g_gc, x, y, cx, cy, 0, 360*64); \
228 break; \
229 } \
230 }
231
232 /* colour maps */
233 extern BOOL g_owncolmap;
234 static Colormap g_xcolmap;
235 static uint32 *g_colmap = NULL;
236
237 #define TRANSLATE(col) ( g_server_depth != 8 ? translate_colour(col) : g_owncolmap ? col : g_colmap[col] )
238 #define SET_FOREGROUND(col) XSetForeground(g_display, g_gc, TRANSLATE(col));
239 #define SET_BACKGROUND(col) XSetBackground(g_display, g_gc, TRANSLATE(col));
240
241 static int rop2_map[] = {
242 GXclear, /* 0 */
243 GXnor, /* DPon */
244 GXandInverted, /* DPna */
245 GXcopyInverted, /* Pn */
246 GXandReverse, /* PDna */
247 GXinvert, /* Dn */
248 GXxor, /* DPx */
249 GXnand, /* DPan */
250 GXand, /* DPa */
251 GXequiv, /* DPxn */
252 GXnoop, /* D */
253 GXorInverted, /* DPno */
254 GXcopy, /* P */
255 GXorReverse, /* PDno */
256 GXor, /* DPo */
257 GXset /* 1 */
258 };
259
260 #define SET_FUNCTION(rop2) { if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, rop2_map[rop2]); }
261 #define RESET_FUNCTION(rop2) { if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, GXcopy); }
262
263 static seamless_window *
264 seamless_get_window_by_id(unsigned long id)
265 {
266 seamless_window *sw;
267 for (sw = g_seamless_windows; sw; sw = sw->next)
268 {
269 if (sw->id == id)
270 return sw;
271 }
272 return NULL;
273 }
274
275
276 static seamless_window *
277 seamless_get_window_by_wnd(Window wnd)
278 {
279 seamless_window *sw;
280 for (sw = g_seamless_windows; sw; sw = sw->next)
281 {
282 if (sw->wnd == wnd)
283 return sw;
284 }
285 return NULL;
286 }
287
288
289 static void
290 seamless_remove_window(seamless_window * win)
291 {
292 seamless_window *sw, **prevnext = &g_seamless_windows;
293 for (sw = g_seamless_windows; sw; sw = sw->next)
294 {
295 if (sw == win)
296 {
297 *prevnext = sw->next;
298 XFree(sw->hints);
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 if (!g_seamless_rdp)
1571 {
1572 XMapWindow(g_display, g_wnd);
1573 /* wait for VisibilityNotify */
1574 do
1575 {
1576 XMaskEvent(g_display, VisibilityChangeMask, &xevent);
1577 }
1578 while (xevent.type != VisibilityNotify);
1579 g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
1580 }
1581
1582 g_focused = False;
1583 g_mouse_in_wnd = False;
1584
1585 /* handle the WM_DELETE_WINDOW protocol */
1586 g_protocol_atom = XInternAtom(g_display, "WM_PROTOCOLS", True);
1587 g_kill_atom = XInternAtom(g_display, "WM_DELETE_WINDOW", True);
1588 XSetWMProtocols(g_display, g_wnd, &g_kill_atom, 1);
1589
1590 /* create invisible 1x1 cursor to be used as null cursor */
1591 if (g_null_cursor == NULL)
1592 g_null_cursor = ui_create_cursor(0, 0, 1, 1, null_pointer_mask, null_pointer_data);
1593
1594 return True;
1595 }
1596
1597 void
1598 ui_resize_window()
1599 {
1600 XSizeHints *sizehints;
1601 Pixmap bs;
1602
1603 sizehints = XAllocSizeHints();
1604 if (sizehints)
1605 {
1606 sizehints->flags = PMinSize | PMaxSize;
1607 sizehints->min_width = sizehints->max_width = g_width;
1608 sizehints->min_height = sizehints->max_height = g_height;
1609 XSetWMNormalHints(g_display, g_wnd, sizehints);
1610 XFree(sizehints);
1611 }
1612
1613 if (!(g_fullscreen || g_embed_wnd))
1614 {
1615 XResizeWindow(g_display, g_wnd, g_width, g_height);
1616 }
1617
1618 /* create new backstore pixmap */
1619 if (g_backstore != 0)
1620 {
1621 bs = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
1622 XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
1623 XFillRectangle(g_display, bs, g_gc, 0, 0, g_width, g_height);
1624 XCopyArea(g_display, g_backstore, bs, g_gc, 0, 0, g_width, g_height, 0, 0);
1625 XFreePixmap(g_display, g_backstore);
1626 g_backstore = bs;
1627 }
1628 }
1629
1630 void
1631 ui_destroy_window(void)
1632 {
1633 if (g_IC != NULL)
1634 XDestroyIC(g_IC);
1635
1636 XDestroyWindow(g_display, g_wnd);
1637 }
1638
1639 void
1640 xwin_toggle_fullscreen(void)
1641 {
1642 Pixmap contents = 0;
1643
1644 if (g_seamless_rdp)
1645 /* Turn off SeamlessRDP mode */
1646 ui_seamless_toggle();
1647
1648 if (!g_ownbackstore)
1649 {
1650 /* need to save contents of window */
1651 contents = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
1652 XCopyArea(g_display, g_wnd, contents, g_gc, 0, 0, g_width, g_height, 0, 0);
1653 }
1654
1655 ui_destroy_window();
1656 g_fullscreen = !g_fullscreen;
1657 ui_create_window();
1658
1659 XDefineCursor(g_display, g_wnd, g_current_cursor);
1660
1661 if (!g_ownbackstore)
1662 {
1663 XCopyArea(g_display, contents, g_wnd, g_gc, 0, 0, g_width, g_height, 0, 0);
1664 XFreePixmap(g_display, contents);
1665 }
1666 }
1667
1668 static void
1669 handle_button_event(XEvent xevent, BOOL down)
1670 {
1671 uint16 button, flags = 0;
1672 g_last_gesturetime = xevent.xbutton.time;
1673 button = xkeymap_translate_button(xevent.xbutton.button);
1674 if (button == 0)
1675 return;
1676
1677 if (down)
1678 flags = MOUSE_FLAG_DOWN;
1679
1680 /* Stop moving window when button is released, regardless of cursor position */
1681 if (g_moving_wnd && (xevent.type == ButtonRelease))
1682 g_moving_wnd = False;
1683
1684 /* If win_button_size is nonzero, enable single app mode */
1685 if (xevent.xbutton.y < g_win_button_size)
1686 {
1687 /* Check from right to left: */
1688 if (xevent.xbutton.x >= g_width - g_win_button_size)
1689 {
1690 /* The close button, continue */
1691 ;
1692 }
1693 else if (xevent.xbutton.x >= g_width - g_win_button_size * 2)
1694 {
1695 /* The maximize/restore button. Do not send to
1696 server. It might be a good idea to change the
1697 cursor or give some other visible indication
1698 that rdesktop inhibited this click */
1699 if (xevent.type == ButtonPress)
1700 return;
1701 }
1702 else if (xevent.xbutton.x >= g_width - g_win_button_size * 3)
1703 {
1704 /* The minimize button. Iconify window. */
1705 if (xevent.type == ButtonRelease)
1706 {
1707 /* Release the mouse button outside the minimize button, to prevent the
1708 actual minimazation to happen */
1709 rdp_send_input(time(NULL), RDP_INPUT_MOUSE, button, 1, 1);
1710 XIconifyWindow(g_display, g_wnd, DefaultScreen(g_display));
1711 return;
1712 }
1713 }
1714 else if (xevent.xbutton.x <= g_win_button_size)
1715 {
1716 /* The system menu. Ignore. */
1717 if (xevent.type == ButtonPress)
1718 return;
1719 }
1720 else
1721 {
1722 /* The title bar. */
1723 if (xevent.type == ButtonPress)
1724 {
1725 if (!g_fullscreen && g_hide_decorations && !g_using_full_workarea)
1726 {
1727 g_moving_wnd = True;
1728 g_move_x_offset = xevent.xbutton.x;
1729 g_move_y_offset = xevent.xbutton.y;
1730 }
1731 return;
1732 }
1733 }
1734 }
1735
1736 if (xevent.xmotion.window == g_wnd)
1737 {
1738 rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
1739 flags | button, xevent.xbutton.x, xevent.xbutton.y);
1740 }
1741 else
1742 {
1743 /* SeamlessRDP */
1744 rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
1745 flags | button, xevent.xbutton.x_root, xevent.xbutton.y_root);
1746 }
1747 }
1748
1749 /* Process events in Xlib queue
1750 Returns 0 after user quit, 1 otherwise */
1751 static int
1752 xwin_process_events(void)
1753 {
1754 XEvent xevent;
1755 KeySym keysym;
1756 uint32 ev_time;
1757 char str[256];
1758 Status status;
1759 int events = 0;
1760 seamless_window *sw;
1761
1762 while ((XPending(g_display) > 0) && events++ < 20)
1763 {
1764 XNextEvent(g_display, &xevent);
1765
1766 if ((g_IC != NULL) && (XFilterEvent(&xevent, None) == True))
1767 {
1768 DEBUG_KBD(("Filtering event\n"));
1769 continue;
1770 }
1771
1772 switch (xevent.type)
1773 {
1774 case VisibilityNotify:
1775 if (xevent.xvisibility.window == g_wnd)
1776 g_Unobscured =
1777 xevent.xvisibility.state == VisibilityUnobscured;
1778
1779 break;
1780 case ClientMessage:
1781 /* the window manager told us to quit */
1782 if ((xevent.xclient.message_type == g_protocol_atom)
1783 && ((Atom) xevent.xclient.data.l[0] == g_kill_atom))
1784 /* Quit */
1785 return 0;
1786 break;
1787
1788 case KeyPress:
1789 g_last_gesturetime = xevent.xkey.time;
1790 if (g_IC != NULL)
1791 /* Multi_key compatible version */
1792 {
1793 XmbLookupString(g_IC,
1794 &xevent.xkey, str, sizeof(str), &keysym,
1795 &status);
1796 if (!((status == XLookupKeySym) || (status == XLookupBoth)))
1797 {
1798 error("XmbLookupString failed with status 0x%x\n",
1799 status);
1800 break;
1801 }
1802 }
1803 else
1804 {
1805 /* Plain old XLookupString */
1806 DEBUG_KBD(("\nNo input context, using XLookupString\n"));
1807 XLookupString((XKeyEvent *) & xevent,
1808 str, sizeof(str), &keysym, NULL);
1809 }
1810
1811 DEBUG_KBD(("KeyPress for keysym (0x%lx, %s)\n", keysym,
1812 get_ksname(keysym)));
1813
1814 ev_time = time(NULL);
1815 if (handle_special_keys(keysym, xevent.xkey.state, ev_time, True))
1816 break;
1817
1818 xkeymap_send_keys(keysym, xevent.xkey.keycode, xevent.xkey.state,
1819 ev_time, True, 0);
1820 break;
1821
1822 case KeyRelease:
1823 g_last_gesturetime = xevent.xkey.time;
1824 XLookupString((XKeyEvent *) & xevent, str,
1825 sizeof(str), &keysym, NULL);
1826
1827 DEBUG_KBD(("\nKeyRelease for keysym (0x%lx, %s)\n", keysym,
1828 get_ksname(keysym)));
1829
1830 ev_time = time(NULL);
1831 if (handle_special_keys(keysym, xevent.xkey.state, ev_time, False))
1832 break;
1833
1834 xkeymap_send_keys(keysym, xevent.xkey.keycode, xevent.xkey.state,
1835 ev_time, False, 0);
1836 break;
1837
1838 case ButtonPress:
1839 handle_button_event(xevent, True);
1840 break;
1841
1842 case ButtonRelease:
1843 handle_button_event(xevent, False);
1844 break;
1845
1846 case MotionNotify:
1847 if (g_moving_wnd)
1848 {
1849 XMoveWindow(g_display, g_wnd,
1850 xevent.xmotion.x_root - g_move_x_offset,
1851 xevent.xmotion.y_root - g_move_y_offset);
1852 break;
1853 }
1854
1855 if (g_fullscreen && !g_focused)
1856 XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
1857 CurrentTime);
1858
1859 if (xevent.xmotion.window == g_wnd)
1860 {
1861 rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
1862 xevent.xmotion.x, xevent.xmotion.y);
1863 }
1864 else
1865 {
1866 /* SeamlessRDP */
1867 rdp_send_input(time(NULL), RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE,
1868 xevent.xmotion.x_root,
1869 xevent.xmotion.y_root);
1870 }
1871 break;
1872
1873 case FocusIn:
1874 if (xevent.xfocus.mode == NotifyGrab)
1875 break;
1876 g_focused = True;
1877 reset_modifier_keys();
1878 if (g_grab_keyboard && g_mouse_in_wnd)
1879 XGrabKeyboard(g_display, g_wnd, True,
1880 GrabModeAsync, GrabModeAsync, CurrentTime);
1881 break;
1882
1883 case FocusOut:
1884 if (xevent.xfocus.mode == NotifyUngrab)
1885 break;
1886 g_focused = False;
1887 if (xevent.xfocus.mode == NotifyWhileGrabbed)
1888 XUngrabKeyboard(g_display, CurrentTime);
1889 break;
1890
1891 case EnterNotify:
1892 /* we only register for this event when in fullscreen mode */
1893 /* or grab_keyboard */
1894 g_mouse_in_wnd = True;
1895 if (g_fullscreen)
1896 {
1897 XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
1898 CurrentTime);
1899 break;
1900 }
1901 if (g_focused)
1902 XGrabKeyboard(g_display, g_wnd, True,
1903 GrabModeAsync, GrabModeAsync, CurrentTime);
1904 break;
1905
1906 case LeaveNotify:
1907 /* we only register for this event when grab_keyboard */
1908 g_mouse_in_wnd = False;
1909 XUngrabKeyboard(g_display, CurrentTime);
1910 break;
1911
1912 case Expose:
1913 if (xevent.xexpose.window == g_wnd)
1914 {
1915 XCopyArea(g_display, g_backstore, xevent.xexpose.window,
1916 g_gc,
1917 xevent.xexpose.x, xevent.xexpose.y,
1918 xevent.xexpose.width, xevent.xexpose.height,
1919 xevent.xexpose.x, xevent.xexpose.y);
1920 }
1921 else
1922 {
1923 sw = seamless_get_window_by_wnd(xevent.xexpose.window);
1924 if (sw)
1925 XCopyArea(g_display, g_backstore,
1926 xevent.xexpose.window, g_gc,
1927 xevent.xexpose.x + sw->xoffset,
1928 xevent.xexpose.y + sw->yoffset,
1929 xevent.xexpose.width,
1930 xevent.xexpose.height, xevent.xexpose.x,
1931 xevent.xexpose.y);
1932 else
1933 {
1934 error("Expose for unknown window 0x%lx\n",
1935 xevent.xexpose.window);
1936 }
1937 }
1938
1939 break;
1940
1941 case MappingNotify:
1942 /* Refresh keyboard mapping if it has changed. This is important for
1943 Xvnc, since it allocates keycodes dynamically */
1944 if (xevent.xmapping.request == MappingKeyboard
1945 || xevent.xmapping.request == MappingModifier)
1946 XRefreshKeyboardMapping(&xevent.xmapping);
1947
1948 if (xevent.xmapping.request == MappingModifier)
1949 {
1950 XFreeModifiermap(g_mod_map);
1951 g_mod_map = XGetModifierMapping(g_display);
1952 }
1953 break;
1954
1955 /* clipboard stuff */
1956 case SelectionNotify:
1957 xclip_handle_SelectionNotify(&xevent.xselection);
1958 break;
1959 case SelectionRequest:
1960 xclip_handle_SelectionRequest(&xevent.xselectionrequest);
1961 break;
1962 case SelectionClear:
1963 xclip_handle_SelectionClear();
1964 break;
1965 case PropertyNotify:
1966 xclip_handle_PropertyNotify(&xevent.xproperty);
1967 if (xevent.xproperty.window == g_wnd)
1968 break;
1969 if (xevent.xproperty.window == DefaultRootWindow(g_display))
1970 break;
1971
1972 /* seamless */
1973 sw = seamless_get_window_by_wnd(xevent.xproperty.window);
1974 if (!sw)
1975 break;
1976
1977 if ((xevent.xproperty.atom == g_net_wm_state_atom)
1978 && (xevent.xproperty.state == PropertyNewValue))
1979 {
1980 sw->state = ewmh_get_window_state(sw->wnd);
1981 seamless_send_state(sw->id, sw->state, 0);
1982 }
1983
1984 if ((xevent.xproperty.atom == g_net_wm_desktop_atom)
1985 && (xevent.xproperty.state == PropertyNewValue))
1986 {
1987 sw->desktop = ewmh_get_window_desktop(sw->wnd);
1988 seamless_all_to_desktop(sw->wnd, sw->desktop);
1989 }
1990
1991 break;
1992 case MapNotify:
1993 if (!g_seamless_rdp)
1994 rdp_send_client_window_status(1);
1995 break;
1996 case UnmapNotify:
1997 if (!g_seamless_rdp)
1998 rdp_send_client_window_status(0);
1999 break;
2000 }
2001 }
2002 /* Keep going */
2003 return 1;
2004 }
2005
2006 /* Returns 0 after user quit, 1 otherwise */
2007 int
2008 ui_select(int rdp_socket)
2009 {
2010 int n;
2011 fd_set rfds, wfds;
2012 struct timeval tv;
2013 BOOL s_timeout = False;
2014
2015 while (True)
2016 {
2017 n = (rdp_socket > g_x_socket) ? rdp_socket : g_x_socket;
2018 /* Process any events already waiting */
2019 if (!xwin_process_events())
2020 /* User quit */
2021 return 0;
2022
2023 FD_ZERO(&rfds);
2024 FD_ZERO(&wfds);
2025 FD_SET(rdp_socket, &rfds);
2026 FD_SET(g_x_socket, &rfds);
2027
2028 #ifdef WITH_RDPSND
2029 /* FIXME: there should be an API for registering fds */
2030 if (g_dsp_busy)
2031 {
2032 FD_SET(g_dsp_fd, &wfds);
2033 n = (g_dsp_fd > n) ? g_dsp_fd : n;
2034 }
2035 #endif
2036 /* default timeout */
2037 tv.tv_sec = 60;
2038 tv.tv_usec = 0;
2039
2040 /* add redirection handles */
2041 rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);
2042
2043 n++;
2044
2045 switch (select(n, &rfds, &wfds, NULL, &tv))
2046 {
2047 case -1:
2048 error("select: %s\n", strerror(errno));
2049
2050 case 0:
2051 /* Abort serial read calls */
2052 if (s_timeout)
2053 rdpdr_check_fds(&rfds, &wfds, (BOOL) True);
2054 continue;
2055 }
2056
2057 rdpdr_check_fds(&rfds, &wfds, (BOOL) False);
2058
2059 if (FD_ISSET(rdp_socket, &rfds))
2060 return 1;
2061
2062 #ifdef WITH_RDPSND
2063 if (g_dsp_busy && FD_ISSET(g_dsp_fd, &wfds))
2064 wave_out_play();
2065 #endif
2066 }
2067 }
2068
2069 void
2070 ui_move_pointer(int x, int y)
2071 {
2072 XWarpPointer(g_display, g_wnd, g_wnd, 0, 0, 0, 0, x, y);
2073 }
2074
2075 HBITMAP
2076 ui_create_bitmap(int width, int height, uint8 * data)
2077 {
2078 XImage *image;
2079 Pixmap bitmap;
2080 uint8 *tdata;
2081 int bitmap_pad;
2082
2083 if (g_server_depth == 8)
2084 {
2085 bitmap_pad = 8;
2086 }
2087 else
2088 {
2089 bitmap_pad = g_bpp;
2090
2091 if (g_bpp == 24)
2092 bitmap_pad = 32;
2093 }
2094
2095 tdata = (g_owncolmap ? data : translate_image(width, height, data));
2096 bitmap = XCreatePixmap(g_display, g_wnd, width, height, g_depth);
2097 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
2098 (char *) tdata, width, height, bitmap_pad, 0);
2099
2100 XPutImage(g_display, bitmap, g_create_bitmap_gc, image, 0, 0, 0, 0, width, height);
2101
2102 XFree(image);
2103 if (tdata != data)
2104 xfree(tdata);
2105 return (HBITMAP) bitmap;
2106 }
2107
2108 void
2109 ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height, uint8 * data)
2110 {
2111 XImage *image;
2112 uint8 *tdata;
2113 int bitmap_pad;
2114
2115 if (g_server_depth == 8)
2116 {
2117 bitmap_pad = 8;
2118 }
2119 else
2120 {
2121 bitmap_pad = g_bpp;
2122
2123 if (g_bpp == 24)
2124 bitmap_pad = 32;
2125 }
2126
2127 tdata = (g_owncolmap ? data : translate_image(width, height, data));
2128 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
2129 (char *) tdata, width, height, bitmap_pad, 0);
2130
2131 if (g_ownbackstore)
2132 {
2133 XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
2134 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2135 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2136 (g_display, g_backstore, sw->wnd, g_gc, x, y, cx, cy,
2137 x - sw->xoffset, y - sw->yoffset));
2138 }
2139 else
2140 {
2141 XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
2142 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2143 (g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy,
2144 x - sw->xoffset, y - sw->yoffset));
2145 }
2146
2147 XFree(image);
2148 if (tdata != data)
2149 xfree(tdata);
2150 }
2151
2152 void
2153 ui_destroy_bitmap(HBITMAP bmp)
2154 {
2155 XFreePixmap(g_display, (Pixmap) bmp);
2156 }
2157
2158 HGLYPH
2159 ui_create_glyph(int width, int height, uint8 * data)
2160 {
2161 XImage *image;
2162 Pixmap bitmap;
2163 int scanline;
2164
2165 scanline = (width + 7) / 8;
2166
2167 bitmap = XCreatePixmap(g_display, g_wnd, width, height, 1);
2168 if (g_create_glyph_gc == 0)
2169 g_create_glyph_gc = XCreateGC(g_display, bitmap, 0, NULL);
2170
2171 image = XCreateImage(g_display, g_visual, 1, ZPixmap, 0, (char *) data,
2172 width, height, 8, scanline);
2173 image->byte_order = MSBFirst;
2174 image->bitmap_bit_order = MSBFirst;
2175 XInitImage(image);
2176
2177 XPutImage(g_display, bitmap, g_create_glyph_gc, image, 0, 0, 0, 0, width, height);
2178
2179 XFree(image);
2180 return (HGLYPH) bitmap;
2181 }
2182
2183 void
2184 ui_destroy_glyph(HGLYPH glyph)
2185 {
2186 XFreePixmap(g_display, (Pixmap) glyph);
2187 }
2188
2189 HCURSOR
2190 ui_create_cursor(unsigned int x, unsigned int y, int width, int height,
2191 uint8 * andmask, uint8 * xormask)
2192 {
2193 HGLYPH maskglyph, cursorglyph;
2194 XColor bg, fg;
2195 Cursor xcursor;
2196 uint8 *cursor, *pcursor;
2197 uint8 *mask, *pmask;
2198 uint8 nextbit;
2199 int scanline, offset;
2200 int i, j;
2201
2202 scanline = (width + 7) / 8;
2203 offset = scanline * height;
2204
2205 cursor = (uint8 *) xmalloc(offset);
2206 memset(cursor, 0, offset);
2207
2208 mask = (uint8 *) xmalloc(offset);
2209 memset(mask, 0, offset);
2210
2211 /* approximate AND and XOR masks with a monochrome X pointer */
2212 for (i = 0; i < height; i++)
2213 {
2214 offset -= scanline;
2215 pcursor = &cursor[offset];
2216 pmask = &mask[offset];
2217
2218 for (j = 0; j < scanline; j++)
2219 {
2220 for (nextbit = 0x80; nextbit != 0; nextbit >>= 1)
2221 {
2222 if (xormask[0] || xormask[1] || xormask[2])
2223 {
2224 *pcursor |= (~(*andmask) & nextbit);
2225 *pmask |= nextbit;
2226 }
2227 else
2228 {
2229 *pcursor |= ((*andmask) & nextbit);
2230 *pmask |= (~(*andmask) & nextbit);
2231 }
2232
2233 xormask += 3;
2234 }
2235
2236 andmask++;
2237 pcursor++;
2238 pmask++;
2239 }
2240 }
2241
2242 fg.red = fg.blue = fg.green = 0xffff;
2243 bg.red = bg.blue = bg.green = 0x0000;
2244 fg.flags = bg.flags = DoRed | DoBlue | DoGreen;
2245
2246 cursorglyph = ui_create_glyph(width, height, cursor);
2247 maskglyph = ui_create_glyph(width, height, mask);
2248
2249 xcursor =
2250 XCreatePixmapCursor(g_display, (Pixmap) cursorglyph,
2251 (Pixmap) maskglyph, &fg, &bg, x, y);
2252
2253 ui_destroy_glyph(maskglyph);
2254 ui_destroy_glyph(cursorglyph);
2255 xfree(mask);
2256 xfree(cursor);
2257 return (HCURSOR) xcursor;
2258 }
2259
2260 void
2261 ui_set_cursor(HCURSOR cursor)
2262 {
2263 g_current_cursor = (Cursor) cursor;
2264 XDefineCursor(g_display, g_wnd, g_current_cursor);
2265 ON_ALL_SEAMLESS_WINDOWS(XDefineCursor, (g_display, sw->wnd, g_current_cursor));
2266 }
2267
2268 void
2269 ui_destroy_cursor(HCURSOR cursor)
2270 {
2271 XFreeCursor(g_display, (Cursor) cursor);
2272 }
2273
2274 void
2275 ui_set_null_cursor(void)
2276 {
2277 ui_set_cursor(g_null_cursor);
2278 }
2279
2280 #define MAKE_XCOLOR(xc,c) \
2281 (xc)->red = ((c)->red << 8) | (c)->red; \
2282 (xc)->green = ((c)->green << 8) | (c)->green; \
2283 (xc)->blue = ((c)->blue << 8) | (c)->blue; \
2284 (xc)->flags = DoRed | DoGreen | DoBlue;
2285
2286
2287 HCOLOURMAP
2288 ui_create_colourmap(COLOURMAP * colours)
2289 {
2290 COLOURENTRY *entry;
2291 int i, ncolours = colours->ncolours;
2292 if (!g_owncolmap)
2293 {
2294 uint32 *map = (uint32 *) xmalloc(sizeof(*g_colmap) * ncolours);
2295 XColor xentry;
2296 XColor xc_cache[256];
2297 uint32 colour;
2298 int colLookup = 256;
2299 for (i = 0; i < ncolours; i++)
2300 {
2301 entry = &colours->colours[i];
2302 MAKE_XCOLOR(&xentry, entry);
2303
2304 if (XAllocColor(g_display, g_xcolmap, &xentry) == 0)
2305 {
2306 /* Allocation failed, find closest match. */
2307 int j = 256;
2308 int nMinDist = 3 * 256 * 256;
2309 long nDist = nMinDist;
2310
2311 /* only get the colors once */
2312 while (colLookup--)
2313 {
2314 xc_cache[colLookup].pixel = colLookup;
2315 xc_cache[colLookup].red = xc_cache[colLookup].green =
2316 xc_cache[colLookup].blue = 0;
2317 xc_cache[colLookup].flags = 0;
2318 XQueryColor(g_display,
2319 DefaultColormap(g_display,
2320 DefaultScreen(g_display)),
2321 &xc_cache[colLookup]);
2322 }
2323 colLookup = 0;
2324
2325 /* approximate the pixel */
2326 while (j--)
2327 {
2328 if (xc_cache[j].flags)
2329 {
2330 nDist = ((long) (xc_cache[j].red >> 8) -
2331 (long) (xentry.red >> 8)) *
2332 ((long) (xc_cache[j].red >> 8) -
2333 (long) (xentry.red >> 8)) +
2334 ((long) (xc_cache[j].green >> 8) -
2335 (long) (xentry.green >> 8)) *
2336 ((long) (xc_cache[j].green >> 8) -
2337 (long) (xentry.green >> 8)) +
2338 ((long) (xc_cache[j].blue >> 8) -
2339 (long) (xentry.blue >> 8)) *
2340 ((long) (xc_cache[j].blue >> 8) -
2341 (long) (xentry.blue >> 8));
2342 }
2343 if (nDist < nMinDist)
2344 {
2345 nMinDist = nDist;
2346 xentry.pixel = j;
2347 }
2348 }
2349 }
2350 colour = xentry.pixel;
2351
2352 /* update our cache */
2353 if (xentry.pixel < 256)
2354 {
2355 xc_cache[xentry.pixel].red = xentry.red;
2356 xc_cache[xentry.pixel].green = xentry.green;
2357 xc_cache[xentry.pixel].blue = xentry.blue;
2358
2359 }
2360
2361 map[i] = colour;
2362 }
2363 return map;
2364 }
2365 else
2366 {
2367 XColor *xcolours, *xentry;
2368 Colormap map;
2369
2370 xcolours = (XColor *) xmalloc(sizeof(XColor) * ncolours);
2371 for (i = 0; i < ncolours; i++)
2372 {
2373 entry = &colours->colours[i];
2374 xentry = &xcolours[i];
2375 xentry->pixel = i;
2376 MAKE_XCOLOR(xentry, entry);
2377 }
2378
2379 map = XCreateColormap(g_display, g_wnd, g_visual, AllocAll);
2380 XStoreColors(g_display, map, xcolours, ncolours);
2381
2382 xfree(xcolours);
2383 return (HCOLOURMAP) map;
2384 }
2385 }
2386
2387 void
2388 ui_destroy_colourmap(HCOLOURMAP map)
2389 {
2390 if (!g_owncolmap)
2391 xfree(map);
2392 else
2393 XFreeColormap(g_display, (Colormap) map);
2394 }
2395
2396 void
2397 ui_set_colourmap(HCOLOURMAP map)
2398 {
2399 if (!g_owncolmap)
2400 {
2401 if (g_colmap)
2402 xfree(g_colmap);
2403
2404 g_colmap = (uint32 *) map;
2405 }
2406 else
2407 {
2408 XSetWindowColormap(g_display, g_wnd, (Colormap) map);
2409 ON_ALL_SEAMLESS_WINDOWS(XSetWindowColormap, (g_display, sw->wnd, (Colormap) map));
2410 }
2411 }
2412
2413 void
2414 ui_set_clip(int x, int y, int cx, int cy)
2415 {
2416 g_clip_rectangle.x = x;
2417 g_clip_rectangle.y = y;
2418 g_clip_rectangle.width = cx;
2419 g_clip_rectangle.height = cy;
2420 XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
2421 }
2422
2423 void
2424 ui_reset_clip(void)
2425 {
2426 g_clip_rectangle.x = 0;
2427 g_clip_rectangle.y = 0;
2428 g_clip_rectangle.width = g_width;
2429 g_clip_rectangle.height = g_height;
2430 XSetClipRectangles(g_display, g_gc, 0, 0, &g_clip_rectangle, 1, YXBanded);
2431 }
2432
2433 void
2434 ui_bell(void)
2435 {
2436 XBell(g_display, 0);
2437 }
2438
2439 void
2440 ui_destblt(uint8 opcode,
2441 /* dest */ int x, int y, int cx, int cy)
2442 {
2443 SET_FUNCTION(opcode);
2444 FILL_RECTANGLE(x, y, cx, cy);
2445 RESET_FUNCTION(opcode);
2446 }
2447
2448 static uint8 hatch_patterns[] = {
2449 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, /* 0 - bsHorizontal */
2450 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, /* 1 - bsVertical */
2451 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, /* 2 - bsFDiagonal */
2452 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /* 3 - bsBDiagonal */
2453 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08, /* 4 - bsCross */
2454 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 /* 5 - bsDiagCross */
2455 };
2456
2457 void
2458 ui_patblt(uint8 opcode,
2459 /* dest */ int x, int y, int cx, int cy,
2460 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2461 {
2462 Pixmap fill;
2463 uint8 i, ipattern[8];
2464
2465 SET_FUNCTION(opcode);
2466
2467 switch (brush->style)
2468 {
2469 case 0: /* Solid */
2470 SET_FOREGROUND(fgcolour);
2471 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2472 break;
2473
2474 case 2: /* Hatch */
2475 fill = (Pixmap) ui_create_glyph(8, 8,
2476 hatch_patterns + brush->pattern[0] * 8);
2477 SET_FOREGROUND(fgcolour);
2478 SET_BACKGROUND(bgcolour);
2479 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2480 XSetStipple(g_display, g_gc, fill);
2481 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2482 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2483 XSetFillStyle(g_display, g_gc, FillSolid);
2484 XSetTSOrigin(g_display, g_gc, 0, 0);
2485 ui_destroy_glyph((HGLYPH) fill);
2486 break;
2487
2488 case 3: /* Pattern */
2489 for (i = 0; i != 8; i++)
2490 ipattern[7 - i] = brush->pattern[i];
2491 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
2492 SET_FOREGROUND(bgcolour);
2493 SET_BACKGROUND(fgcolour);
2494 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2495 XSetStipple(g_display, g_gc, fill);
2496 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2497 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2498 XSetFillStyle(g_display, g_gc, FillSolid);
2499 XSetTSOrigin(g_display, g_gc, 0, 0);
2500 ui_destroy_glyph((HGLYPH) fill);
2501 break;
2502
2503 default:
2504 unimpl("brush %d\n", brush->style);
2505 }
2506
2507 RESET_FUNCTION(opcode);
2508
2509 if (g_ownbackstore)
2510 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2511 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2512 (g_display, g_ownbackstore ? g_backstore : g_wnd, sw->wnd, g_gc,
2513 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2514 }
2515
2516 void
2517 ui_screenblt(uint8 opcode,
2518 /* dest */ int x, int y, int cx, int cy,
2519 /* src */ int srcx, int srcy)
2520 {
2521 SET_FUNCTION(opcode);
2522 if (g_ownbackstore)
2523 {
2524 XCopyArea(g_display, g_Unobscured ? g_wnd : g_backstore,
2525 g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2526 XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
2527 }
2528 else
2529 {
2530 XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2531 }
2532
2533 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2534 (g_display, g_ownbackstore ? g_backstore : g_wnd,
2535 sw->wnd, g_gc, x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2536
2537 RESET_FUNCTION(opcode);
2538 }
2539
2540 void
2541 ui_memblt(uint8 opcode,
2542 /* dest */ int x, int y, int cx, int cy,
2543 /* src */ HBITMAP src, int srcx, int srcy)
2544 {
2545 SET_FUNCTION(opcode);
2546 XCopyArea(g_display, (Pixmap) src, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2547 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2548 (g_display, (Pixmap) src, sw->wnd, g_gc,
2549 srcx, srcy, cx, cy, x - sw->xoffset, y - sw->yoffset));
2550 if (g_ownbackstore)
2551 XCopyArea(g_display, (Pixmap) src, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
2552 RESET_FUNCTION(opcode);
2553 }
2554
2555 void
2556 ui_triblt(uint8 opcode,
2557 /* dest */ int x, int y, int cx, int cy,
2558 /* src */ HBITMAP src, int srcx, int srcy,
2559 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2560 {
2561 /* This is potentially difficult to do in general. Until someone
2562 comes up with a more efficient way of doing it I am using cases. */
2563
2564 switch (opcode)
2565 {
2566 case 0x69: /* PDSxxn */
2567 ui_memblt(ROP2_XOR, x, y, cx, cy, src, srcx, srcy);
2568 ui_patblt(ROP2_NXOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2569 break;
2570
2571 case 0xb8: /* PSDPxax */
2572 ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2573 ui_memblt(ROP2_AND, x, y, cx, cy, src, srcx, srcy);
2574 ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2575 break;
2576
2577 case 0xc0: /* PSa */
2578 ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2579 ui_patblt(ROP2_AND, x, y, cx, cy, brush, bgcolour, fgcolour);
2580 break;
2581
2582 default:
2583 unimpl("triblt 0x%x\n", opcode);
2584 ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2585 }
2586 }
2587
2588 void
2589 ui_line(uint8 opcode,
2590 /* dest */ int startx, int starty, int endx, int endy,
2591 /* pen */ PEN * pen)
2592 {
2593 SET_FUNCTION(opcode);
2594 SET_FOREGROUND(pen->colour);
2595 XDrawLine(g_display, g_wnd, g_gc, startx, starty, endx, endy);
2596 ON_ALL_SEAMLESS_WINDOWS(XDrawLine, (g_display, sw->wnd, g_gc,
2597 startx - sw->xoffset, starty - sw->yoffset,
2598 endx - sw->xoffset, endy - sw->yoffset));
2599 if (g_ownbackstore)
2600 XDrawLine(g_display, g_backstore, g_gc, startx, starty, endx, endy);
2601 RESET_FUNCTION(opcode);
2602 }
2603
2604 void
2605 ui_rect(
2606 /* dest */ int x, int y, int cx, int cy,
2607 /* brush */ int colour)
2608 {
2609 SET_FOREGROUND(colour);
2610 FILL_RECTANGLE(x, y, cx, cy);
2611 }
2612
2613 void
2614 ui_polygon(uint8 opcode,
2615 /* mode */ uint8 fillmode,
2616 /* dest */ POINT * point, int npoints,
2617 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2618 {
2619 uint8 style, i, ipattern[8];
2620 Pixmap fill;
2621
2622 SET_FUNCTION(opcode);
2623
2624 switch (fillmode)
2625 {
2626 case ALTERNATE:
2627 XSetFillRule(g_display, g_gc, EvenOddRule);
2628 break;
2629 case WINDING:
2630 XSetFillRule(g_display, g_gc, WindingRule);
2631 break;
2632 default:
2633 unimpl("fill mode %d\n", fillmode);
2634 }
2635
2636 if (brush)
2637 style = brush->style;
2638 else
2639 style = 0;
2640
2641 switch (style)
2642 {
2643 case 0: /* Solid */
2644 SET_FOREGROUND(fgcolour);
2645 FILL_POLYGON((XPoint *) point, npoints);
2646 break;
2647
2648 case 2: /* Hatch */
2649 fill = (Pixmap) ui_create_glyph(8, 8,
2650 hatch_patterns + brush->pattern[0] * 8);
2651 SET_FOREGROUND(fgcolour);
2652 SET_BACKGROUND(bgcolour);
2653 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2654 XSetStipple(g_display, g_gc, fill);
2655 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2656 FILL_POLYGON((XPoint *) point, npoints);
2657 XSetFillStyle(g_display, g_gc, FillSolid);
2658 XSetTSOrigin(g_display, g_gc, 0, 0);
2659 ui_destroy_glyph((HGLYPH) fill);
2660 break;
2661
2662 case 3: /* Pattern */
2663 for (i = 0; i != 8; i++)
2664 ipattern[7 - i] = brush->pattern[i];
2665 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
2666 SET_FOREGROUND(bgcolour);
2667 SET_BACKGROUND(fgcolour);
2668 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2669 XSetStipple(g_display, g_gc, fill);
2670 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2671 FILL_POLYGON((XPoint *) point, npoints);
2672 XSetFillStyle(g_display, g_gc, FillSolid);
2673 XSetTSOrigin(g_display, g_gc, 0, 0);
2674 ui_destroy_glyph((HGLYPH) fill);
2675 break;
2676
2677 default:
2678 unimpl("brush %d\n", brush->style);
2679 }
2680
2681 RESET_FUNCTION(opcode);
2682 }
2683
2684 void
2685 ui_polyline(uint8 opcode,
2686 /* dest */ POINT * points, int npoints,
2687 /* pen */ PEN * pen)
2688 {
2689 /* TODO: set join style */
2690 SET_FUNCTION(opcode);
2691 SET_FOREGROUND(pen->colour);
2692 XDrawLines(g_display, g_wnd, g_gc, (XPoint *) points, npoints, CoordModePrevious);
2693 if (g_ownbackstore)
2694 XDrawLines(g_display, g_backstore, g_gc, (XPoint *) points, npoints,
2695 CoordModePrevious);
2696
2697 ON_ALL_SEAMLESS_WINDOWS(seamless_XDrawLines,
2698 (sw->wnd, (XPoint *) points, npoints, sw->xoffset, sw->yoffset));
2699
2700 RESET_FUNCTION(opcode);
2701 }
2702
2703 void
2704 ui_ellipse(uint8 opcode,
2705 /* mode */ uint8 fillmode,
2706 /* dest */ int x, int y, int cx, int cy,
2707 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2708 {
2709 uint8 style, i, ipattern[8];
2710 Pixmap fill;
2711
2712 SET_FUNCTION(opcode);
2713
2714 if (brush)
2715 style = brush->style;
2716 else
2717 style = 0;
2718
2719 switch (style)
2720 {
2721 case 0: /* Solid */
2722 SET_FOREGROUND(fgcolour);
2723 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2724 break;
2725
2726 case 2: /* Hatch */
2727 fill = (Pixmap) ui_create_glyph(8, 8,
2728 hatch_patterns + brush->pattern[0] * 8);
2729 SET_FOREGROUND(fgcolour);
2730 SET_BACKGROUND(bgcolour);
2731 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2732 XSetStipple(g_display, g_gc, fill);
2733 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2734 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2735 XSetFillStyle(g_display, g_gc, FillSolid);
2736 XSetTSOrigin(g_display, g_gc, 0, 0);
2737 ui_destroy_glyph((HGLYPH) fill);
2738 break;
2739
2740 case 3: /* Pattern */
2741 for (i = 0; i != 8; i++)
2742 ipattern[7 - i] = brush->pattern[i];
2743 fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
2744 SET_FOREGROUND(bgcolour);
2745 SET_BACKGROUND(fgcolour);
2746 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2747 XSetStipple(g_display, g_gc, fill);
2748 XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2749 DRAW_ELLIPSE(x, y, cx, cy, fillmode);
2750 XSetFillStyle(g_display, g_gc, FillSolid);
2751 XSetTSOrigin(g_display, g_gc, 0, 0);
2752 ui_destroy_glyph((HGLYPH) fill);
2753 break;
2754
2755 default:
2756 unimpl("brush %d\n", brush->style);
2757 }
2758
2759 RESET_FUNCTION(opcode);
2760 }
2761
2762 /* warning, this function only draws on wnd or backstore, not both */
2763 void
2764 ui_draw_glyph(int mixmode,
2765 /* dest */ int x, int y, int cx, int cy,
2766 /* src */ HGLYPH glyph, int srcx, int srcy,
2767 int bgcolour, int fgcolour)
2768 {
2769 SET_FOREGROUND(fgcolour);
2770 SET_BACKGROUND(bgcolour);
2771
2772 XSetFillStyle(g_display, g_gc,
2773 (mixmode == MIX_TRANSPARENT) ? FillStippled : FillOpaqueStippled);
2774 XSetStipple(g_display, g_gc, (Pixmap) glyph);
2775 XSetTSOrigin(g_display, g_gc, x, y);
2776
2777 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2778
2779 XSetFillStyle(g_display, g_gc, FillSolid);
2780 }
2781
2782 #define DO_GLYPH(ttext,idx) \
2783 {\
2784 glyph = cache_get_font (font, ttext[idx]);\
2785 if (!(flags & TEXT2_IMPLICIT_X))\
2786 {\
2787 xyoffset = ttext[++idx];\
2788 if ((xyoffset & 0x80))\
2789 {\
2790 if (flags & TEXT2_VERTICAL)\
2791 y += ttext[idx+1] | (ttext[idx+2] << 8);\
2792 else\
2793 x += ttext[idx+1] | (ttext[idx+2] << 8);\
2794 idx += 2;\
2795 }\
2796 else\
2797 {\
2798 if (flags & TEXT2_VERTICAL)\
2799 y += xyoffset;\
2800 else\
2801 x += xyoffset;\
2802 }\
2803 }\
2804 if (glyph != NULL)\
2805 {\
2806 x1 = x + glyph->offset;\
2807 y1 = y + glyph->baseline;\
2808 XSetStipple(g_display, g_gc, (Pixmap) glyph->pixmap);\
2809 XSetTSOrigin(g_display, g_gc, x1, y1);\
2810 FILL_RECTANGLE_BACKSTORE(x1, y1, glyph->width, glyph->height);\
2811 if (flags & TEXT2_IMPLICIT_X)\
2812 x += glyph->width;\
2813 }\
2814 }
2815
2816 void
2817 ui_draw_text(uint8 font, uint8 flags, uint8 opcode, int mixmode, int x, int y,
2818 int clipx, int clipy, int clipcx, int clipcy,
2819 int boxx, int boxy, int boxcx, int boxcy, BRUSH * brush,
2820 int bgcolour, int fgcolour, uint8 * text, uint8 length)
2821 {
2822 /* TODO: use brush appropriately */
2823
2824 FONTGLYPH *glyph;
2825 int i, j, xyoffset, x1, y1;
2826 DATABLOB *entry;
2827
2828 SET_FOREGROUND(bgcolour);
2829
2830 /* Sometimes, the boxcx value is something really large, like
2831 32691. This makes XCopyArea fail with Xvnc. The code below
2832 is a quick fix. */
2833 if (boxx + boxcx > g_width)
2834 boxcx = g_width - boxx;
2835
2836 if (boxcx > 1)
2837 {
2838 FILL_RECTANGLE_BACKSTORE(boxx, boxy, boxcx, boxcy);
2839 }
2840 else if (mixmode == MIX_OPAQUE)
2841 {
2842 FILL_RECTANGLE_BACKSTORE(clipx, clipy, clipcx, clipcy);
2843 }
2844
2845 SET_FOREGROUND(fgcolour);
2846 SET_BACKGROUND(bgcolour);
2847 XSetFillStyle(g_display, g_gc, FillStippled);
2848
2849 /* Paint text, character by character */
2850 for (i = 0; i < length;)
2851 {
2852 switch (text[i])
2853 {
2854 case 0xff:
2855 /* At least two bytes needs to follow */
2856 if (i + 3 > length)
2857 {
2858 warning("Skipping short 0xff command:");
2859 for (j = 0; j < length; j++)
2860 fprintf(stderr, "%02x ", text[j]);
2861 fprintf(stderr, "\n");
2862 i = length = 0;
2863 break;
2864 }
2865 cache_put_text(text[i + 1], text, text[i + 2]);
2866 i += 3;
2867 length -= i;
2868 /* this will move pointer from start to first character after FF command */
2869 text = &(text[i]);
2870 i = 0;
2871 break;
2872
2873 case 0xfe:
2874 /* At least one byte needs to follow */
2875 if (i + 2 > length)
2876 {
2877 warning("Skipping short 0xfe command:");
2878 for (j = 0; j < length; j++)
2879 fprintf(stderr, "%02x ", text[j]);
2880 fprintf(stderr, "\n");
2881 i = length = 0;
2882 break;
2883 }
2884 entry = cache_get_text(text[i + 1]);
2885 if (entry->data != NULL)
2886 {
2887 if ((((uint8 *) (entry->data))[1] == 0)
2888 && (!(flags & TEXT2_IMPLICIT_X)) && (i + 2 < length))
2889 {
2890 if (flags & TEXT2_VERTICAL)
2891 y += text[i + 2];
2892 else
2893 x += text[i + 2];
2894 }
2895 for (j = 0; j < entry->size; j++)
2896 DO_GLYPH(((uint8 *) (entry->data)), j);
2897 }
2898 if (i + 2 < length)
2899 i += 3;
2900 else
2901 i += 2;
2902 length -= i;
2903 /* this will move pointer from start to first character after FE command */
2904 text = &(text[i]);
2905 i = 0;
2906 break;
2907
2908 default:
2909 DO_GLYPH(text, i);
2910 i++;
2911 break;
2912 }
2913 }
2914
2915 XSetFillStyle(g_display, g_gc, FillSolid);
2916
2917 if (g_ownbackstore)
2918 {
2919 if (boxcx > 1)
2920 {
2921 XCopyArea(g_display, g_backstore, g_wnd, g_gc, boxx,
2922 boxy, boxcx, boxcy, boxx, boxy);
2923 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2924 (g_display, g_backstore, sw->wnd, g_gc,
2925 boxx, boxy,
2926 boxcx, boxcy,
2927 boxx - sw->xoffset, boxy - sw->yoffset));
2928 }
2929 else
2930 {
2931 XCopyArea(g_display, g_backstore, g_wnd, g_gc, clipx,
2932 clipy, clipcx, clipcy, clipx, clipy);
2933 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2934 (g_display, g_backstore, sw->wnd, g_gc,
2935 clipx, clipy,
2936 clipcx, clipcy, clipx - sw->xoffset,
2937 clipy - sw->yoffset));
2938 }
2939 }
2940 }
2941
2942 void
2943 ui_desktop_save(uint32 offset, int x, int y, int cx, int cy)
2944 {
2945 Pixmap pix;
2946 XImage *image;
2947
2948 if (g_ownbackstore)
2949 {
2950 image = XGetImage(g_display, g_backstore, x, y, cx, cy, AllPlanes, ZPixmap);
2951 }
2952 else
2953 {
2954 pix = XCreatePixmap(g_display, g_wnd, cx, cy, g_depth);
2955 XCopyArea(g_display, g_wnd, pix, g_gc, x, y, cx, cy, 0, 0);
2956 image = XGetImage(g_display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap);
2957 XFreePixmap(g_display, pix);
2958 }
2959
2960 offset *= g_bpp / 8;
2961 cache_put_desktop(offset, cx, cy, image->bytes_per_line, g_bpp / 8, (uint8 *) image->data);
2962
2963 XDestroyImage(image);
2964 }
2965
2966 void
2967 ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy)
2968 {
2969 XImage *image;
2970 uint8 *data;
2971
2972 offset *= g_bpp / 8;
2973 data = cache_get_desktop(offset, cx, cy, g_bpp / 8);
2974 if (data == NULL)
2975 return;
2976
2977 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
2978 (char *) data, cx, cy, BitmapPad(g_display), cx * g_bpp / 8);
2979
2980 if (g_ownbackstore)
2981 {
2982 XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
2983 XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2984 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2985 (g_display, g_backstore, sw->wnd, g_gc,
2986 x, y, cx, cy, x - sw->xoffset, y - sw->yoffset));
2987 }
2988 else
2989 {
2990 XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
2991 ON_ALL_SEAMLESS_WINDOWS(XCopyArea,
2992 (g_display, g_wnd, sw->wnd, g_gc, x, y, cx, cy,
2993 x - sw->xoffset, y - sw->yoffset));
2994 }
2995
2996 XFree(image);
2997 }
2998
2999 /* these do nothing here but are used in uiports */
3000 void
3001 ui_begin_update(void)
3002 {
3003 }
3004
3005 void
3006 ui_end_update(void)
3007 {
3008 }
3009
3010 void
3011 ui_seamless_toggle()
3012 {
3013 if (g_seamless_rdp)
3014 {
3015 /* Deactivate */
3016 while (g_seamless_windows)
3017 {
3018 XDestroyWindow(g_display, g_seamless_windows->wnd);
3019 seamless_remove_window(g_seamless_windows);
3020 }
3021 XMapWindow(g_display, g_wnd);
3022 }
3023 else
3024 {
3025 /* Activate */
3026 if (g_win_button_size)
3027 {
3028 error("SeamlessRDP mode cannot be activated when using single application mode\n");
3029 return;
3030 }
3031 if (!g_using_full_workarea)
3032 {
3033 error("SeamlessRDP mode requires a session that covers the whole screen");
3034 return;
3035 }
3036
3037 XUnmapWindow(g_display, g_wnd);
3038 seamless_send_sync();
3039 }
3040
3041 g_seamless_rdp = !g_seamless_rdp;
3042 }
3043
3044 void
3045 ui_seamless_create_window(unsigned long id, unsigned long parent, unsigned long flags)
3046 {
3047 Window wnd;
3048 XSetWindowAttributes attribs;
3049 XClassHint *classhints;
3050 XSizeHints *sizehints;
3051 long input_mask;
3052 seamless_window *sw, *sw_parent;
3053
3054 /* Ignore CREATEs for existing windows */
3055 sw = seamless_get_window_by_id(id);
3056 if (sw)
3057 return;
3058
3059 get_window_attribs(&attribs);
3060 attribs.override_redirect = False;
3061
3062 wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), -1, -1, 1, 1, 0, g_depth,
3063 InputOutput, g_visual,
3064 CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
3065 CWBorderPixel, &attribs);
3066
3067 XStoreName(g_display, wnd, "SeamlessRDP");
3068 ewmh_set_wm_name(wnd, "SeamlessRDP");
3069
3070 mwm_hide_decorations(wnd);
3071
3072 classhints = XAllocClassHint();
3073 if (classhints != NULL)
3074 {
3075 classhints->res_name = "rdesktop";
3076 classhints->res_class = "SeamlessRDP";
3077 XSetClassHint(g_display, wnd, classhints);
3078 XFree(classhints);
3079 }
3080
3081 /* WM_NORMAL_HINTS */
3082 sizehints = XAllocSizeHints();
3083 if (sizehints != NULL)
3084 {
3085 sizehints->flags = USPosition;
3086 XSetWMNormalHints(g_display, wnd, sizehints);
3087 XFree(sizehints);
3088 }
3089
3090 /* Set WM_TRANSIENT_FOR, if necessary */
3091 if ((parent != 0x00000000) && (parent != 0xFFFFFFFF))
3092 {
3093 sw_parent = seamless_get_window_by_id(parent);
3094 if (sw_parent)
3095 XSetTransientForHint(g_display, wnd, sw_parent->wnd);
3096 else
3097 warning("ui_seamless_create_window: No parent window 0x%lx\n", parent);
3098 }
3099
3100
3101 /* FIXME: Support for Input Context:s */
3102
3103 get_input_mask(&input_mask);
3104 input_mask |= PropertyChangeMask;
3105
3106 XSelectInput(g_display, wnd, input_mask);
3107
3108 /* handle the WM_DELETE_WINDOW protocol. FIXME: When killing a
3109 seamless window, we could try to close the window on the
3110 serverside, instead of terminating rdesktop */
3111 XSetWMProtocols(g_display, wnd, &g_kill_atom, 1);
3112
3113 sw = malloc(sizeof(seamless_window));
3114 sw->wnd = wnd;
3115 sw->id = id;
3116 sw->parent = parent;
3117 sw->hints = XAllocWMHints();
3118 sw->hints->flags = 0;
3119 sw->xoffset = 0;
3120 sw->yoffset = 0;
3121 sw->width = 0;
3122 sw->height = 0;
3123 sw->state = SEAMLESSRDP_NOTYETMAPPED;
3124 sw->desktop = 0;
3125 sw->next = g_seamless_windows;
3126 g_seamless_windows = sw;
3127 }
3128
3129
3130 void
3131 ui_seamless_destroy_window(unsigned long id, unsigned long flags)
3132 {
3133 seamless_window *sw;
3134
3135 sw = seamless_get_window_by_id(id);
3136 if (!sw)
3137 {
3138 warning("ui_seamless_destroy_window: No information for window 0x%lx\n", id);
3139 return;
3140 }
3141
3142 XDestroyWindow(g_display, sw->wnd);
3143 seamless_remove_window(sw);
3144 }
3145
3146
3147 void
3148 ui_seamless_move_window(unsigned long id, int x, int y, int width, int height, unsigned long flags)
3149 {
3150 seamless_window *sw;
3151
3152 sw = seamless_get_window_by_id(id);
3153 if (!sw)
3154 {
3155 warning("ui_seamless_move_window: No information for window 0x%lx\n", id);
3156 return;
3157 }
3158
3159 if (!width || !height)
3160 /* X11 windows must be at least 1x1 */
3161 return;
3162
3163 /* About MAX and MIN: Windows allows moving a window outside
3164 the desktop. This happens, for example, when maximizing an
3165 application. In this case, the position is set to something
3166 like -4,-4,1288,1032. Many WMs does not allow windows
3167 outside the desktop, however. Therefore, clip the window
3168 ourselves. */
3169 sw->xoffset = MAX(0, x);
3170 sw->yoffset = MAX(0, y);
3171 sw->width = MIN(MIN(width, width + x), g_width - sw->xoffset);
3172 sw->height = MIN(MIN(height, height + y), g_height - sw->yoffset);
3173
3174 /* If we move the window in a maximized state, then KDE won't
3175 accept restoration */
3176 switch (sw->state)
3177 {
3178 case SEAMLESSRDP_MINIMIZED:
3179 case SEAMLESSRDP_MAXIMIZED:
3180 return;
3181 }
3182
3183 /* FIXME: Perhaps use ewmh_net_moveresize_window instead */
3184 XMoveResizeWindow(g_display, sw->wnd, sw->xoffset, sw->yoffset, sw->width, sw->height);
3185 }
3186
3187
3188 void
3189 ui_seamless_settitle(unsigned long id, const char *title, unsigned long flags)
3190 {
3191 seamless_window *sw;
3192
3193 sw = seamless_get_window_by_id(id);
3194 if (!sw)
3195 {
3196 warning("ui_seamless_settitle: No information for window 0x%lx\n", id);
3197 return;
3198 }
3199
3200 /* FIXME: Might want to convert the name for non-EWMH WMs */
3201 XStoreName(g_display, sw->wnd, title);
3202 ewmh_set_wm_name(sw->wnd, title);
3203 }
3204
3205
3206 void
3207 ui_seamless_setstate(unsigned long id, unsigned int state, unsigned long flags)
3208 {
3209 seamless_window *sw;
3210
3211 sw = seamless_get_window_by_id(id);
3212 if (!sw)
3213 {
3214 warning("ui_seamless_setstate: No information for window 0x%lx\n", id);
3215 return;
3216 }
3217
3218 switch (state)
3219 {
3220 case SEAMLESSRDP_NORMAL:
3221 case SEAMLESSRDP_MAXIMIZED:
3222 ewmh_change_state(sw->wnd, state);
3223 XMapWindow(g_display, sw->wnd);
3224 break;
3225 case SEAMLESSRDP_MINIMIZED:
3226 /* EWMH says: "if an Application asks to toggle _NET_WM_STATE_HIDDEN
3227 the Window Manager should probably just ignore the request, since
3228 _NET_WM_STATE_HIDDEN is a function of some other aspect of the window
3229 such as minimization, rather than an independent state." Besides,
3230 XIconifyWindow is easier. */
3231 if (sw->state == SEAMLESSRDP_NOTYETMAPPED)
3232 {
3233 sw->hints->flags |= StateHint;
3234 sw->hints->initial_state = IconicState;
3235 XSetWMHints(g_display, sw->wnd, sw->hints);
3236 XMapWindow(g_display, sw->wnd);
3237 }
3238 else
3239 XIconifyWindow(g_display, sw->wnd, DefaultScreen(g_display));
3240 break;
3241 default:
3242 warning("SeamlessRDP: Invalid state %d\n", state);
3243 break;
3244 }
3245
3246 /* Handle popups without parents through some ewm hints */
3247 if ((sw->state == SEAMLESSRDP_NOTYETMAPPED) && (sw->parent == 0xFFFFFFFF))
3248 ewmh_set_window_popup(sw->wnd);
3249
3250 sw->state = state;
3251 }
3252
3253
3254 void
3255 ui_seamless_syncbegin(unsigned long flags)
3256 {
3257 /* Destroy all seamless windows */
3258 while (g_seamless_windows)
3259 {
3260 XDestroyWindow(g_display, g_seamless_windows->wnd);
3261 seamless_remove_window(g_seamless_windows);
3262 }
3263 }

  ViewVC Help
Powered by ViewVC 1.1.26