/[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 1155 - (show annotations)
Fri Mar 17 10:48:11 2006 UTC (18 years, 3 months ago) by ossman_
File MIME type: text/plain
File size: 79096 byte(s)
Add some client side book keeping of window ordering and focus in order to
not send unnecessary updates. This is done to avoid side effects for commands
that would seem to be no-ops.

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

  ViewVC Help
Powered by ViewVC 1.1.26