/[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 1154 - (show annotations)
Fri Mar 17 09:56:20 2006 UTC (18 years, 2 months ago) by ossman_
File MIME type: text/plain
File size: 78140 byte(s)
Detect focus changes and send FOCUS to the server.

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

  ViewVC Help
Powered by ViewVC 1.1.26