1 // $Id: x11_display.cxx,v 1.46 2003/07/28 22:46:48 grumbel Exp $
3 // Construo - A wire-frame construction game
4 // Copyright (C) 2002 Ingo Ruhnke <grumbel@gmx.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #include <X11/Xutil.h>
23 #include <X11/cursorfont.h>
24 #include <X11/keysym.h>
26 #include "cursor_insert.xbm"
27 #include "cursor_insert_mask.xbm"
29 #include "cursor_select.xbm"
30 #include "cursor_select_mask.xbm"
32 #include "cursor_zoom.xbm"
33 #include "cursor_zoom_mask.xbm"
35 #include "cursor_scroll.xbm"
36 #include "cursor_scroll_mask.xbm"
38 #include "cursor_collider.xbm"
39 #include "cursor_collider_mask.xbm"
41 #include "construo_error.hxx"
42 #include "x11_display.hxx"
43 #include "settings.hxx"
44 #include "construo_main.hxx"
46 #include "controller.hxx"
47 #include "screen_manager.hxx"
49 extern ConstruoMain
* construo_main
;
50 Atom wm_delete_window
;
52 X11Display::X11Display(int w
, int h
, bool fullscreen_
)
53 : doublebuffer (settings
.doublebuffer
),
54 width(w
), height(h
), shift_pressed (false), fullscreen (fullscreen_
)
56 #ifndef HAVE_LIBXXF86VM
58 std::cout
<< "X11Display: libXxf86vm missing, fullscreen support not\n"
59 << " available, please recompile." << std::endl
;
62 std::cout
<< "Opening X11 display" << std::endl
;
63 display
= XOpenDisplay(NULL
);
66 throw ConstruoError("X11Display: Couldn't conncet to X server");
68 int screen
= DefaultScreen(display
);
69 XSetWindowAttributes attributes
;
71 attributes
.background_pixel
= BlackPixel(display
, screen
);
72 attributes
.border_pixel
= WhitePixel(display
, screen
);
75 attributes
.override_redirect
= True
;
77 attributes
.override_redirect
= False
;
79 attributes
.event_mask
=
89 colormap
= DefaultColormap (display
, screen
);
90 attributes
.colormap
= colormap
;
91 window
= XCreateWindow(display
, RootWindow(display
, screen
),
94 CopyFromParent
, InputOutput
, CopyFromParent
,
95 CWOverrideRedirect
| CWBackPixel
| CWBorderPixel
| CWEventMask
| CWColormap
,
98 { // Communicate a bit with the window manager
99 char *title
= construo_main
->get_title();
101 XTextProperty text_property
;
102 XStringListToTextProperty(&title
, 1, &text_property
);
103 XSizeHints size_hints
;
106 size_hints
.flags
= PSize
| PMinSize
| PMaxSize
;
108 size_hints
.width
= width
;
109 size_hints
.height
= height
;
111 size_hints
.min_width
= width
;
112 size_hints
.min_height
= height
;
113 size_hints
.max_width
= width
;
114 size_hints
.max_height
= height
;
127 // Set WM_DELETE_WINDOW atom in WM_PROTOCOLS property (to get window_delete requests).
128 wm_delete_window
= XInternAtom (display
, "WM_DELETE_WINDOW", False
);
129 XSetWMProtocols (display
, window
, &wm_delete_window
, 1);
133 drawable
= XCreatePixmap (display
, window
, width
, height
,
134 DefaultDepth(display
, screen
));
138 XMapRaised(display
, window
);
141 gcv
.foreground
= 0xFFFFFF;
142 gcv
.background
= 0x000000;
144 if (settings
.thick_lines
)
149 gc
= XCreateGC(display
, window
,
150 GCLineWidth
| GCForeground
| GCBackground
,
157 // Visual* visual = XDefaultVisual(display, DefaultScreen(display));
158 depth
= DefaultDepth(display
, DefaultScreen(display
));
159 if (depth
!= 16 && depth
!= 32)
161 std::cout
<< "X11Display: Warring color depth '" << depth
162 << "' not supported, Construo will be slow!" << std::endl
;
168 XColor cursor_fg
= get_xcolor(Color(1.0f
, 1.0f
, 1.0f
));
169 XColor cursor_bg
= get_xcolor(Color(0, 0, 0));
171 cursor_scroll_pix
= XCreateBitmapFromData (display
, window
, (char*)cursor_scroll_bits
,
172 cursor_scroll_width
, cursor_scroll_height
);
173 cursor_scroll_mask
= XCreateBitmapFromData (display
, window
, (char*)cursor_scroll_mask_bits
,
174 cursor_scroll_width
, cursor_scroll_height
);
175 cursor_scroll
= XCreatePixmapCursor(display
, cursor_scroll_pix
, cursor_scroll_mask
, &cursor_bg
, &cursor_fg
,
176 cursor_scroll_x_hot
, cursor_scroll_y_hot
);
178 cursor_zoom_pix
= XCreateBitmapFromData (display
, window
, (char*)cursor_zoom_bits
,
179 cursor_zoom_width
, cursor_zoom_height
);
180 cursor_zoom_mask
= XCreateBitmapFromData (display
, window
, (char*)cursor_zoom_mask_bits
,
181 cursor_zoom_width
, cursor_zoom_height
);
182 cursor_zoom
= XCreatePixmapCursor(display
, cursor_zoom_pix
, cursor_zoom_mask
, &cursor_bg
, &cursor_fg
,
183 cursor_zoom_x_hot
, cursor_zoom_y_hot
);
185 cursor_insert_pix
= XCreateBitmapFromData (display
, window
, (char*)cursor_insert_bits
,
186 cursor_insert_width
, cursor_insert_height
);
187 cursor_insert_mask
= XCreateBitmapFromData (display
, window
, (char*)cursor_insert_mask_bits
,
188 cursor_insert_width
, cursor_insert_height
);
189 cursor_insert
= XCreatePixmapCursor(display
, cursor_insert_pix
, cursor_insert_mask
, &cursor_bg
, &cursor_fg
,
190 cursor_insert_x_hot
, cursor_insert_y_hot
);
192 cursor_select_pix
= XCreateBitmapFromData (display
, window
, (char*)cursor_select_bits
,
193 cursor_select_width
, cursor_select_height
);
194 cursor_select_mask
= XCreateBitmapFromData (display
, window
, (char*)cursor_select_mask_bits
,
195 cursor_select_width
, cursor_select_height
);
196 cursor_select
= XCreatePixmapCursor(display
, cursor_select_pix
, cursor_select_mask
, &cursor_bg
, &cursor_fg
,
197 cursor_select_x_hot
, cursor_select_y_hot
);
199 cursor_collider_pix
= XCreateBitmapFromData (display
, window
, (char*)cursor_collider_bits
,
200 cursor_collider_width
, cursor_collider_height
);
201 cursor_collider_mask
= XCreateBitmapFromData (display
, window
, (char*)cursor_collider_mask_bits
,
202 cursor_collider_width
, cursor_collider_height
);
203 cursor_collider
= XCreatePixmapCursor(display
, cursor_collider_pix
, cursor_collider_mask
, &cursor_bg
, &cursor_fg
,
204 cursor_collider_x_hot
, cursor_collider_y_hot
);
207 set_cursor(CURSOR_INSERT
);
210 X11Display::~X11Display ()
212 std::cout
<< "Closing X11 display" << std::endl
;
216 std::cout
<< "X11Display: Restoring video mode" << std::endl
;
221 XFreePixmap (display
, drawable
);
223 XDestroyWindow (display
, window
);
224 XCloseDisplay(display
);
228 X11Display::set_cursor_real(CursorType cursor
)
233 XDefineCursor (display
, window
, cursor_insert
);
236 XDefineCursor (display
, window
, cursor_scroll
);
239 XDefineCursor (display
, window
, cursor_zoom
);
241 case CURSOR_COLLIDER
:
242 XDefineCursor (display
, window
, cursor_collider
);
245 XDefineCursor (display
, window
, cursor_select
);
248 std::cout
<< "X11Display: Unhandled cursor type: " << cursor
<< std::endl
;
254 X11Display::draw_lines (std::vector
<Line
>& lines
, Color color
, int wide
)
256 std::vector
<XSegment
> segments (lines
.size());
258 for (std::vector
<Line
>::size_type i
= 0; i
< lines
.size(); ++i
)
260 segments
[i
].x1
= static_cast<short>(lines
[i
].x1
);
261 segments
[i
].y1
= static_cast<short>(lines
[i
].y1
);
262 segments
[i
].x2
= static_cast<short>(lines
[i
].x2
);
263 segments
[i
].y2
= static_cast<short>(lines
[i
].y2
);
266 XDrawSegments(display
, drawable
, gc
, &*segments
.begin(), segments
.size());
270 X11Display::draw_circles(std::vector
<Circle
>& circles
, Color color
)
272 std::vector
<XArc
> arcs (circles
.size());
273 for (std::vector
<Circle
>::size_type i
= 0; i
< circles
.size(); ++i
)
275 arcs
[i
].x
= static_cast<short>(circles
[i
].x
- circles
[i
].r
);
276 arcs
[i
].y
= static_cast<short>(circles
[i
].y
- circles
[i
].r
);
277 arcs
[i
].width
= static_cast<short>(2 * circles
[i
].r
);
278 arcs
[i
].height
= static_cast<short>(2 * circles
[i
].r
);
280 arcs
[i
].angle2
= 360 * 64;
283 XSetForeground(display
, gc
, get_color_value(color
));
284 XFillArcs(display
, drawable
, gc
,
285 &*arcs
.begin(), arcs
.size());
289 X11Display::draw_line(float x1
, float y1
, float x2
, float y2
, Color color
, int wide
)
291 XSetForeground(display
, gc
, get_color_value(color
));
292 XDrawLine (display
, drawable
, gc
, (int) x1
, (int) y1
, (int) x2
, (int) y2
);
296 X11Display::draw_fill_rect(float x1
, float y1
, float x2
, float y2
, Color color
)
298 XSetForeground(display
, gc
, get_color_value(color
));
299 XFillRectangle (display
, drawable
, gc
,
301 int(x2
- x1
), int(y2
- y1
));
305 X11Display::draw_fill_circle(float x
, float y
, float r
, Color color
)
307 // FIXME: doesn't work
308 XSetForeground(display
, gc
, get_color_value(color
));
309 XFillArc(display
, drawable
, gc
,
311 int(r
*2), int(r
*2), 0,
316 X11Display::draw_circle(float x
, float y
, float r
, Color color
)
318 // FIXME: doesn't work
319 XSetForeground(display
, gc
, get_color_value(color
));
320 XDrawArc(display
, drawable
, gc
, int(x
-r
), int(y
-r
), int(r
*2.0f
), int(r
*2.0f
), 0, 360*64);
324 X11Display::draw_rect(float x1
, float y1
, float x2
, float y2
, Color color
)
326 XSetForeground(display
, gc
, get_color_value(color
));
327 XDrawRectangle (display
, drawable
, gc
,
329 int(x2
- x1
), int(y2
- y1
));
333 X11Display::draw_string(float x
, float y
, const std::string
& str
, Color color
)
335 XSetForeground(display
, gc
, get_color_value(color
));
336 XDrawString (display
, drawable
, gc
, int(x
), int(y
), str
.c_str (), str
.length ());
340 X11Display::draw_string_centered(float x
, float y
, const std::string
& str
, Color color
)
342 XSetForeground(display
, gc
, get_color_value(color
));
343 XDrawString (display
, drawable
, gc
,
344 int(x
) - ((str
.length() * 6) / 2), int(y
),
345 str
.c_str (), str
.length ());
349 X11Display::get_mouse_x ()
355 X11Display::get_mouse_y ()
361 X11Display::get_key (int key
)
367 X11Display::wait_for_events_blocking ()
370 while (read_event () == false);
371 } while (XPending (display
) > 0);
375 X11Display::wait_for_events ()
377 while (XPending (display
) > 0)
384 X11Display::read_event ()
388 XNextEvent (display
, &event
);
393 mouse_x
= event
.xmotion
.x
;
394 mouse_y
= event
.xmotion
.y
;
398 if (event
.xexpose
.count
== 0)
403 //std::cout << "NoExpose" << std::endl;
404 return false; // FIXME: Hack, no idea how to handle NoExpose
409 //std::cout << "ButtonID: " << event.xbutton.button << " " << event.xbutton.state << std::endl;
411 if (event
.xbutton
.button
== 1)
412 send_button_press(BUTTON_PRIMARY
);
413 else if (event
.xbutton
.button
== 2)
414 send_button_press(BUTTON_TERTIARY
);
415 else if (event
.xbutton
.button
== 3)
416 send_button_press(BUTTON_SECONDARY
);
417 else if (event
.xbutton
.button
== 4)
418 send_button_press(BUTTON_ZOOM_IN
);
419 else if (event
.xbutton
.button
== 5)
420 send_button_press(BUTTON_ZOOM_OUT
);
426 //std::cout << "ButtonID: " << event.xbutton.button << " " << event.xbutton.state << std::endl;
427 if (event
.xbutton
.button
== 1)
428 send_button_release(BUTTON_PRIMARY
);
429 else if (event
.xbutton
.button
== 2)
430 send_button_release(BUTTON_TERTIARY
);
431 else if (event
.xbutton
.button
== 3)
432 send_button_release(BUTTON_SECONDARY
);
433 else if (event
.xbutton
.button
== 4)
434 send_button_release(BUTTON_ZOOM_IN
);
435 else if (event
.xbutton
.button
== 5)
436 send_button_release(BUTTON_ZOOM_OUT
);
442 KeySym sym
= XLookupKeysym(&event
.xkey
,0);
448 //send_button_press(BUTTON_FULLSCREEN);
453 send_button_press(BUTTON_SCROLL_LEFT
);
457 send_button_press(BUTTON_SCROLL_RIGHT
);
461 send_button_press(BUTTON_SCROLL_UP
);
465 send_button_press(BUTTON_SCROLL_DOWN
);
469 send_button_press(BUTTON_ACTIONCAM
);
473 send_button_press(BUTTON_SCALE
);
477 send_button_press(BUTTON_JOIN
);
481 send_button_press(BUTTON_HIDEDOTS
);
485 send_button_press(BUTTON_SETVELOCITY
);
490 shift_pressed
= true;
493 send_button_press(BUTTON_MODE_CHANGE
);
496 send_button_press(BUTTON_FIX
);
499 send_button_press(BUTTON_FLIP
);
502 send_button_press(BUTTON_CLEAR
);
505 send_button_press(BUTTON_DELETE
);
508 send_button_press(BUTTON_ESCAPE
);
511 send_button_press(BUTTON_UNDO
);
514 send_button_press(BUTTON_REDO
);
517 send_button_press(BUTTON_DUPLICATE
);
520 send_button_press(BUTTON_RUN
);
523 send_button_press(BUTTON_GRID
);
526 send_button_press(BUTTON_TOGGLESLOWMO
);
528 case 65451: // FIXME: insert symbol here
531 send_button_press(BUTTON_ZOOM_IN
);
533 case 65453: // FIXME: insert symbol here
535 send_button_press(BUTTON_ZOOM_OUT
);
538 send_load_or_save(0);
541 send_load_or_save(1);
544 send_load_or_save(2);
547 send_load_or_save(3);
550 send_load_or_save(4);
553 send_load_or_save(5);
556 send_load_or_save(6);
559 send_load_or_save(7);
562 send_load_or_save(8);
565 send_load_or_save(9);
569 std::cout
<< "X11Display: unhandled keypress: " << sym
<< " " << XK_grave
<< std::endl
;
577 KeySym sym
= XLookupKeysym(&event
.xkey
,0);
583 shift_pressed
= false;
586 //std::cout << "X11Display: unhandled keyrelease: " << sym << " " << XK_f << std::endl;
592 case ConfigureNotify
:
593 //std::cout << "X11Display: " << event.xconfigure.width << "x" << event.xconfigure.height
594 //<< "+" << event.xconfigure.x << "+" << event.xconfigure.y << std::endl;
598 std::cout
<< "Window got destroyed" << std::endl
;
602 std::cout
<< "X11Display: got client message" << std::endl
;
603 // Window close request
604 if ((int) event
.xclient
.data
.l
[0] == (int) wm_delete_window
) {
605 std::cout
<< "Window is destroyed" << std::endl
;
606 send_button_press(BUTTON_ESCAPE
);
611 std::cout
<< "X11Display: Unhandled event: " << event
.type
<< std::endl
;
618 X11Display::send_load_or_save(int n
)
621 send_button_press(BUTTON_QUICKLOAD0
+ n
);
623 send_button_press(BUTTON_QUICKSAVE0
+ n
);
627 X11Display::send_button_press (int i
)
630 ev
.button
.type
= BUTTON_EVENT
;
632 ev
.button
.pressed
= true;
637 X11Display::send_button_release (int i
)
640 ev
.button
.type
= BUTTON_EVENT
;
642 ev
.button
.pressed
= false;
649 XSetForeground (display
, gc
, 0x000000);
650 XFillRectangle (display
, drawable
, gc
, 0, 0, width
, height
);
654 X11Display::flip (int x1
, int y1
, int x2
, int y2
)
665 //flip_rects.push_back(flip_rect);
670 X11Display::real_flip ()
674 for (std::vector
<FlipRect
>::iterator i
= flip_rects
.begin ();
675 i
!= flip_rects
.end ();
678 XCopyArea (display
, drawable
, window
, gc
,
679 i
->x1
, i
->y1
, // source
680 i
->x2
- i
->x1
, i
->y2
- i
->y1
, // width/height
681 i
->x1
, i
->y1
// destination
693 // FIXME: Use another gc here
694 XCopyArea (display
, drawable
, window
, gc
,
704 X11Display::save_mode()
706 #ifdef HAVE_LIBXXF86VM
707 memset(&orig_modeline
, 0, sizeof(orig_modeline
));
709 // Get the current display settings for later restore
710 XF86VidModeGetModeLine(display
,
711 DefaultScreen(display
),
715 XF86VidModeGetViewPort(display
,
716 DefaultScreen(display
),
719 std::cout
<< "save_mode: "
720 << orig_dotclock
<< " "
721 << orig_viewport_x
<< ", " << orig_viewport_y
<< std::endl
;
723 #endif /* HAVE_LIBXXF86VM */
727 X11Display::enter_fullscreen ()
729 #ifndef HAVE_LIBXXF86VM
730 std::cout
<< "X11Display: libXxf86vm missing, fullscreen support not\n"
731 << " available, please recompile." << std::endl
;
736 if (XF86VidModeQueryExtension(display
, &event_base
, &error_base
) != True
)
738 // No VidMode extension available, bailout
739 std::cout
<< "X11Display: VidMode extension not available, bailout." << std::endl
;
745 XF86VidModeModeInfo
**modes
;
748 if (XF86VidModeGetAllModeLines(display
,
749 DefaultScreen(display
),
750 &nmodes
,&modes
)) // FIXME: memleak
752 std::cout
<< "VideoModes: (searching for " << width
<< "x" << height
<< ")" << std::endl
;
753 for (int i
= 0; i
< nmodes
; i
++)
755 //std::cout << i << " " << mode.Width,mode.Height);
756 std::cout
<< " " << modes
[i
]->hdisplay
757 << "x" << modes
[i
]->vdisplay
;
759 if (modes
[i
]->hdisplay
== width
&& modes
[i
]->vdisplay
== height
)
764 std::cout
<< std::endl
;
767 if (mode_index
!= -1) // Found a good mode
770 { // FIXME: doesn't work to change override_redirect after window creation
771 std::cout
<< "Changing override_redirect" << std::endl
;
772 // Switch border away and go to 0,0
773 XSetWindowAttributes attributes
;
774 attributes
.override_redirect
= True
;
775 XChangeWindowAttributes(display
, window
, CWOverrideRedirect
, &attributes
);
778 std::cout
<< "Switching to: "
779 << modes
[mode_index
]->hdisplay
<< "x" << modes
[mode_index
]->vdisplay
782 if(XF86VidModeSwitchToMode(display
,
783 DefaultScreen(display
),
788 { // Now that we have switched to the correct mode, we
789 // need to position the Viewport correct to the window
792 // Get the windows absolute position (aka relative to
794 XTranslateCoordinates(display
, window
, DefaultRootWindow(display
),
796 &x
, &y
, &child_window
);
797 XF86VidModeSetViewPort(display
, DefaultScreen(display
), x
, y
);
800 // Hijack the focus (works only till the next focus change)
801 XSetInputFocus(display
, window
, RevertToParent
, CurrentTime
);
803 // Capture the pointer
804 if (XGrabPointer(display
, window
, true, 0, GrabModeAsync
, GrabModeAsync
,
805 window
, None
, CurrentTime
) != GrabSuccess
)
807 std::cout
<< "X11Display: Couldn't grab the pointer" << std::endl
;
812 std::cout
<< "X11Display: Throuble switiching to fullscreen?!" << std::endl
;
815 else // No mode found
817 std::cout
<< "Disabling override redirect" << std::endl
;
818 // Fullscreen not possible, switch Window attributes back to windowed mode
819 XSetWindowAttributes attributes
;
820 attributes
.override_redirect
= False
;
821 XChangeWindowAttributes(display
, window
, CWOverrideRedirect
, &attributes
);
823 // Remap the Window to let the allow override to take effect
824 XUnmapWindow (display
, window
);
825 XMapRaised(display
, window
);
830 std::cout
<< "X11Display: Couldn't get available video modes" << std::endl
;
838 while (!ScreenManager::instance ()->is_finished ())
840 ScreenManager::instance ()->run_once();
842 if (Controller::instance()->is_running())
844 system_context
->sleep (0); // FIXME: limit CPU usage via brute force
849 wait_for_events_blocking();
855 X11Display::toggle_fullscreen()
857 //std::cout << "Fullscreen state: " << fullscreen << std::endl;
866 X11Display::leave_fullscreen()
868 #ifdef HAVE_LIBXXF86VM
869 std::cout
<< "X11Display::restore_mode()" << std::endl
;
871 XF86VidModeModeInfo modeinfo
;
873 modeinfo
.dotclock
= orig_dotclock
;
875 // Copy XF86VidModeModeLine struct into XF86VidModeModeInfo
876 modeinfo
.hdisplay
= orig_modeline
.hdisplay
;
877 modeinfo
.hsyncstart
= orig_modeline
.hsyncstart
;
878 modeinfo
.hsyncend
= orig_modeline
.hsyncend
;
879 modeinfo
.htotal
= orig_modeline
.htotal
;
880 modeinfo
.hskew
= orig_modeline
.hskew
;
881 modeinfo
.vdisplay
= orig_modeline
.vdisplay
;
882 modeinfo
.vsyncstart
= orig_modeline
.vsyncstart
;
883 modeinfo
.vsyncend
= orig_modeline
.vsyncend
;
884 modeinfo
.vtotal
= orig_modeline
.vtotal
;
885 modeinfo
.flags
= orig_modeline
.flags
;
886 modeinfo
.privsize
= orig_modeline
.privsize
;
887 modeinfo
.c_private
= orig_modeline
.c_private
;
889 XF86VidModeSwitchToMode(display
, DefaultScreen(display
),
891 XF86VidModeSetViewPort(display
, DefaultScreen(display
),
892 orig_viewport_x
, orig_viewport_y
);
894 XUngrabPointer(display
, CurrentTime
);
901 X11Display::set_clip_rect (int x1
, int y1
, int x2
, int y2
)
907 rect
[0].width
= x2
- x1
+ 1;
908 rect
[0].height
= y2
- y1
+ 1;
910 XSetClipRectangles (display
, gc
,
917 X11Display::get_color_value(const Color
& color
)
923 return static_cast<unsigned int>(color
.get_as_rrggbb());
925 return int(31 * color
.b
) | (int((63 * color
.g
)) << 5) | (int((31 * color
.r
)) << 11);
927 { // This is extremly slow!
930 x_color
.red
= int(color
.r
* 65535);
931 x_color
.green
= int(color
.g
* 65535);
932 x_color
.blue
= int(color
.b
* 65535);
934 XAllocColor(display
, colormap
, &x_color
);
936 return x_color
.pixel
;
943 X11Display::get_xcolor(const Color
& color
)
947 x_color
.red
= int(color
.r
* 65535);
948 x_color
.green
= int(color
.g
* 65535);
949 x_color
.blue
= int(color
.b
* 65535);
951 XAllocColor(display
, colormap
, &x_color
);