/[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 1159 - (show annotations)
Fri Mar 17 15:16:52 2006 UTC (18 years, 2 months ago) by ossman_
File MIME type: text/plain
File size: 80056 byte(s)
Set _NET_WM_STATE property manually for withdrawn windows.

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

  ViewVC Help
Powered by ViewVC 1.1.26