/[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 1183 - (show annotations)
Wed Mar 22 09:49:21 2006 UTC (18 years, 3 months ago) by ossman_
File MIME type: text/plain
File size: 83341 byte(s)
Fix memory leak spotted by valgrind.

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

  ViewVC Help
Powered by ViewVC 1.1.26