/[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 1170 - (show annotations)
Mon Mar 20 15:43:15 2006 UTC (18 years, 2 months ago) by astrand
File MIME type: text/plain
File size: 80635 byte(s)
Cleaned up SeamlessRDP functions: All utility functions are prefixed with sw_.

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

  ViewVC Help
Powered by ViewVC 1.1.26