/[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 828 - (hide annotations)
Sun Mar 6 21:11:18 2005 UTC (19 years, 2 months ago) by stargo
File MIME type: text/plain
File size: 51794 byte(s)
bump version to 1.4.0
change year in files
add missing docu for updated commandline-flags
update changelog

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 stargo 828 Copyright (C) Matthew Chapman 1999-2005
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 matthewc 537 #include <unistd.h>
24 stargo 609 #include <sys/time.h>
25 matty 10 #include <time.h>
26 matty 33 #include <errno.h>
27 stargo 603 #include <strings.h>
28 matty 10 #include "rdesktop.h"
29 forsberg 415 #include "xproto.h"
30 matty 6
31 jsorg71 447 extern int g_width;
32     extern int g_height;
33 stargo 800 extern int g_xpos;
34     extern int g_ypos;
35 jsorg71 447 extern BOOL g_sendmotion;
36     extern BOOL g_fullscreen;
37 jsorg71 450 extern BOOL g_grab_keyboard;
38     extern BOOL g_hide_decorations;
39     extern char g_title[];
40 jsorg71 438 extern int g_server_bpp;
41 jsorg71 450 extern int g_win_button_size;
42 matty 10
43 jsorg71 450 Display *g_display;
44     Time g_last_gesturetime;
45     static int g_x_socket;
46     static Screen *g_screen;
47     Window g_wnd;
48 stargo 648 extern uint32 g_embed_wnd;
49 matthewc 481 BOOL g_enable_compose = False;
50 astrand 691 BOOL g_Unobscured; /* used for screenblt */
51 stargo 576 static GC g_gc = NULL;
52 jsorg71 725 static GC g_create_bitmap_gc = NULL;
53     static GC g_create_glyph_gc = NULL;
54 jsorg71 450 static Visual *g_visual;
55     static int g_depth;
56     static int g_bpp;
57     static XIM g_IM;
58     static XIC g_IC;
59     static XModifierKeymap *g_mod_map;
60     static Cursor g_current_cursor;
61 stargo 576 static HCURSOR g_null_cursor = NULL;
62 jsorg71 450 static Atom g_protocol_atom, g_kill_atom;
63 matthewc 481 static BOOL g_focused;
64     static BOOL g_mouse_in_wnd;
65 astrand 651 static BOOL g_arch_match = False; /* set to True if RGB XServer and little endian */
66 matty 29
67 matty 33 /* endianness */
68 jsorg71 450 static BOOL g_host_be;
69     static BOOL g_xserver_be;
70 matthewc 527 static int g_red_shift_r, g_blue_shift_r, g_green_shift_r;
71     static int g_red_shift_l, g_blue_shift_l, g_green_shift_l;
72 matty 33
73     /* software backing store */
74 stargo 648 extern BOOL g_ownbackstore;
75 stargo 603 static Pixmap g_backstore = 0;
76 matty 31
77 astrand 342 /* Moving in single app mode */
78 jsorg71 450 static BOOL g_moving_wnd;
79     static int g_move_x_offset = 0;
80     static int g_move_y_offset = 0;
81 astrand 342
82 matthewc 474 #ifdef WITH_RDPSND
83     extern int g_dsp_fd;
84     extern BOOL g_dsp_busy;
85 stargo 501 extern BOOL g_rdpsnd;
86 matthewc 474 #endif
87    
88 astrand 262 /* MWM decorations */
89     #define MWM_HINTS_DECORATIONS (1L << 1)
90     #define PROP_MOTIF_WM_HINTS_ELEMENTS 5
91     typedef struct
92     {
93 matthewc 301 uint32 flags;
94     uint32 functions;
95     uint32 decorations;
96     sint32 inputMode;
97     uint32 status;
98 astrand 262 }
99     PropMotifWmHints;
100    
101 jsorg71 316 typedef struct
102     {
103     uint32 red;
104     uint32 green;
105     uint32 blue;
106     }
107     PixelColour;
108 astrand 262
109 forsberg 415
110 matty 31 #define FILL_RECTANGLE(x,y,cx,cy)\
111     { \
112 jsorg71 450 XFillRectangle(g_display, g_wnd, g_gc, x, y, cx, cy); \
113     if (g_ownbackstore) \
114     XFillRectangle(g_display, g_backstore, g_gc, x, y, cx, cy); \
115 matty 31 }
116    
117 matthewc 296 #define FILL_RECTANGLE_BACKSTORE(x,y,cx,cy)\
118 jsorg71 281 { \
119 jsorg71 450 XFillRectangle(g_display, g_ownbackstore ? g_backstore : g_wnd, g_gc, x, y, cx, cy); \
120 jsorg71 281 }
121    
122 matty 33 /* colour maps */
123 stargo 648 extern BOOL g_owncolmap;
124 jsorg71 450 static Colormap g_xcolmap;
125     static uint32 *g_colmap = NULL;
126 matty 10
127 matthewc 527 #define TRANSLATE(col) ( g_server_bpp != 8 ? translate_colour(col) : g_owncolmap ? col : g_colmap[col] )
128 jsorg71 450 #define SET_FOREGROUND(col) XSetForeground(g_display, g_gc, TRANSLATE(col));
129     #define SET_BACKGROUND(col) XSetBackground(g_display, g_gc, TRANSLATE(col));
130 matty 28
131     static int rop2_map[] = {
132     GXclear, /* 0 */
133     GXnor, /* DPon */
134     GXandInverted, /* DPna */
135     GXcopyInverted, /* Pn */
136     GXandReverse, /* PDna */
137     GXinvert, /* Dn */
138     GXxor, /* DPx */
139     GXnand, /* DPan */
140     GXand, /* DPa */
141     GXequiv, /* DPxn */
142     GXnoop, /* D */
143     GXorInverted, /* DPno */
144     GXcopy, /* P */
145     GXorReverse, /* PDno */
146     GXor, /* DPo */
147     GXset /* 1 */
148     };
149    
150 jsorg71 450 #define SET_FUNCTION(rop2) { if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, rop2_map[rop2]); }
151     #define RESET_FUNCTION(rop2) { if (rop2 != ROP2_COPY) XSetFunction(g_display, g_gc, GXcopy); }
152 matty 29
153 astrand 319 static void
154 astrand 262 mwm_hide_decorations(void)
155     {
156     PropMotifWmHints motif_hints;
157     Atom hintsatom;
158    
159     /* setup the property */
160     motif_hints.flags = MWM_HINTS_DECORATIONS;
161     motif_hints.decorations = 0;
162    
163     /* get the atom for the property */
164 jsorg71 450 hintsatom = XInternAtom(g_display, "_MOTIF_WM_HINTS", False);
165 astrand 262 if (!hintsatom)
166     {
167 matthewc 297 warning("Failed to get atom _MOTIF_WM_HINTS: probably your window manager does not support MWM hints\n");
168 astrand 262 return;
169     }
170    
171 jsorg71 450 XChangeProperty(g_display, g_wnd, hintsatom, hintsatom, 32, PropModeReplace,
172 astrand 262 (unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
173     }
174    
175 jdmeijer 821 #define SPLITCOLOUR15(colour, rv) \
176     { \
177     rv.red = ((colour >> 7) & 0xf8) | ((colour >> 12) & 0x7); \
178     rv.green = ((colour >> 2) & 0xf8) | ((colour >> 8) & 0x7); \
179     rv.blue = ((colour << 3) & 0xf8) | ((colour >> 2) & 0x7); \
180 jsorg71 311 }
181    
182 jdmeijer 821 #define SPLITCOLOUR16(colour, rv) \
183     { \
184     rv.red = ((colour >> 8) & 0xf8) | ((colour >> 13) & 0x7); \
185     rv.green = ((colour >> 3) & 0xfc) | ((colour >> 9) & 0x3); \
186     rv.blue = ((colour << 3) & 0xf8) | ((colour >> 2) & 0x7); \
187     } \
188 jsorg71 311
189 jdmeijer 821 #define SPLITCOLOUR24(colour, rv) \
190     { \
191     rv.blue = (colour & 0xff0000) >> 16; \
192     rv.green = (colour & 0x00ff00) >> 8; \
193     rv.red = (colour & 0x0000ff); \
194 jsorg71 311 }
195    
196 jdmeijer 821 #define MAKECOLOUR(pc) \
197     ((pc.red >> g_red_shift_r) << g_red_shift_l) \
198     | ((pc.green >> g_green_shift_r) << g_green_shift_l) \
199     | ((pc.blue >> g_blue_shift_r) << g_blue_shift_l) \
200 jsorg71 316
201 jsorg71 311 #define BSWAP16(x) { x = (((x & 0xff) << 8) | (x >> 8)); }
202 stargo 534 #define BSWAP24(x) { x = (((x & 0xff) << 16) | (x >> 16) | (x & 0xff00)); }
203 jsorg71 311 #define BSWAP32(x) { x = (((x & 0xff00ff) << 8) | ((x >> 8) & 0xff00ff)); \
204     x = (x << 16) | (x >> 16); }
205    
206 jdmeijer 821 #define BOUT16(o, x) { *(o++) = x >> 8; *(o++) = x; }
207     #define BOUT24(o, x) { *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }
208     #define BOUT32(o, x) { *(o++) = x >> 24; *(o++) = x >> 16; *(o++) = x >> 8; *(o++) = x; }
209     #define LOUT16(o, x) { *(o++) = x; *(o++) = x >> 8; }
210     #define LOUT24(o, x) { *(o++) = x; *(o++) = x >> 8; *(o++) = x >> 16; }
211     #define LOUT32(o, x) { *(o++) = x; *(o++) = x >> 8; *(o++) = x >> 16; *(o++) = x >> 24; }
212    
213 jsorg71 311 static uint32
214     translate_colour(uint32 colour)
215     {
216 matthewc 527 PixelColour pc;
217 jsorg71 438 switch (g_server_bpp)
218 jsorg71 311 {
219 jsorg71 316 case 15:
220 jdmeijer 821 SPLITCOLOUR15(colour, pc);
221 jsorg71 316 break;
222 jsorg71 311 case 16:
223 jdmeijer 821 SPLITCOLOUR16(colour, pc);
224 jsorg71 311 break;
225     case 24:
226 jdmeijer 821 SPLITCOLOUR24(colour, pc);
227 jsorg71 311 break;
228     }
229 jdmeijer 821 return MAKECOLOUR(pc);
230 jsorg71 311 }
231    
232 jsorg71 709 /* indent is confused by UNROLL8 */
233     /* *INDENT-OFF* */
234    
235     /* repeat and unroll, similar to bitmap.c */
236     /* potentialy any of the following translate */
237     /* functions can use repeat but just doing */
238     /* the most common ones */
239    
240 jsorg71 679 #define UNROLL8(stm) { stm stm stm stm stm stm stm stm }
241 jsorg71 709 /* 2 byte output repeat */
242     #define REPEAT2(stm) \
243 jsorg71 679 { \
244 jsorg71 709 while (out <= end - 8 * 2) \
245     UNROLL8(stm) \
246     while (out < end) \
247     { stm } \
248     }
249 jdmeijer 821 /* 3 byte output repeat */
250     #define REPEAT3(stm) \
251     { \
252     while (out <= end - 8 * 3) \
253     UNROLL8(stm) \
254     while (out < end) \
255     { stm } \
256     }
257 jsorg71 709 /* 4 byte output repeat */
258     #define REPEAT4(stm) \
259     { \
260 jsorg71 679 while (out <= end - 8 * 4) \
261     UNROLL8(stm) \
262     while (out < end) \
263     { stm } \
264     }
265 jdmeijer 821 /* *INDENT-ON* */
266 jsorg71 679
267 matty 28 static void
268 jdmeijer 821 translate8to8(const uint8 * data, uint8 * out, uint8 * end)
269 matty 28 {
270 matty 29 while (out < end)
271 jsorg71 450 *(out++) = (uint8) g_colmap[*(data++)];
272 matty 29 }
273 matty 28
274 matty 29 static void
275 jdmeijer 821 translate8to16(const uint8 * data, uint8 * out, uint8 * end)
276 matty 29 {
277 stargo 521 uint16 value;
278    
279 jsorg71 679 if (g_arch_match)
280 stargo 521 {
281 jdmeijer 821 /* *INDENT-OFF* */
282 jsorg71 709 REPEAT2
283     (
284     *((uint16 *) out) = g_colmap[*(data++)];
285     out += 2;
286     )
287 jdmeijer 821 /* *INDENT-ON* */
288 jsorg71 643 }
289 jsorg71 709 else if (g_xserver_be)
290 jsorg71 643 {
291 jsorg71 709 while (out < end)
292     {
293     value = (uint16) g_colmap[*(data++)];
294 jdmeijer 821 BOUT16(out, value);
295 jsorg71 709 }
296 stargo 521 }
297 jsorg71 709 else
298     {
299     while (out < end)
300     {
301     value = (uint16) g_colmap[*(data++)];
302 jdmeijer 821 LOUT16(out, value);
303 jsorg71 709 }
304     }
305 matty 29 }
306    
307 jsorg71 316 /* little endian - conversion happens when colourmap is built */
308 jsorg71 311 static void
309 jdmeijer 821 translate8to24(const uint8 * data, uint8 * out, uint8 * end)
310 jsorg71 311 {
311 jsorg71 316 uint32 value;
312    
313 jsorg71 643 if (g_xserver_be)
314 jsorg71 316 {
315 jsorg71 643 while (out < end)
316 stargo 521 {
317 jsorg71 643 value = g_colmap[*(data++)];
318 jdmeijer 821 BOUT24(out, value);
319 stargo 521 }
320 jsorg71 643 }
321     else
322     {
323     while (out < end)
324 stargo 521 {
325 jsorg71 643 value = g_colmap[*(data++)];
326 jdmeijer 821 LOUT24(out, value);
327 stargo 521 }
328 jsorg71 316 }
329 jsorg71 311 }
330    
331 matty 29 static void
332 jdmeijer 821 translate8to32(const uint8 * data, uint8 * out, uint8 * end)
333 matty 29 {
334 stargo 521 uint32 value;
335    
336 jsorg71 679 if (g_arch_match)
337 stargo 521 {
338 jdmeijer 821 /* *INDENT-OFF* */
339 jsorg71 709 REPEAT4
340     (
341     *((uint32 *) out) = g_colmap[*(data++)];
342     out += 4;
343     )
344 jdmeijer 821 /* *INDENT-ON* */
345 jsorg71 643 }
346 jsorg71 709 else if (g_xserver_be)
347 jsorg71 643 {
348 jsorg71 709 while (out < end)
349     {
350     value = g_colmap[*(data++)];
351 jdmeijer 821 BOUT32(out, value);
352 jsorg71 709 }
353 stargo 521 }
354 jsorg71 709 else
355     {
356     while (out < end)
357     {
358     value = g_colmap[*(data++)];
359 jdmeijer 821 LOUT32(out, value);
360 jsorg71 709 }
361     }
362 jsorg71 316 }
363    
364     static void
365 jdmeijer 821 translate15to16(const uint16 * data, uint8 * out, uint8 * end)
366 jsorg71 316 {
367 jsorg71 483 uint16 pixel;
368     uint16 value;
369 jdmeijer 821 PixelColour pc;
370 jsorg71 483
371 jdmeijer 821 if (g_xserver_be)
372 jsorg71 483 {
373 jdmeijer 821 while (out < end)
374 jsorg71 483 {
375 jdmeijer 821 pixel = *(data++);
376     if (g_host_be)
377     {
378     BSWAP16(pixel);
379     }
380     SPLITCOLOUR15(pixel, pc);
381     value = MAKECOLOUR(pc);
382     BOUT16(out, value);
383 stargo 521 }
384 jdmeijer 821 }
385     else
386     {
387     while (out < end)
388 jsorg71 483 {
389 jdmeijer 821 pixel = *(data++);
390     if (g_host_be)
391     {
392     BSWAP16(pixel);
393     }
394     SPLITCOLOUR15(pixel, pc);
395     value = MAKECOLOUR(pc);
396     LOUT16(out, value);
397 jsorg71 483 }
398     }
399 jsorg71 316 }
400    
401     static void
402 jdmeijer 821 translate15to24(const uint16 * data, uint8 * out, uint8 * end)
403 jsorg71 316 {
404 matty 29 uint32 value;
405 jsorg71 483 uint16 pixel;
406 jdmeijer 821 PixelColour pc;
407 matty 29
408 jdmeijer 821 if (g_arch_match)
409 matty 28 {
410 jdmeijer 821 /* *INDENT-OFF* */
411     REPEAT3
412     (
413     pixel = *(data++);
414     SPLITCOLOUR15(pixel, pc);
415     *(out++) = pc.blue;
416     *(out++) = pc.green;
417     *(out++) = pc.red;
418     )
419     /* *INDENT-ON* */
420     }
421     else if (g_xserver_be)
422     {
423     while (out < end)
424 jsorg71 483 {
425 jdmeijer 821 pixel = *(data++);
426     if (g_host_be)
427     {
428     BSWAP16(pixel);
429     }
430     SPLITCOLOUR15(pixel, pc);
431     value = MAKECOLOUR(pc);
432     BOUT24(out, value);
433 stargo 521 }
434 jdmeijer 821 }
435     else
436     {
437     while (out < end)
438 jsorg71 483 {
439 jdmeijer 821 pixel = *(data++);
440     if (g_host_be)
441     {
442     BSWAP16(pixel);
443     }
444     SPLITCOLOUR15(pixel, pc);
445     value = MAKECOLOUR(pc);
446     LOUT24(out, value);
447 jsorg71 483 }
448 matty 28 }
449     }
450    
451     static void
452 jdmeijer 821 translate15to32(const uint16 * data, uint8 * out, uint8 * end)
453 jsorg71 316 {
454 jsorg71 472 uint16 pixel;
455 jsorg71 483 uint32 value;
456 jdmeijer 821 PixelColour pc;
457 jsorg71 472
458 jdmeijer 821 if (g_arch_match)
459 jsorg71 472 {
460 jdmeijer 821 /* *INDENT-OFF* */
461     REPEAT4
462     (
463     pixel = *(data++);
464     SPLITCOLOUR15(pixel, pc);
465     *(out++) = pc.blue;
466     *(out++) = pc.green;
467     *(out++) = pc.red;
468     *(out++) = 0;
469     )
470     /* *INDENT-ON* */
471     }
472     else if (g_xserver_be)
473     {
474     while (out < end)
475 jsorg71 472 {
476 jdmeijer 821 pixel = *(data++);
477     if (g_host_be)
478     {
479     BSWAP16(pixel);
480     }
481     SPLITCOLOUR15(pixel, pc);
482     value = MAKECOLOUR(pc);
483     BOUT32(out, value);
484 jsorg71 472 }
485 jdmeijer 821 }
486     else
487     {
488     while (out < end)
489 jsorg71 483 {
490 jdmeijer 821 pixel = *(data++);
491     if (g_host_be)
492     {
493     BSWAP16(pixel);
494     }
495     SPLITCOLOUR15(pixel, pc);
496     value = MAKECOLOUR(pc);
497     LOUT32(out, value);
498 jsorg71 483 }
499 jsorg71 472 }
500 jsorg71 316 }
501    
502     static void
503 jdmeijer 821 translate16to16(const uint16 * data, uint8 * out, uint8 * end)
504 jsorg71 316 {
505 matthewc 528 uint16 pixel;
506 jsorg71 483 uint16 value;
507 jdmeijer 821 PixelColour pc;
508 jsorg71 483
509 jdmeijer 821 if (g_xserver_be)
510 jsorg71 483 {
511 matthewc 528 if (g_host_be)
512 jsorg71 483 {
513 jdmeijer 821 while (out < end)
514     {
515     pixel = *(data++);
516     BSWAP16(pixel);
517     SPLITCOLOUR16(pixel, pc);
518     value = MAKECOLOUR(pc);
519     BOUT16(out, value);
520     }
521 jsorg71 483 }
522 jdmeijer 821 else
523 jsorg71 483 {
524 jdmeijer 821 while (out < end)
525     {
526     pixel = *(data++);
527     SPLITCOLOUR16(pixel, pc);
528     value = MAKECOLOUR(pc);
529     BOUT16(out, value);
530     }
531 jsorg71 483 }
532 jdmeijer 821 }
533     else
534     {
535     if (g_host_be)
536     {
537     while (out < end)
538     {
539     pixel = *(data++);
540     BSWAP16(pixel);
541     SPLITCOLOUR16(pixel, pc);
542     value = MAKECOLOUR(pc);
543     LOUT16(out, value);
544     }
545     }
546 matthewc 528 else
547     {
548 jdmeijer 821 while (out < end)
549     {
550     pixel = *(data++);
551     SPLITCOLOUR16(pixel, pc);
552     value = MAKECOLOUR(pc);
553     LOUT16(out, value);
554     }
555 matthewc 528 }
556 jsorg71 483 }
557 jsorg71 316 }
558    
559     static void
560 jdmeijer 821 translate16to24(const uint16 * data, uint8 * out, uint8 * end)
561 matty 28 {
562 jsorg71 311 uint32 value;
563 jsorg71 483 uint16 pixel;
564 jdmeijer 821 PixelColour pc;
565 jsorg71 311
566 jdmeijer 821 if (g_arch_match)
567 jsorg71 311 {
568 jdmeijer 821 /* *INDENT-OFF* */
569     REPEAT3
570     (
571     pixel = *(data++);
572     SPLITCOLOUR16(pixel, pc);
573     *(out++) = pc.blue;
574     *(out++) = pc.green;
575     *(out++) = pc.red;
576     )
577     /* *INDENT-ON* */
578     }
579     else if (g_xserver_be)
580     {
581 jsorg71 483 if (g_host_be)
582     {
583 jdmeijer 821 while (out < end)
584     {
585     pixel = *(data++);
586     BSWAP16(pixel);
587     SPLITCOLOUR16(pixel, pc);
588     value = MAKECOLOUR(pc);
589     BOUT24(out, value);
590     }
591 stargo 521 }
592 jdmeijer 821 else
593 jsorg71 483 {
594 jdmeijer 821 while (out < end)
595     {
596     pixel = *(data++);
597     SPLITCOLOUR16(pixel, pc);
598     value = MAKECOLOUR(pc);
599     BOUT24(out, value);
600     }
601 jsorg71 483 }
602 jdmeijer 821 }
603     else
604     {
605     if (g_host_be)
606     {
607     while (out < end)
608     {
609     pixel = *(data++);
610     BSWAP16(pixel);
611     SPLITCOLOUR16(pixel, pc);
612     value = MAKECOLOUR(pc);
613     LOUT24(out, value);
614     }
615     }
616 jsorg71 483 else
617     {
618 jdmeijer 821 while (out < end)
619     {
620     pixel = *(data++);
621     SPLITCOLOUR16(pixel, pc);
622     value = MAKECOLOUR(pc);
623     LOUT24(out, value);
624     }
625 jsorg71 483 }
626 jsorg71 311 }
627     }
628    
629     static void
630 jdmeijer 821 translate16to32(const uint16 * data, uint8 * out, uint8 * end)
631 jsorg71 311 {
632 jsorg71 472 uint16 pixel;
633 jsorg71 483 uint32 value;
634 jdmeijer 821 PixelColour pc;
635 jsorg71 472
636 jdmeijer 821 if (g_arch_match)
637 jsorg71 472 {
638 jdmeijer 821 /* *INDENT-OFF* */
639     REPEAT4
640     (
641     pixel = *(data++);
642     SPLITCOLOUR16(pixel, pc);
643     *(out++) = pc.blue;
644     *(out++) = pc.green;
645     *(out++) = pc.red;
646     *(out++) = 0;
647     )
648     /* *INDENT-ON* */
649     }
650     else if (g_xserver_be)
651     {
652 jsorg71 472 if (g_host_be)
653     {
654 jdmeijer 821 while (out < end)
655     {
656     pixel = *(data++);
657     BSWAP16(pixel);
658     SPLITCOLOUR16(pixel, pc);
659     value = MAKECOLOUR(pc);
660     BOUT32(out, value);
661     }
662 stargo 534 }
663 jdmeijer 821 else
664 jsorg71 472 {
665 stargo 823 while (out < end)
666 jdmeijer 821 {
667     pixel = *(data++);
668     SPLITCOLOUR16(pixel, pc);
669     value = MAKECOLOUR(pc);
670     BOUT32(out, value);
671     }
672 astrand 499 }
673 jdmeijer 821 }
674     else
675     {
676     if (g_host_be)
677     {
678     while (out < end)
679     {
680     pixel = *(data++);
681     BSWAP16(pixel);
682     SPLITCOLOUR16(pixel, pc);
683     value = MAKECOLOUR(pc);
684     LOUT32(out, value);
685     }
686     }
687 astrand 499 else
688     {
689 jdmeijer 821 while (out < end)
690     {
691     pixel = *(data++);
692     SPLITCOLOUR16(pixel, pc);
693     value = MAKECOLOUR(pc);
694     LOUT32(out, value);
695     }
696 astrand 499 }
697     }
698 matty 28 }
699    
700 jsorg71 311 static void
701 jdmeijer 821 translate24to16(const uint8 * data, uint8 * out, uint8 * end)
702 jsorg71 311 {
703 jsorg71 316 uint32 pixel = 0;
704 jsorg71 483 uint16 value;
705 jdmeijer 821 PixelColour pc;
706    
707 jsorg71 311 while (out < end)
708 jsorg71 316 {
709     pixel = *(data++) << 16;
710     pixel |= *(data++) << 8;
711     pixel |= *(data++);
712 jdmeijer 821 SPLITCOLOUR24(pixel, pc);
713     value = MAKECOLOUR(pc);
714 jsorg71 483 if (g_xserver_be)
715     {
716 jdmeijer 821 BOUT16(out, value);
717 jsorg71 483 }
718     else
719     {
720 jdmeijer 821 LOUT16(out, value);
721 jsorg71 483 }
722 jsorg71 316 }
723 jsorg71 311 }
724    
725 jsorg71 316 static void
726 jdmeijer 821 translate24to24(const uint8 * data, uint8 * out, uint8 * end)
727 jsorg71 316 {
728 stargo 534 uint32 pixel;
729     uint32 value;
730 jdmeijer 821 PixelColour pc;
731 stargo 534
732 jdmeijer 821 if (g_xserver_be)
733 jsorg71 316 {
734 jdmeijer 821 while (out < end)
735 stargo 534 {
736 jdmeijer 821 pixel = *(data++) << 16;
737     pixel |= *(data++) << 8;
738     pixel |= *(data++);
739     SPLITCOLOUR24(pixel, pc);
740     value = MAKECOLOUR(pc);
741     BOUT24(out, value);
742 stargo 534 }
743 jdmeijer 821 }
744     else
745     {
746     while (out < end)
747 stargo 534 {
748 jdmeijer 821 pixel = *(data++) << 16;
749     pixel |= *(data++) << 8;
750     pixel |= *(data++);
751     SPLITCOLOUR24(pixel, pc);
752     value = MAKECOLOUR(pc);
753     LOUT24(out, value);
754 stargo 534 }
755 jsorg71 316 }
756     }
757    
758     static void
759 jdmeijer 821 translate24to32(const uint8 * data, uint8 * out, uint8 * end)
760 jsorg71 316 {
761 stargo 534 uint32 pixel;
762     uint32 value;
763 jdmeijer 821 PixelColour pc;
764 stargo 534
765 jdmeijer 821 if (g_arch_match)
766 jsorg71 316 {
767 jdmeijer 821 /* *INDENT-OFF* */
768 stargo 822 #ifdef NEED_ALIGN
769 jdmeijer 821 REPEAT4
770     (
771     *(out++) = *(data++);
772     *(out++) = *(data++);
773     *(out++) = *(data++);
774     *(out++) = 0;
775 stargo 822 )
776 jdmeijer 821 #else
777 stargo 822 REPEAT4
778     (
779 jdmeijer 821 *((uint32 *) out) = *((uint32 *) data);
780     out += 4;
781     data += 3;
782 stargo 822 )
783 jdmeijer 821 #endif
784     /* *INDENT-ON* */
785     }
786     else if (g_xserver_be)
787     {
788     while (out < end)
789 jsorg71 472 {
790 jdmeijer 821 pixel = *(data++) << 16;
791     pixel |= *(data++) << 8;
792     pixel |= *(data++);
793     SPLITCOLOUR24(pixel, pc);
794     value = MAKECOLOUR(pc);
795     BOUT32(out, value);
796 jsorg71 472 }
797 jdmeijer 821 }
798     else
799     {
800     while (out < end)
801 jsorg71 472 {
802 jdmeijer 821 pixel = *(data++) << 16;
803     pixel |= *(data++) << 8;
804     pixel |= *(data++);
805     SPLITCOLOUR24(pixel, pc);
806     value = MAKECOLOUR(pc);
807     LOUT32(out, value);
808 jsorg71 472 }
809 jsorg71 316 }
810     }
811    
812 matty 29 static uint8 *
813 astrand 64 translate_image(int width, int height, uint8 * data)
814 matty 28 {
815 jsorg71 644 int size;
816     uint8 *out;
817     uint8 *end;
818 matty 29
819 jsorg71 644 /* if server and xserver bpp match, */
820     /* and arch(endian) matches, no need to translate */
821     /* just return data */
822 jsorg71 645 if (g_arch_match)
823     {
824     if (g_depth == 15 && g_server_bpp == 15)
825     return data;
826     if (g_depth == 16 && g_server_bpp == 16)
827     return data;
828 jdmeijer 821 if (g_depth == 24 && g_bpp == 24 && g_server_bpp == 24)
829     return data;
830 jsorg71 645 }
831 jsorg71 644
832     size = width * height * (g_bpp / 8);
833     out = (uint8 *) xmalloc(size);
834     end = out + size;
835    
836 jsorg71 438 switch (g_server_bpp)
837 jsorg71 311 {
838 jsorg71 316 case 24:
839 jsorg71 450 switch (g_bpp)
840 jsorg71 316 {
841     case 32:
842 jsorg71 483 translate24to32(data, out, end);
843 jsorg71 316 break;
844     case 24:
845     translate24to24(data, out, end);
846     break;
847     case 16:
848 jsorg71 483 translate24to16(data, out, end);
849 jsorg71 316 break;
850     }
851 matty 29 break;
852     case 16:
853 jsorg71 450 switch (g_bpp)
854 jsorg71 316 {
855     case 32:
856 jsorg71 483 translate16to32((uint16 *) data, out, end);
857 jsorg71 316 break;
858     case 24:
859     translate16to24((uint16 *) data, out, end);
860     break;
861     case 16:
862 matthewc 528 translate16to16((uint16 *) data, out, end);
863 jsorg71 316 break;
864     }
865 matty 29 break;
866 jsorg71 316 case 15:
867 jsorg71 450 switch (g_bpp)
868 jsorg71 316 {
869     case 32:
870 jsorg71 483 translate15to32((uint16 *) data, out, end);
871 jsorg71 316 break;
872     case 24:
873     translate15to24((uint16 *) data, out, end);
874     break;
875     case 16:
876 jsorg71 483 translate15to16((uint16 *) data, out, end);
877 jsorg71 316 break;
878     }
879 matty 29 break;
880 jsorg71 316 case 8:
881 jsorg71 450 switch (g_bpp)
882 jsorg71 316 {
883     case 8:
884     translate8to8(data, out, end);
885     break;
886     case 16:
887 stargo 521 translate8to16(data, out, end);
888 jsorg71 316 break;
889     case 24:
890     translate8to24(data, out, end);
891     break;
892     case 32:
893 stargo 521 translate8to32(data, out, end);
894 jsorg71 316 break;
895     }
896 matty 29 break;
897     }
898     return out;
899 matty 28 }
900    
901 astrand 118 BOOL
902 matthewc 209 get_key_state(unsigned int state, uint32 keysym)
903 astrand 102 {
904 matthewc 203 int modifierpos, key, keysymMask = 0;
905 astrand 102 int offset;
906    
907 jsorg71 450 KeyCode keycode = XKeysymToKeycode(g_display, keysym);
908 astrand 102
909     if (keycode == NoSymbol)
910     return False;
911    
912     for (modifierpos = 0; modifierpos < 8; modifierpos++)
913     {
914 jsorg71 450 offset = g_mod_map->max_keypermod * modifierpos;
915 astrand 102
916 jsorg71 450 for (key = 0; key < g_mod_map->max_keypermod; key++)
917 astrand 102 {
918 jsorg71 450 if (g_mod_map->modifiermap[offset + key] == keycode)
919 matthewc 203 keysymMask |= 1 << modifierpos;
920 astrand 102 }
921     }
922    
923 matthewc 203 return (state & keysymMask) ? True : False;
924 astrand 102 }
925    
926 matthewc 527 static void
927     calculate_shifts(uint32 mask, int *shift_r, int *shift_l)
928     {
929     *shift_l = ffs(mask) - 1;
930     mask >>= *shift_l;
931     *shift_r = 8 - ffs(mask & ~(mask >> 1));
932     }
933    
934 jsorg71 81 BOOL
935 matthewc 192 ui_init(void)
936 jsorg71 81 {
937 matthewc 527 XVisualInfo vi;
938 matthewc 121 XPixmapFormatValues *pfm;
939     uint16 test;
940 stargo 565 int i, screen_num, nvisuals;
941     XVisualInfo *vmatches = NULL;
942     XVisualInfo template;
943     Bool TrueColorVisual = False;
944 matthewc 121
945 jsorg71 450 g_display = XOpenDisplay(NULL);
946     if (g_display == NULL)
947 jsorg71 81 {
948 matthewc 210 error("Failed to open display: %s\n", XDisplayName(NULL));
949 jsorg71 81 return False;
950     }
951 matthewc 121
952 matthewc 527 screen_num = DefaultScreen(g_display);
953 jsorg71 450 g_x_socket = ConnectionNumber(g_display);
954 matthewc 527 g_screen = ScreenOfDisplay(g_display, screen_num);
955 jsorg71 450 g_depth = DefaultDepthOfScreen(g_screen);
956 matthewc 121
957 stargo 565 /* Search for best TrueColor depth */
958     template.class = TrueColor;
959     vmatches = XGetVisualInfo(g_display, VisualClassMask, &template, &nvisuals);
960    
961     nvisuals--;
962     while (nvisuals >= 0)
963 matthewc 527 {
964 stargo 565 if ((vmatches + nvisuals)->depth > g_depth)
965     {
966     g_depth = (vmatches + nvisuals)->depth;
967     }
968     nvisuals--;
969     TrueColorVisual = True;
970     }
971    
972 jsorg71 644 test = 1;
973     g_host_be = !(BOOL) (*(uint8 *) (&test));
974     g_xserver_be = (ImageByteOrder(g_display) == MSBFirst);
975    
976 astrand 580 if ((g_server_bpp == 8) && ((!TrueColorVisual) || (g_depth <= 8)))
977 stargo 565 {
978     /* we use a colourmap, so the default visual should do */
979 matthewc 527 g_visual = DefaultVisualOfScreen(g_screen);
980 stargo 565 g_depth = DefaultDepthOfScreen(g_screen);
981    
982     /* Do not allocate colours on a TrueColor visual */
983     if (g_visual->class == TrueColor)
984     {
985     g_owncolmap = False;
986     }
987 matthewc 527 }
988     else
989     {
990     /* need a truecolour visual */
991     if (!XMatchVisualInfo(g_display, screen_num, g_depth, TrueColor, &vi))
992     {
993     error("The display does not support true colour - high colour support unavailable.\n");
994     return False;
995     }
996    
997     g_visual = vi.visual;
998     g_owncolmap = False;
999 astrand 532 calculate_shifts(vi.red_mask, &g_red_shift_r, &g_red_shift_l);
1000     calculate_shifts(vi.blue_mask, &g_blue_shift_r, &g_blue_shift_l);
1001 matthewc 527 calculate_shifts(vi.green_mask, &g_green_shift_r, &g_green_shift_l);
1002 jsorg71 644
1003 jdmeijer 821 /* if RGB video and everything is little endian */
1004     if ((vi.red_mask > vi.green_mask && vi.green_mask > vi.blue_mask) &&
1005     !g_xserver_be && !g_host_be)
1006     {
1007     if (g_depth <= 16 || (g_red_shift_l == 16 && g_green_shift_l == 8 &&
1008     g_blue_shift_l == 0))
1009     {
1010 jsorg71 644 g_arch_match = True;
1011 jdmeijer 821 }
1012     }
1013    
1014     if (g_arch_match)
1015     {
1016     DEBUG(("Architectures match, enabling little endian optimisations.\n"));
1017     }
1018 matthewc 527 }
1019    
1020 jsorg71 450 pfm = XListPixmapFormats(g_display, &i);
1021 matthewc 121 if (pfm != NULL)
1022     {
1023     /* Use maximum bpp for this depth - this is generally
1024     desirable, e.g. 24 bits->32 bits. */
1025     while (i--)
1026     {
1027 jsorg71 450 if ((pfm[i].depth == g_depth) && (pfm[i].bits_per_pixel > g_bpp))
1028 matthewc 121 {
1029 jsorg71 450 g_bpp = pfm[i].bits_per_pixel;
1030 matthewc 121 }
1031     }
1032     XFree(pfm);
1033     }
1034    
1035 jsorg71 450 if (g_bpp < 8)
1036 matthewc 121 {
1037     error("Less than 8 bpp not currently supported.\n");
1038 jsorg71 450 XCloseDisplay(g_display);
1039 matthewc 121 return False;
1040     }
1041    
1042 matthewc 517 if (!g_owncolmap)
1043 n-ki 279 {
1044 astrand 580 g_xcolmap =
1045     XCreateColormap(g_display, RootWindowOfScreen(g_screen), g_visual,
1046     AllocNone);
1047 jsorg71 450 if (g_depth <= 8)
1048 matthewc 297 warning("Screen depth is 8 bits or lower: you may want to use -C for a private colourmap\n");
1049 n-ki 279 }
1050    
1051 stargo 609 if ((!g_ownbackstore) && (DoesBackingStore(g_screen) != Always))
1052     {
1053     warning("External BackingStore not available, using internal\n");
1054 jsorg71 450 g_ownbackstore = True;
1055 stargo 609 }
1056 matthewc 121
1057 astrand 500 /*
1058     * Determine desktop size
1059     */
1060 astrand 547 if (g_fullscreen)
1061 astrand 263 {
1062 astrand 547 g_width = WidthOfScreen(g_screen);
1063     g_height = HeightOfScreen(g_screen);
1064     }
1065     else if (g_width < 0)
1066     {
1067 astrand 500 /* Percent of screen */
1068     g_height = HeightOfScreen(g_screen) * (-g_width) / 100;
1069     g_width = WidthOfScreen(g_screen) * (-g_width) / 100;
1070     }
1071     else if (g_width == 0)
1072     {
1073 astrand 263 /* Fetch geometry from _NET_WORKAREA */
1074 matthewc 300 uint32 x, y, cx, cy;
1075 astrand 263
1076 matthewc 300 if (get_current_workarea(&x, &y, &cx, &cy) == 0)
1077 astrand 263 {
1078 jsorg71 447 g_width = cx;
1079     g_height = cy;
1080 matthewc 300 }
1081     else
1082     {
1083 matthewc 297 warning("Failed to get workarea: probably your window manager does not support extended hints\n");
1084 jsorg71 447 g_width = 800;
1085     g_height = 600;
1086 astrand 263 }
1087     }
1088 matthewc 121
1089 matthewc 160 /* make sure width is a multiple of 4 */
1090 jsorg71 447 g_width = (g_width + 3) & ~3;
1091 matthewc 160
1092 jsorg71 450 g_mod_map = XGetModifierMapping(g_display);
1093 matthewc 203
1094 astrand 498 xkeymap_init();
1095    
1096 jsorg71 447 if (g_enable_compose)
1097 jsorg71 450 g_IM = XOpenIM(g_display, NULL, NULL, NULL);
1098 matthewc 188
1099 matthewc 432 xclip_init();
1100 jsorg71 316
1101 matthewc 519 DEBUG_RDP5(("server bpp %d client bpp %d depth %d\n", g_server_bpp, g_bpp, g_depth));
1102 jsorg71 316
1103 jsorg71 81 return True;
1104     }
1105 astrand 66
1106 matthewc 188 void
1107 matthewc 192 ui_deinit(void)
1108 matthewc 188 {
1109 jsorg71 450 if (g_IM != NULL)
1110     XCloseIM(g_IM);
1111 astrand 580
1112 stargo 576 if (g_null_cursor != NULL)
1113     ui_destroy_cursor(g_null_cursor);
1114 matthewc 188
1115 jsorg71 450 XFreeModifiermap(g_mod_map);
1116 matthewc 203
1117 jsorg71 450 if (g_ownbackstore)
1118     XFreePixmap(g_display, g_backstore);
1119 matthewc 188
1120 jsorg71 450 XFreeGC(g_display, g_gc);
1121     XCloseDisplay(g_display);
1122     g_display = NULL;
1123 matthewc 188 }
1124    
1125 matthewc 121 BOOL
1126 matthewc 192 ui_create_window(void)
1127 matty 6 {
1128 matthewc 536 uint8 null_pointer_mask[1] = { 0x80 };
1129 astrand 788 uint8 null_pointer_data[24] = { 0x00 };
1130    
1131 matthewc 121 XSetWindowAttributes attribs;
1132 matty 28 XClassHint *classhints;
1133     XSizeHints *sizehints;
1134 matthewc 188 int wndwidth, wndheight;
1135 matthewc 432 long input_mask, ic_input_mask;
1136 jsorg71 100 XEvent xevent;
1137    
1138 jsorg71 450 wndwidth = g_fullscreen ? WidthOfScreen(g_screen) : g_width;
1139     wndheight = g_fullscreen ? HeightOfScreen(g_screen) : g_height;
1140 matthewc 188
1141 jsorg71 450 attribs.background_pixel = BlackPixelOfScreen(g_screen);
1142 stargo 565 attribs.border_pixel = WhitePixelOfScreen(g_screen);
1143 jsorg71 450 attribs.backing_store = g_ownbackstore ? NotUseful : Always;
1144 jsorg71 447 attribs.override_redirect = g_fullscreen;
1145 stargo 565 attribs.colormap = g_xcolmap;
1146 matthewc 188
1147 astrand 801 g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, wndwidth,
1148     wndheight, 0, g_depth, InputOutput, g_visual,
1149     CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
1150     CWBorderPixel, &attribs);
1151 jsorg71 100
1152 stargo 576 if (g_gc == NULL)
1153 stargo 566 g_gc = XCreateGC(g_display, g_wnd, 0, NULL);
1154 stargo 565
1155 jsorg71 725 if (g_create_bitmap_gc == NULL)
1156     g_create_bitmap_gc = XCreateGC(g_display, g_wnd, 0, NULL);
1157    
1158 stargo 603 if ((g_ownbackstore) && (g_backstore == 0))
1159 stargo 565 {
1160 astrand 580 g_backstore = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
1161 stargo 565
1162     /* clear to prevent rubbish being exposed at startup */
1163     XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
1164     XFillRectangle(g_display, g_backstore, g_gc, 0, 0, g_width, g_height);
1165     }
1166    
1167 jsorg71 450 XStoreName(g_display, g_wnd, g_title);
1168 jsorg71 100
1169 jsorg71 450 if (g_hide_decorations)
1170 astrand 262 mwm_hide_decorations();
1171    
1172 jsorg71 100 classhints = XAllocClassHint();
1173     if (classhints != NULL)
1174     {
1175     classhints->res_name = classhints->res_class = "rdesktop";
1176 jsorg71 450 XSetClassHint(g_display, g_wnd, classhints);
1177 jsorg71 100 XFree(classhints);
1178     }
1179    
1180     sizehints = XAllocSizeHints();
1181     if (sizehints)
1182     {
1183     sizehints->flags = PMinSize | PMaxSize;
1184 jsorg71 447 sizehints->min_width = sizehints->max_width = g_width;
1185     sizehints->min_height = sizehints->max_height = g_height;
1186 jsorg71 450 XSetWMNormalHints(g_display, g_wnd, sizehints);
1187 jsorg71 100 XFree(sizehints);
1188     }
1189    
1190 astrand 651 if (g_embed_wnd)
1191     {
1192     XReparentWindow(g_display, g_wnd, (Window) g_embed_wnd, 0, 0);
1193     }
1194 stargo 636
1195 matthewc 121 input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
1196 jsorg71 257 VisibilityChangeMask | FocusChangeMask;
1197 matthewc 121
1198 jsorg71 447 if (g_sendmotion)
1199 matthewc 121 input_mask |= PointerMotionMask;
1200 jsorg71 450 if (g_ownbackstore)
1201 matthewc 121 input_mask |= ExposureMask;
1202 jsorg71 450 if (g_fullscreen || g_grab_keyboard)
1203 matthewc 250 input_mask |= EnterWindowMask;
1204 jsorg71 450 if (g_grab_keyboard)
1205 jsorg71 257 input_mask |= LeaveWindowMask;
1206 jsorg71 100
1207 jsorg71 450 if (g_IM != NULL)
1208 matthewc 188 {
1209 jsorg71 450 g_IC = XCreateIC(g_IM, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
1210 astrand 456 XNClientWindow, g_wnd, XNFocusWindow, g_wnd, NULL);
1211 matthewc 188
1212 jsorg71 450 if ((g_IC != NULL)
1213     && (XGetICValues(g_IC, XNFilterEvents, &ic_input_mask, NULL) == NULL))
1214 matthewc 188 input_mask |= ic_input_mask;
1215     }
1216    
1217 jsorg71 450 XSelectInput(g_display, g_wnd, input_mask);
1218     XMapWindow(g_display, g_wnd);
1219 jsorg71 100
1220 matthewc 208 /* wait for VisibilityNotify */
1221 astrand 196 do
1222     {
1223 jsorg71 450 XMaskEvent(g_display, VisibilityChangeMask, &xevent);
1224 astrand 196 }
1225 matthewc 208 while (xevent.type != VisibilityNotify);
1226 jsorg71 688 g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
1227 matthewc 123
1228 jsorg71 447 g_focused = False;
1229     g_mouse_in_wnd = False;
1230 jsorg71 257
1231 astrand 275 /* handle the WM_DELETE_WINDOW protocol */
1232 jsorg71 450 g_protocol_atom = XInternAtom(g_display, "WM_PROTOCOLS", True);
1233     g_kill_atom = XInternAtom(g_display, "WM_DELETE_WINDOW", True);
1234     XSetWMProtocols(g_display, g_wnd, &g_kill_atom, 1);
1235 astrand 275
1236 astrand 508 /* create invisible 1x1 cursor to be used as null cursor */
1237 stargo 576 if (g_null_cursor == NULL)
1238     g_null_cursor = ui_create_cursor(0, 0, 1, 1, null_pointer_mask, null_pointer_data);
1239 astrand 508
1240 matty 10 return True;
1241 matty 6 }
1242    
1243 matty 25 void
1244 n-ki 677 ui_resize_window()
1245     {
1246     XSizeHints *sizehints;
1247 jsorg71 708 Pixmap bs;
1248 n-ki 677
1249     sizehints = XAllocSizeHints();
1250     if (sizehints)
1251     {
1252     sizehints->flags = PMinSize | PMaxSize;
1253     sizehints->min_width = sizehints->max_width = g_width;
1254     sizehints->min_height = sizehints->max_height = g_height;
1255     XSetWMNormalHints(g_display, g_wnd, sizehints);
1256     XFree(sizehints);
1257     }
1258    
1259     if (!(g_fullscreen || g_embed_wnd))
1260     {
1261     XResizeWindow(g_display, g_wnd, g_width, g_height);
1262     }
1263 jsorg71 708
1264     /* create new backstore pixmap */
1265     if (g_backstore != 0)
1266     {
1267     bs = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
1268     XSetForeground(g_display, g_gc, BlackPixelOfScreen(g_screen));
1269     XFillRectangle(g_display, bs, g_gc, 0, 0, g_width, g_height);
1270     XCopyArea(g_display, g_backstore, bs, g_gc, 0, 0, g_width, g_height, 0, 0);
1271     XFreePixmap(g_display, g_backstore);
1272     g_backstore = bs;
1273     }
1274 n-ki 677 }
1275    
1276     void
1277 matthewc 192 ui_destroy_window(void)
1278 matty 6 {
1279 jsorg71 450 if (g_IC != NULL)
1280     XDestroyIC(g_IC);
1281 matty 31
1282 jsorg71 450 XDestroyWindow(g_display, g_wnd);
1283 matty 6 }
1284    
1285 jsorg71 100 void
1286 matthewc 192 xwin_toggle_fullscreen(void)
1287 jsorg71 100 {
1288 matthewc 188 Pixmap contents = 0;
1289 matthewc 123
1290 jsorg71 450 if (!g_ownbackstore)
1291 matthewc 188 {
1292     /* need to save contents of window */
1293 jsorg71 450 contents = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth);
1294     XCopyArea(g_display, g_wnd, contents, g_gc, 0, 0, g_width, g_height, 0, 0);
1295 matthewc 188 }
1296    
1297     ui_destroy_window();
1298 jsorg71 447 g_fullscreen = !g_fullscreen;
1299 matthewc 188 ui_create_window();
1300 matthewc 123
1301 jsorg71 450 XDefineCursor(g_display, g_wnd, g_current_cursor);
1302 matthewc 188
1303 jsorg71 450 if (!g_ownbackstore)
1304 matthewc 188 {
1305 jsorg71 450 XCopyArea(g_display, contents, g_wnd, g_gc, 0, 0, g_width, g_height, 0, 0);
1306     XFreePixmap(g_display, contents);
1307 matthewc 188 }
1308 jsorg71 100 }
1309    
1310 jsorg71 447 /* Process all events in Xlib queue
1311 astrand 275 Returns 0 after user quit, 1 otherwise */
1312     static int
1313 matthewc 192 xwin_process_events(void)
1314 matty 9 {
1315 n-ki 54 XEvent xevent;
1316 matthewc 38 KeySym keysym;
1317 matthewc 50 uint16 button, flags;
1318 matty 10 uint32 ev_time;
1319 astrand 66 key_translation tr;
1320     char str[256];
1321     Status status;
1322 matty 9
1323 jsorg71 450 while (XPending(g_display) > 0)
1324 matty 9 {
1325 jsorg71 450 XNextEvent(g_display, &xevent);
1326 matthewc 123
1327 jsorg71 450 if ((g_IC != NULL) && (XFilterEvent(&xevent, None) == True))
1328 astrand 66 {
1329 astrand 84 DEBUG_KBD(("Filtering event\n"));
1330 astrand 66 continue;
1331     }
1332    
1333 matthewc 50 flags = 0;
1334 matty 10
1335 n-ki 54 switch (xevent.type)
1336 matty 9 {
1337 jsorg71 688 case VisibilityNotify:
1338     g_Unobscured = xevent.xvisibility.state == VisibilityUnobscured;
1339     break;
1340 astrand 275 case ClientMessage:
1341     /* the window manager told us to quit */
1342 jsorg71 450 if ((xevent.xclient.message_type == g_protocol_atom)
1343     && ((Atom) xevent.xclient.data.l[0] == g_kill_atom))
1344 astrand 275 /* Quit */
1345     return 0;
1346     break;
1347    
1348 matty 9 case KeyPress:
1349 jsorg71 450 g_last_gesturetime = xevent.xkey.time;
1350     if (g_IC != NULL)
1351 astrand 66 /* Multi_key compatible version */
1352     {
1353 jsorg71 450 XmbLookupString(g_IC,
1354 astrand 435 &xevent.xkey, str, sizeof(str), &keysym,
1355     &status);
1356 astrand 82 if (!((status == XLookupKeySym) || (status == XLookupBoth)))
1357 astrand 66 {
1358 astrand 82 error("XmbLookupString failed with status 0x%x\n",
1359     status);
1360 astrand 66 break;
1361     }
1362     }
1363     else
1364     {
1365     /* Plain old XLookupString */
1366 astrand 182 DEBUG_KBD(("\nNo input context, using XLookupString\n"));
1367 astrand 66 XLookupString((XKeyEvent *) & xevent,
1368 astrand 82 str, sizeof(str), &keysym, NULL);
1369 astrand 66 }
1370    
1371 astrand 261 DEBUG_KBD(("KeyPress for (keysym 0x%lx, %s)\n", keysym,
1372     get_ksname(keysym)));
1373 astrand 66
1374 matthewc 203 ev_time = time(NULL);
1375     if (handle_special_keys(keysym, xevent.xkey.state, ev_time, True))
1376 astrand 118 break;
1377    
1378 astrand 66 tr = xkeymap_translate_key(keysym,
1379 astrand 82 xevent.xkey.keycode, xevent.xkey.state);
1380 astrand 69
1381 astrand 66 if (tr.scancode == 0)
1382 n-ki 52 break;
1383    
1384 astrand 470 save_remote_modifiers(tr.scancode);
1385 astrand 115 ensure_remote_modifiers(ev_time, tr);
1386 astrand 449 rdp_send_scancode(ev_time, RDP_KEYPRESS, tr.scancode);
1387 astrand 470 restore_remote_modifiers(ev_time, tr.scancode);
1388 astrand 115
1389 astrand 66 break;
1390 matthewc 203
1391 astrand 66 case KeyRelease:
1392 jsorg71 450 g_last_gesturetime = xevent.xkey.time;
1393 astrand 66 XLookupString((XKeyEvent *) & xevent, str,
1394     sizeof(str), &keysym, NULL);
1395 n-ki 52
1396 astrand 84 DEBUG_KBD(("\nKeyRelease for (keysym 0x%lx, %s)\n", keysym,
1397 matthewc 203 get_ksname(keysym)));
1398 n-ki 52
1399 matthewc 203 ev_time = time(NULL);
1400     if (handle_special_keys(keysym, xevent.xkey.state, ev_time, False))
1401 astrand 118 break;
1402    
1403 astrand 66 tr = xkeymap_translate_key(keysym,
1404 astrand 82 xevent.xkey.keycode, xevent.xkey.state);
1405 astrand 66
1406     if (tr.scancode == 0)
1407     break;
1408    
1409 astrand 82 rdp_send_scancode(ev_time, RDP_KEYRELEASE, tr.scancode);
1410 matty 9 break;
1411    
1412     case ButtonPress:
1413 matthewc 50 flags = MOUSE_FLAG_DOWN;
1414     /* fall through */
1415 matty 9
1416     case ButtonRelease:
1417 jsorg71 450 g_last_gesturetime = xevent.xbutton.time;
1418 astrand 82 button = xkeymap_translate_button(xevent.xbutton.button);
1419 matty 9 if (button == 0)
1420     break;
1421    
1422 astrand 328 /* If win_button_size is nonzero, enable single app mode */
1423 jsorg71 450 if (xevent.xbutton.y < g_win_button_size)
1424 astrand 328 {
1425 astrand 342 /* Stop moving window when button is released, regardless of cursor position */
1426 jsorg71 450 if (g_moving_wnd && (xevent.type == ButtonRelease))
1427     g_moving_wnd = False;
1428 astrand 332
1429 astrand 342 /* Check from right to left: */
1430    
1431 jsorg71 450 if (xevent.xbutton.x >= g_width - g_win_button_size)
1432 astrand 328 {
1433 astrand 331 /* The close button, continue */
1434 astrand 328 ;
1435     }
1436 astrand 456 else if (xevent.xbutton.x >=
1437     g_width - g_win_button_size * 2)
1438 astrand 328 {
1439     /* The maximize/restore button. Do not send to
1440     server. It might be a good idea to change the
1441 jsorg71 447 cursor or give some other visible indication
1442 astrand 328 that rdesktop inhibited this click */
1443     break;
1444     }
1445 astrand 456 else if (xevent.xbutton.x >=
1446     g_width - g_win_button_size * 3)
1447 astrand 328 {
1448     /* The minimize button. Iconify window. */
1449 jsorg71 450 XIconifyWindow(g_display, g_wnd,
1450     DefaultScreen(g_display));
1451 astrand 328 break;
1452     }
1453 jsorg71 450 else if (xevent.xbutton.x <= g_win_button_size)
1454 astrand 342 {
1455     /* The system menu. Ignore. */
1456     break;
1457     }
1458 astrand 332 else
1459     {
1460 astrand 342 /* The title bar. */
1461 jsorg71 447 if ((xevent.type == ButtonPress) && !g_fullscreen
1462 jsorg71 450 && g_hide_decorations)
1463 astrand 342 {
1464 jsorg71 450 g_moving_wnd = True;
1465     g_move_x_offset = xevent.xbutton.x;
1466     g_move_y_offset = xevent.xbutton.y;
1467 astrand 342 }
1468 astrand 332 break;
1469 astrand 342
1470 astrand 332 }
1471 astrand 328 }
1472    
1473 matthewc 203 rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
1474 astrand 82 flags | button, xevent.xbutton.x, xevent.xbutton.y);
1475 matty 10 break;
1476    
1477     case MotionNotify:
1478 jsorg71 450 if (g_moving_wnd)
1479 astrand 342 {
1480 jsorg71 450 XMoveWindow(g_display, g_wnd,
1481     xevent.xmotion.x_root - g_move_x_offset,
1482     xevent.xmotion.y_root - g_move_y_offset);
1483 astrand 342 break;
1484     }
1485    
1486 jsorg71 447 if (g_fullscreen && !g_focused)
1487 jsorg71 450 XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
1488 jsorg71 288 CurrentTime);
1489 matthewc 203 rdp_send_input(time(NULL), RDP_INPUT_MOUSE,
1490 astrand 82 MOUSE_FLAG_MOVE, xevent.xmotion.x, xevent.xmotion.y);
1491 matty 28 break;
1492    
1493 matthewc 194 case FocusIn:
1494 jsorg71 257 if (xevent.xfocus.mode == NotifyGrab)
1495     break;
1496 jsorg71 447 g_focused = True;
1497 astrand 543 reset_modifier_keys();
1498 jsorg71 450 if (g_grab_keyboard && g_mouse_in_wnd)
1499     XGrabKeyboard(g_display, g_wnd, True,
1500 astrand 82 GrabModeAsync, GrabModeAsync, CurrentTime);
1501 matty 28 break;
1502    
1503 matthewc 194 case FocusOut:
1504 jsorg71 257 if (xevent.xfocus.mode == NotifyUngrab)
1505     break;
1506 jsorg71 447 g_focused = False;
1507 matthewc 201 if (xevent.xfocus.mode == NotifyWhileGrabbed)
1508 jsorg71 450 XUngrabKeyboard(g_display, CurrentTime);
1509 matty 28 break;
1510 matty 31
1511 matthewc 250 case EnterNotify:
1512     /* we only register for this event when in fullscreen mode */
1513 jsorg71 257 /* or grab_keyboard */
1514 jsorg71 447 g_mouse_in_wnd = True;
1515     if (g_fullscreen)
1516 jsorg71 257 {
1517 jsorg71 450 XSetInputFocus(g_display, g_wnd, RevertToPointerRoot,
1518 astrand 261 CurrentTime);
1519 jsorg71 257 break;
1520     }
1521 jsorg71 447 if (g_focused)
1522 jsorg71 450 XGrabKeyboard(g_display, g_wnd, True,
1523 jsorg71 257 GrabModeAsync, GrabModeAsync, CurrentTime);
1524 matthewc 250 break;
1525    
1526 matthewc 253 case LeaveNotify:
1527 jsorg71 257 /* we only register for this event when grab_keyboard */
1528 jsorg71 447 g_mouse_in_wnd = False;
1529 jsorg71 450 XUngrabKeyboard(g_display, CurrentTime);
1530 matthewc 253 break;
1531    
1532 matty 31 case Expose:
1533 jsorg71 450 XCopyArea(g_display, g_backstore, g_wnd, g_gc,
1534 n-ki 54 xevent.xexpose.x, xevent.xexpose.y,
1535 astrand 64 xevent.xexpose.width,
1536     xevent.xexpose.height,
1537 n-ki 54 xevent.xexpose.x, xevent.xexpose.y);
1538 matty 31 break;
1539 astrand 119
1540     case MappingNotify:
1541     /* Refresh keyboard mapping if it has changed. This is important for
1542     Xvnc, since it allocates keycodes dynamically */
1543     if (xevent.xmapping.request == MappingKeyboard
1544     || xevent.xmapping.request == MappingModifier)
1545     XRefreshKeyboardMapping(&xevent.xmapping);
1546 matthewc 203
1547     if (xevent.xmapping.request == MappingModifier)
1548     {
1549 jsorg71 450 XFreeModifiermap(g_mod_map);
1550     g_mod_map = XGetModifierMapping(g_display);
1551 matthewc 203 }
1552 astrand 119 break;
1553 matthewc 432
1554 astrand 435 /* clipboard stuff */
1555 forsberg 415 case SelectionNotify:
1556 matthewc 432 xclip_handle_SelectionNotify(&xevent.xselection);
1557 forsberg 415 break;
1558     case SelectionRequest:
1559 matthewc 432 xclip_handle_SelectionRequest(&xevent.xselectionrequest);
1560 forsberg 415 break;
1561 matthewc 432 case SelectionClear:
1562     xclip_handle_SelectionClear();
1563     break;
1564 forsberg 415 case PropertyNotify:
1565 matthewc 432 xclip_handle_PropertyNotify(&xevent.xproperty);
1566 forsberg 415 break;
1567 matty 9 }
1568     }
1569 astrand 275 /* Keep going */
1570     return 1;
1571 matty 9 }
1572    
1573 astrand 275 /* Returns 0 after user quit, 1 otherwise */
1574     int
1575 matty 33 ui_select(int rdp_socket)
1576     {
1577 stargo 606 int n;
1578 matthewc 474 fd_set rfds, wfds;
1579 n-ki 592 struct timeval tv;
1580     BOOL s_timeout = False;
1581 matty 33
1582     while (True)
1583     {
1584 stargo 606 n = (rdp_socket > g_x_socket) ? rdp_socket : g_x_socket;
1585 matthewc 121 /* Process any events already waiting */
1586 astrand 275 if (!xwin_process_events())
1587     /* User quit */
1588     return 0;
1589 astrand 119
1590 matty 33 FD_ZERO(&rfds);
1591 matthewc 474 FD_ZERO(&wfds);
1592 matty 33 FD_SET(rdp_socket, &rfds);
1593 jsorg71 450 FD_SET(g_x_socket, &rfds);
1594 matty 33
1595 matthewc 474 #ifdef WITH_RDPSND
1596     /* FIXME: there should be an API for registering fds */
1597 stargo 504 if (g_dsp_busy)
1598 matty 33 {
1599 matthewc 474 FD_SET(g_dsp_fd, &wfds);
1600 n-ki 592 n = (g_dsp_fd > n) ? g_dsp_fd : n;
1601 astrand 499 }
1602 matthewc 474 #endif
1603 n-ki 592 /* default timeout */
1604     tv.tv_sec = 60;
1605     tv.tv_usec = 0;
1606 matthewc 474
1607 n-ki 592 /* add redirection handles */
1608     rdpdr_add_fds(&n, &rfds, &wfds, &tv, &s_timeout);
1609    
1610     n++;
1611    
1612     switch (select(n, &rfds, &wfds, NULL, &tv))
1613 matthewc 474 {
1614 matty 33 case -1:
1615     error("select: %s\n", strerror(errno));
1616    
1617     case 0:
1618 stargo 795 /* Abort serial read calls */
1619     if (s_timeout)
1620     rdpdr_check_fds(&rfds, &wfds, (BOOL) True);
1621 matty 33 continue;
1622     }
1623    
1624 n-ki 592 rdpdr_check_fds(&rfds, &wfds, (BOOL) False);
1625    
1626 stargo 754 if (FD_ISSET(rdp_socket, &rfds))
1627     return 1;
1628    
1629 matthewc 474 #ifdef WITH_RDPSND
1630 stargo 504 if (g_dsp_busy && FD_ISSET(g_dsp_fd, &wfds))
1631 matthewc 474 wave_out_play();
1632     #endif
1633 matty 33 }
1634     }
1635    
1636     void
1637 matty 25 ui_move_pointer(int x, int y)
1638 matty 9 {
1639 jsorg71 450 XWarpPointer(g_display, g_wnd, g_wnd, 0, 0, 0, 0, x, y);
1640 matty 9 }
1641    
1642 matty 25 HBITMAP
1643 astrand 64 ui_create_bitmap(int width, int height, uint8 * data)
1644 matty 6 {
1645     XImage *image;
1646 matty 9 Pixmap bitmap;
1647 matty 28 uint8 *tdata;
1648 stargo 521 int bitmap_pad;
1649 matty 29
1650 stargo 521 if (g_server_bpp == 8)
1651     {
1652     bitmap_pad = 8;
1653     }
1654     else
1655     {
1656     bitmap_pad = g_bpp;
1657    
1658     if (g_bpp == 24)
1659     bitmap_pad = 32;
1660     }
1661    
1662 jsorg71 450 tdata = (g_owncolmap ? data : translate_image(width, height, data));
1663     bitmap = XCreatePixmap(g_display, g_wnd, width, height, g_depth);
1664     image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
1665 stargo 521 (char *) tdata, width, height, bitmap_pad, 0);
1666 matty 6
1667 jsorg71 725 XPutImage(g_display, bitmap, g_create_bitmap_gc, image, 0, 0, 0, 0, width, height);
1668 matty 9
1669     XFree(image);
1670 jsorg71 644 if (tdata != data)
1671 n-ki 279 xfree(tdata);
1672 matty 24 return (HBITMAP) bitmap;
1673 matty 6 }
1674    
1675 matty 25 void
1676 astrand 82 ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height, uint8 * data)
1677 matty 6 {
1678 matty 10 XImage *image;
1679 matty 29 uint8 *tdata;
1680 stargo 521 int bitmap_pad;
1681    
1682     if (g_server_bpp == 8)
1683     {
1684     bitmap_pad = 8;
1685     }
1686     else
1687     {
1688     bitmap_pad = g_bpp;
1689    
1690     if (g_bpp == 24)
1691     bitmap_pad = 32;
1692     }
1693    
1694 jsorg71 450 tdata = (g_owncolmap ? data : translate_image(width, height, data));
1695     image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
1696 stargo 521 (char *) tdata, width, height, bitmap_pad, 0);
1697 matty 28
1698 jsorg71 450 if (g_ownbackstore)
1699 matty 31 {
1700 jsorg71 450 XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
1701     XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
1702 matty 31 }
1703     else
1704     {
1705 jsorg71 450 XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
1706 matty 31 }
1707 matty 29
1708 matty 24 XFree(image);
1709 jsorg71 644 if (tdata != data)
1710 n-ki 279 xfree(tdata);
1711 matty 6 }
1712    
1713 matty 25 void
1714     ui_destroy_bitmap(HBITMAP bmp)
1715 matty 6 {
1716 jsorg71 450 XFreePixmap(g_display, (Pixmap) bmp);
1717 matty 10 }
1718    
1719 matty 25 HGLYPH
1720 astrand 64 ui_create_glyph(int width, int height, uint8 * data)
1721 matty 10 {
1722 matty 9 XImage *image;
1723     Pixmap bitmap;
1724     int scanline;
1725 matty 6
1726 matty 9 scanline = (width + 7) / 8;
1727 matty 6
1728 jsorg71 450 bitmap = XCreatePixmap(g_display, g_wnd, width, height, 1);
1729 jsorg71 725 if (g_create_glyph_gc == 0)
1730     g_create_glyph_gc = XCreateGC(g_display, bitmap, 0, NULL);
1731 matty 9
1732 jsorg71 450 image = XCreateImage(g_display, g_visual, 1, ZPixmap, 0, (char *) data,
1733 astrand 73 width, height, 8, scanline);
1734 matty 23 image->byte_order = MSBFirst;
1735     image->bitmap_bit_order = MSBFirst;
1736     XInitImage(image);
1737    
1738 jsorg71 725 XPutImage(g_display, bitmap, g_create_glyph_gc, image, 0, 0, 0, 0, width, height);
1739 matty 29
1740 matty 9 XFree(image);
1741 astrand 64 return (HGLYPH) bitmap;
1742 matty 6 }
1743 matty 7
1744 matty 25 void
1745     ui_destroy_glyph(HGLYPH glyph)
1746 matty 7 {
1747 jsorg71 450 XFreePixmap(g_display, (Pixmap) glyph);
1748 matty 9 }
1749    
1750 matty 29 HCURSOR
1751 astrand 66 ui_create_cursor(unsigned int x, unsigned int y, int width, int height,
1752     uint8 * andmask, uint8 * xormask)
1753 matty 9 {
1754 matty 29 HGLYPH maskglyph, cursorglyph;
1755     XColor bg, fg;
1756     Cursor xcursor;
1757     uint8 *cursor, *pcursor;
1758     uint8 *mask, *pmask;
1759     uint8 nextbit;
1760     int scanline, offset;
1761     int i, j;
1762    
1763     scanline = (width + 7) / 8;
1764     offset = scanline * height;
1765    
1766 forsberg 415 cursor = (uint8 *) xmalloc(offset);
1767 matty 29 memset(cursor, 0, offset);
1768    
1769 forsberg 415 mask = (uint8 *) xmalloc(offset);
1770 matty 29 memset(mask, 0, offset);
1771    
1772     /* approximate AND and XOR masks with a monochrome X pointer */
1773     for (i = 0; i < height; i++)
1774 matty 7 {
1775 matty 29 offset -= scanline;
1776     pcursor = &cursor[offset];
1777     pmask = &mask[offset];
1778    
1779     for (j = 0; j < scanline; j++)
1780 matty 28 {
1781 matty 29 for (nextbit = 0x80; nextbit != 0; nextbit >>= 1)
1782     {
1783     if (xormask[0] || xormask[1] || xormask[2])
1784     {
1785     *pcursor |= (~(*andmask) & nextbit);
1786     *pmask |= nextbit;
1787     }
1788     else
1789     {
1790     *pcursor |= ((*andmask) & nextbit);
1791     *pmask |= (~(*andmask) & nextbit);
1792     }
1793    
1794     xormask += 3;
1795     }
1796    
1797     andmask++;
1798     pcursor++;
1799     pmask++;
1800 matty 28 }
1801 matty 7 }
1802 matty 29
1803     fg.red = fg.blue = fg.green = 0xffff;
1804     bg.red = bg.blue = bg.green = 0x0000;
1805     fg.flags = bg.flags = DoRed | DoBlue | DoGreen;
1806    
1807     cursorglyph = ui_create_glyph(width, height, cursor);
1808     maskglyph = ui_create_glyph(width, height, mask);
1809    
1810 astrand 66 xcursor =
1811 jsorg71 450 XCreatePixmapCursor(g_display, (Pixmap) cursorglyph,
1812 astrand 66 (Pixmap) maskglyph, &fg, &bg, x, y);
1813 astrand 64
1814 matty 29 ui_destroy_glyph(maskglyph);
1815     ui_destroy_glyph(cursorglyph);
1816     xfree(mask);
1817     xfree(cursor);
1818 astrand 64 return (HCURSOR) xcursor;
1819 matty 29 }
1820    
1821     void
1822     ui_set_cursor(HCURSOR cursor)
1823     {
1824 jsorg71 450 g_current_cursor = (Cursor) cursor;
1825     XDefineCursor(g_display, g_wnd, g_current_cursor);
1826 matty 29 }
1827    
1828     void
1829     ui_destroy_cursor(HCURSOR cursor)
1830     {
1831 jsorg71 450 XFreeCursor(g_display, (Cursor) cursor);
1832 matty 29 }
1833    
1834 astrand 508 void
1835     ui_set_null_cursor(void)
1836     {
1837     ui_set_cursor(g_null_cursor);
1838     }
1839    
1840 matty 29 #define MAKE_XCOLOR(xc,c) \
1841     (xc)->red = ((c)->red << 8) | (c)->red; \
1842     (xc)->green = ((c)->green << 8) | (c)->green; \
1843     (xc)->blue = ((c)->blue << 8) | (c)->blue; \
1844     (xc)->flags = DoRed | DoGreen | DoBlue;
1845    
1846 n-ki 279
1847 matty 29 HCOLOURMAP
1848 astrand 64 ui_create_colourmap(COLOURMAP * colours)
1849 matty 29 {
1850     COLOURENTRY *entry;
1851     int i, ncolours = colours->ncolours;
1852 jsorg71 450 if (!g_owncolmap)
1853 matty 28 {
1854 jsorg71 450 uint32 *map = (uint32 *) xmalloc(sizeof(*g_colmap) * ncolours);
1855 n-ki 279 XColor xentry;
1856     XColor xc_cache[256];
1857     uint32 colour;
1858     int colLookup = 256;
1859     for (i = 0; i < ncolours; i++)
1860 matty 28 {
1861 n-ki 279 entry = &colours->colours[i];
1862     MAKE_XCOLOR(&xentry, entry);
1863 matty 7
1864 jsorg71 450 if (XAllocColor(g_display, g_xcolmap, &xentry) == 0)
1865 astrand 196 {
1866 n-ki 279 /* Allocation failed, find closest match. */
1867     int j = 256;
1868     int nMinDist = 3 * 256 * 256;
1869     long nDist = nMinDist;
1870 matty 28
1871 n-ki 279 /* only get the colors once */
1872     while (colLookup--)
1873 astrand 196 {
1874 n-ki 279 xc_cache[colLookup].pixel = colLookup;
1875     xc_cache[colLookup].red = xc_cache[colLookup].green =
1876     xc_cache[colLookup].blue = 0;
1877     xc_cache[colLookup].flags = 0;
1878 jsorg71 450 XQueryColor(g_display,
1879     DefaultColormap(g_display,
1880     DefaultScreen(g_display)),
1881 n-ki 279 &xc_cache[colLookup]);
1882 n-ki 185 }
1883 n-ki 279 colLookup = 0;
1884    
1885     /* approximate the pixel */
1886     while (j--)
1887 astrand 196 {
1888 n-ki 279 if (xc_cache[j].flags)
1889     {
1890     nDist = ((long) (xc_cache[j].red >> 8) -
1891     (long) (xentry.red >> 8)) *
1892     ((long) (xc_cache[j].red >> 8) -
1893     (long) (xentry.red >> 8)) +
1894     ((long) (xc_cache[j].green >> 8) -
1895     (long) (xentry.green >> 8)) *
1896     ((long) (xc_cache[j].green >> 8) -
1897     (long) (xentry.green >> 8)) +
1898     ((long) (xc_cache[j].blue >> 8) -
1899     (long) (xentry.blue >> 8)) *
1900     ((long) (xc_cache[j].blue >> 8) -
1901     (long) (xentry.blue >> 8));
1902     }
1903     if (nDist < nMinDist)
1904     {
1905     nMinDist = nDist;
1906     xentry.pixel = j;
1907     }
1908 n-ki 185 }
1909     }
1910 n-ki 279 colour = xentry.pixel;
1911    
1912     /* update our cache */
1913     if (xentry.pixel < 256)
1914     {
1915     xc_cache[xentry.pixel].red = xentry.red;
1916     xc_cache[xentry.pixel].green = xentry.green;
1917     xc_cache[xentry.pixel].blue = xentry.blue;
1918    
1919     }
1920    
1921 matthewc 527 map[i] = colour;
1922 n-ki 185 }
1923 n-ki 279 return map;
1924     }
1925     else
1926     {
1927     XColor *xcolours, *xentry;
1928     Colormap map;
1929 matty 29
1930 forsberg 415 xcolours = (XColor *) xmalloc(sizeof(XColor) * ncolours);
1931 n-ki 279 for (i = 0; i < ncolours; i++)
1932 astrand 196 {
1933 n-ki 279 entry = &colours->colours[i];
1934     xentry = &xcolours[i];
1935     xentry->pixel = i;
1936     MAKE_XCOLOR(xentry, entry);
1937 matty 29 }
1938    
1939 jsorg71 450 map = XCreateColormap(g_display, g_wnd, g_visual, AllocAll);
1940     XStoreColors(g_display, map, xcolours, ncolours);
1941 n-ki 185
1942 n-ki 279 xfree(xcolours);
1943     return (HCOLOURMAP) map;
1944 matty 29 }
1945 matty 7 }
1946    
1947 matty 25 void
1948     ui_destroy_colourmap(HCOLOURMAP map)
1949 matty 7 {
1950 jsorg71 450 if (!g_owncolmap)
1951 n-ki 279 xfree(map);
1952     else
1953 jsorg71 450 XFreeColormap(g_display, (Colormap) map);
1954 matty 7 }
1955    
1956 matty 25 void
1957     ui_set_colourmap(HCOLOURMAP map)
1958 matty 7 {
1959 jsorg71 450 if (!g_owncolmap)
1960 astrand 448 {
1961 jsorg71 450 if (g_colmap)
1962     xfree(g_colmap);
1963 astrand 448
1964 jsorg71 450 g_colmap = (uint32 *) map;
1965 astrand 448 }
1966 n-ki 279 else
1967 jsorg71 450 XSetWindowColormap(g_display, g_wnd, (Colormap) map);
1968 matty 7 }
1969    
1970 matty 25 void
1971     ui_set_clip(int x, int y, int cx, int cy)
1972 matty 7 {
1973 matty 9 XRectangle rect;
1974 matty 7
1975 matty 9 rect.x = x;
1976     rect.y = y;
1977     rect.width = cx;
1978     rect.height = cy;
1979 jsorg71 450 XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded);
1980 matty 9 }
1981 matty 7
1982 matty 25 void
1983 matthewc 192 ui_reset_clip(void)
1984 matty 9 {
1985     XRectangle rect;
1986    
1987     rect.x = 0;
1988     rect.y = 0;
1989 jsorg71 447 rect.width = g_width;
1990     rect.height = g_height;
1991 jsorg71 450 XSetClipRectangles(g_display, g_gc, 0, 0, &rect, 1, YXBanded);
1992 matty 7 }
1993    
1994 matty 25 void
1995 matthewc 192 ui_bell(void)
1996 matty 10 {
1997 jsorg71 450 XBell(g_display, 0);
1998 matty 10 }
1999    
2000 matty 25 void
2001     ui_destblt(uint8 opcode,
2002     /* dest */ int x, int y, int cx, int cy)
2003 matty 9 {
2004 matty 29 SET_FUNCTION(opcode);
2005 matty 31 FILL_RECTANGLE(x, y, cx, cy);
2006 matty 29 RESET_FUNCTION(opcode);
2007 matty 9 }
2008    
2009 jsorg71 373 static uint8 hatch_patterns[] = {
2010 forsberg 415 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, /* 0 - bsHorizontal */
2011     0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, /* 1 - bsVertical */
2012     0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, /* 2 - bsFDiagonal */
2013     0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /* 3 - bsBDiagonal */
2014     0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08, /* 4 - bsCross */
2015     0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 /* 5 - bsDiagCross */
2016 jsorg71 373 };
2017    
2018 matty 25 void
2019     ui_patblt(uint8 opcode,
2020     /* dest */ int x, int y, int cx, int cy,
2021 astrand 64 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2022 matty 9 {
2023     Pixmap fill;
2024 jsorg71 59 uint8 i, ipattern[8];
2025 matty 9
2026 matty 29 SET_FUNCTION(opcode);
2027 matty 9
2028     switch (brush->style)
2029     {
2030 matty 24 case 0: /* Solid */
2031 matty 29 SET_FOREGROUND(fgcolour);
2032 jsorg71 680 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2033 matty 9 break;
2034    
2035 jsorg71 373 case 2: /* Hatch */
2036 forsberg 415 fill = (Pixmap) ui_create_glyph(8, 8,
2037     hatch_patterns + brush->pattern[0] * 8);
2038 astrand 487 SET_FOREGROUND(fgcolour);
2039     SET_BACKGROUND(bgcolour);
2040 jsorg71 450 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2041     XSetStipple(g_display, g_gc, fill);
2042     XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2043 jsorg71 680 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2044 jsorg71 450 XSetFillStyle(g_display, g_gc, FillSolid);
2045     XSetTSOrigin(g_display, g_gc, 0, 0);
2046 jsorg71 373 ui_destroy_glyph((HGLYPH) fill);
2047     break;
2048    
2049 matty 24 case 3: /* Pattern */
2050 jsorg71 59 for (i = 0; i != 8; i++)
2051     ipattern[7 - i] = brush->pattern[i];
2052     fill = (Pixmap) ui_create_glyph(8, 8, ipattern);
2053 matty 29 SET_FOREGROUND(bgcolour);
2054     SET_BACKGROUND(fgcolour);
2055 jsorg71 450 XSetFillStyle(g_display, g_gc, FillOpaqueStippled);
2056     XSetStipple(g_display, g_gc, fill);
2057     XSetTSOrigin(g_display, g_gc, brush->xorigin, brush->yorigin);
2058 jsorg71 680 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2059 jsorg71 450 XSetFillStyle(g_display, g_gc, FillSolid);
2060     XSetTSOrigin(g_display, g_gc, 0, 0);
2061 astrand 64 ui_destroy_glyph((HGLYPH) fill);
2062 matty 9 break;
2063    
2064     default:
2065 matty 30 unimpl("brush %d\n", brush->style);
2066 matty 9 }
2067 matty 29
2068     RESET_FUNCTION(opcode);
2069 jsorg71 680
2070     if (g_ownbackstore)
2071     XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2072 matty 9 }
2073    
2074 matty 25 void
2075     ui_screenblt(uint8 opcode,
2076     /* dest */ int x, int y, int cx, int cy,
2077     /* src */ int srcx, int srcy)
2078 matty 9 {
2079 matty 29 SET_FUNCTION(opcode);
2080 jsorg71 450 if (g_ownbackstore)
2081 stargo 609 {
2082 jsorg71 688 if (g_Unobscured)
2083     {
2084     XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2085 astrand 691 XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x,
2086     y);
2087 jsorg71 688 }
2088     else
2089     {
2090     XCopyArea(g_display, g_backstore, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2091 astrand 691 XCopyArea(g_display, g_backstore, g_backstore, g_gc, srcx, srcy, cx, cy, x,
2092     y);
2093 jsorg71 688 }
2094 stargo 609 }
2095     else
2096     {
2097     XCopyArea(g_display, g_wnd, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2098     }
2099 matty 29 RESET_FUNCTION(opcode);
2100 matty 9 }
2101    
2102 matty 25 void
2103     ui_memblt(uint8 opcode,
2104     /* dest */ int x, int y, int cx, int cy,
2105     /* src */ HBITMAP src, int srcx, int srcy)
2106 matty 9 {
2107 matty 29 SET_FUNCTION(opcode);
2108 jsorg71 450 XCopyArea(g_display, (Pixmap) src, g_wnd, g_gc, srcx, srcy, cx, cy, x, y);
2109     if (g_ownbackstore)
2110     XCopyArea(g_display, (Pixmap) src, g_backstore, g_gc, srcx, srcy, cx, cy, x, y);
2111 matty 29 RESET_FUNCTION(opcode);
2112 matty 9 }
2113    
2114 matty 25 void
2115     ui_triblt(uint8 opcode,
2116     /* dest */ int x, int y, int cx, int cy,
2117     /* src */ HBITMAP src, int srcx, int srcy,
2118 astrand 64 /* brush */ BRUSH * brush, int bgcolour, int fgcolour)
2119 matty 9 {
2120     /* This is potentially difficult to do in general. Until someone
2121 matty 10 comes up with a more efficient way of doing it I am using cases. */
2122 matty 9
2123     switch (opcode)
2124     {
2125 matty 24 case 0x69: /* PDSxxn */
2126 matty 16 ui_memblt(ROP2_XOR, x, y, cx, cy, src, srcx, srcy);
2127 astrand 82 ui_patblt(ROP2_NXOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2128 matty 16 break;
2129    
2130 matty 24 case 0xb8: /* PSDPxax */
2131 astrand 82 ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2132 matty 16 ui_memblt(ROP2_AND, x, y, cx, cy, src, srcx, srcy);
2133 astrand 82 ui_patblt(ROP2_XOR, x, y, cx, cy, brush, bgcolour, fgcolour);
2134 matty 9 break;
2135    
2136 matty 29 case 0xc0: /* PSa */
2137 matty 28 ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2138 astrand 82 ui_patblt(ROP2_AND, x, y, cx, cy, brush, bgcolour, fgcolour);
2139 matty 28 break;
2140    
2141 matty 9 default:
2142 matty 30 unimpl("triblt 0x%x\n", opcode);
2143 matty 16 ui_memblt(ROP2_COPY, x, y, cx, cy, src, srcx, srcy);
2144 matty 9 }
2145     }
2146    
2147 matty 25 void
2148     ui_line(uint8 opcode,
2149     /* dest */ int startx, int starty, int endx, int endy,
2150 astrand 64 /* pen */ PEN * pen)
2151 matty 9 {
2152 matty 29 SET_FUNCTION(opcode);
2153     SET_FOREGROUND(pen->colour);
2154 jsorg71 450 XDrawLine(g_display, g_wnd, g_gc, startx, starty, endx, endy);
2155     if (g_ownbackstore)
2156     XDrawLine(g_display, g_backstore, g_gc, startx, starty, endx, endy);
2157 matty 29 RESET_FUNCTION(opcode);
2158 matty 9 }
2159    
2160 matty 25 void
2161     ui_rect(
2162     /* dest */ int x, int y, int cx, int cy,
2163     /* brush */ int colour)
2164 matty 9 {
2165 matty 29 SET_FOREGROUND(colour);
2166 matty 31 FILL_RECTANGLE(x, y, cx, cy);
2167 matty 9 }
2168    
2169 jsorg71 278 /* warning, this function only draws on wnd or backstore, not both */
2170 matty 25 void
2171     ui_draw_glyph(int mixmode,
2172     /* dest */ int x, int y, int cx, int cy,
2173 astrand 66 /* src */ HGLYPH glyph, int srcx, int srcy,
2174     int bgcolour, int fgcolour)
2175 matty 9 {
2176 matty 29 SET_FOREGROUND(fgcolour);
2177     SET_BACKGROUND(bgcolour);
2178 matty 9
2179 jsorg71 450 XSetFillStyle(g_display, g_gc,
2180 astrand 82 (mixmode == MIX_TRANSPARENT) ? FillStippled : FillOpaqueStippled);
2181 jsorg71 450 XSetStipple(g_display, g_gc, (Pixmap) glyph);
2182     XSetTSOrigin(g_display, g_gc, x, y);
2183 matty 9
2184 matthewc 296 FILL_RECTANGLE_BACKSTORE(x, y, cx, cy);
2185 matty 9
2186 jsorg71 450 XSetFillStyle(g_display, g_gc, FillSolid);
2187 matty 9 }
2188    
2189 mmihalik 49 #define DO_GLYPH(ttext,idx) \
2190     {\
2191     glyph = cache_get_font (font, ttext[idx]);\
2192     if (!(flags & TEXT2_IMPLICIT_X))\
2193 jsorg71 564 {\
2194     xyoffset = ttext[++idx];\
2195     if ((xyoffset & 0x80))\
2196 mmihalik 49 {\
2197 jsorg71 564 if (flags & TEXT2_VERTICAL)\
2198     y += ttext[idx+1] | (ttext[idx+2] << 8);\
2199 mmihalik 49 else\
2200 jsorg71 564 x += ttext[idx+1] | (ttext[idx+2] << 8);\
2201     idx += 2;\
2202 mmihalik 49 }\
2203 jsorg71 564 else\
2204 mmihalik 49 {\
2205 jsorg71 564 if (flags & TEXT2_VERTICAL)\
2206     y += xyoffset;\
2207     else\
2208     x += xyoffset;\
2209 mmihalik 49 }\
2210 jsorg71 564 }\
2211     if (glyph != NULL)\
2212     {\
2213     x1 = x + glyph->offset;\
2214     y1 = y + glyph->baseline;\
2215     XSetStipple(g_display, g_gc, (Pixmap) glyph->pixmap);\
2216     XSetTSOrigin(g_display, g_gc, x1, y1);\
2217     FILL_RECTANGLE_BACKSTORE(x1, y1, glyph->width, glyph->height);\
2218     if (flags & TEXT2_IMPLICIT_X)\
2219     x += glyph->width;\
2220     }\
2221 mmihalik 49 }
2222    
2223 matty 25 void
2224     ui_draw_text(uint8 font, uint8 flags, int mixmode, int x, int y,
2225 astrand 66 int clipx, int clipy, int clipcx, int clipcy,
2226     int boxx, int boxy, int boxcx, int boxcy, int bgcolour,
2227 mmihalik 49 int fgcolour, uint8 * text, uint8 length)
2228 matty 9 {
2229 matty 10 FONTGLYPH *glyph;
2230 jsorg71 564 int i, j, xyoffset, x1, y1;
2231 mmihalik 49 DATABLOB *entry;
2232 matty 9
2233 matty 29 SET_FOREGROUND(bgcolour);
2234 matty 28
2235 astrand 620 /* Sometimes, the boxcx value is something really large, like
2236     32691. This makes XCopyArea fail with Xvnc. The code below
2237     is a quick fix. */
2238     if (boxx + boxcx > g_width)
2239     boxcx = g_width - boxx;
2240    
2241 matty 9 if (boxcx > 1)
2242 matty 31 {
2243 matthewc 296 FILL_RECTANGLE_BACKSTORE(boxx, boxy, boxcx, boxcy);
2244 matty 31 }
2245 matty 17 else if (mixmode == MIX_OPAQUE)
2246 matty 31 {
2247 matthewc 296 FILL_RECTANGLE_BACKSTORE(clipx, clipy, clipcx, clipcy);
2248 matty 31 }
2249 matty 9
2250 jsorg71 564 SET_FOREGROUND(fgcolour);
2251     SET_BACKGROUND(bgcolour);
2252     XSetFillStyle(g_display, g_gc, FillStippled);
2253    
2254 matty 9 /* Paint text, character by character */
2255 astrand 64 for (i = 0; i < length;)
2256     {
2257     switch (text[i])
2258     {
2259     case 0xff:
2260     if (i + 2 < length)
2261 astrand 82 cache_put_text(text[i + 1], text, text[i + 2]);
2262 astrand 64 else
2263     {
2264     error("this shouldn't be happening\n");
2265 astrand 265 exit(1);
2266 astrand 64 }
2267     /* this will move pointer from start to first character after FF command */
2268     length -= i + 3;
2269     text = &(text[i + 3]);
2270     i = 0;
2271 mmihalik 49 break;
2272 matty 9
2273 astrand 64 case 0xfe:
2274     entry = cache_get_text(text[i + 1]);
2275     if (entry != NULL)
2276     {
2277     if ((((uint8 *) (entry->data))[1] ==
2278 astrand 82 0) && (!(flags & TEXT2_IMPLICIT_X)))
2279 astrand 64 {
2280     if (flags & TEXT2_VERTICAL)
2281     y += text[i + 2];
2282     else
2283     x += text[i + 2];
2284     }
2285     for (j = 0; j < entry->size; j++)
2286 astrand 82 DO_GLYPH(((uint8 *) (entry->data)), j);
2287 matthewc 44 }
2288 jsorg71 286 if (i + 2 < length)
2289     i += 3;
2290     else
2291     i += 2;
2292     length -= i;
2293     /* this will move pointer from start to first character after FE command */
2294     text = &(text[i]);
2295     i = 0;
2296 astrand 64 break;
2297 matty 17
2298 astrand 64 default:
2299     DO_GLYPH(text, i);
2300     i++;
2301     break;
2302 matty 29 }
2303 mmihalik 49 }
2304 jsorg71 564
2305     XSetFillStyle(g_display, g_gc, FillSolid);
2306    
2307 jsorg71 450 if (g_ownbackstore)
2308 jsorg71 278 {
2309     if (boxcx > 1)
2310 jsorg71 450 XCopyArea(g_display, g_backstore, g_wnd, g_gc, boxx,
2311 jsorg71 278 boxy, boxcx, boxcy, boxx, boxy);
2312     else
2313 jsorg71 450 XCopyArea(g_display, g_backstore, g_wnd, g_gc, clipx,
2314 jsorg71 278 clipy, clipcx, clipcy, clipx, clipy);
2315     }
2316 matty 9 }
2317    
2318 matty 25 void
2319     ui_desktop_save(uint32 offset, int x, int y, int cx, int cy)
2320 matty 9 {
2321 matty 28 Pixmap pix;
2322 matty 9 XImage *image;
2323    
2324 jsorg71 450 if (g_ownbackstore)
2325 matty 31 {
2326 jsorg71 450 image = XGetImage(g_display, g_backstore, x, y, cx, cy, AllPlanes, ZPixmap);
2327 matty 31 }
2328     else
2329     {
2330 jsorg71 450 pix = XCreatePixmap(g_display, g_wnd, cx, cy, g_depth);
2331     XCopyArea(g_display, g_wnd, pix, g_gc, x, y, cx, cy, 0, 0);
2332     image = XGetImage(g_display, pix, 0, 0, cx, cy, AllPlanes, ZPixmap);
2333     XFreePixmap(g_display, pix);
2334 matty 31 }
2335 matty 28
2336 jsorg71 450 offset *= g_bpp / 8;
2337     cache_put_desktop(offset, cx, cy, image->bytes_per_line, g_bpp / 8, (uint8 *) image->data);
2338 matty 28
2339     XDestroyImage(image);
2340 matty 9 }
2341    
2342 matty 25 void
2343     ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy)
2344 matty 9 {
2345     XImage *image;
2346 matty 10 uint8 *data;
2347 matty 9
2348 jsorg71 450 offset *= g_bpp / 8;
2349     data = cache_get_desktop(offset, cx, cy, g_bpp / 8);
2350 matty 10 if (data == NULL)
2351     return;
2352 matty 29
2353 jsorg71 450 image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0,
2354     (char *) data, cx, cy, BitmapPad(g_display), cx * g_bpp / 8);
2355 matty 29
2356 jsorg71 450 if (g_ownbackstore)
2357 matty 31 {
2358 jsorg71 450 XPutImage(g_display, g_backstore, g_gc, image, 0, 0, x, y, cx, cy);
2359     XCopyArea(g_display, g_backstore, g_wnd, g_gc, x, y, cx, cy, x, y);
2360 matty 31 }
2361     else
2362     {
2363 jsorg71 450 XPutImage(g_display, g_wnd, g_gc, image, 0, 0, x, y, cx, cy);
2364 matty 31 }
2365    
2366 matty 9 XFree(image);
2367     }
2368 jsorg71 713
2369     /* these do nothing here but are used in uiports */
2370     void
2371     ui_begin_update(void)
2372     {
2373     }
2374    
2375     void
2376     ui_end_update(void)
2377     {
2378     }

  ViewVC Help
Powered by ViewVC 1.1.26