/[rdesktop]/sourceforge.net/trunk/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

Annotation of /sourceforge.net/trunk/rdesktop/xwin.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1372 - (hide annotations)
Mon Jan 8 04:47:06 2007 UTC (17 years, 4 months ago) by jsorg71
File MIME type: text/plain
File size: 84173 byte(s)
prefix BOOL with RD_

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

  ViewVC Help
Powered by ViewVC 1.1.26