camera position fix, rework transportque locks again, camera drag tweaks, window...
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / bcwindowbase.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcbitmap.h"
23 #include "bcclipboard.h"
24 #include "bcdisplay.h"
25 #include "bcdisplayinfo.h"
26 #include "bcmenubar.h"
27 #include "bcpixmap.h"
28 #include "bcpopup.h"
29 #include "bcpopupmenu.h"
30 #include "bcrepeater.h"
31 #include "bcresources.h"
32 #include "bcsignals.h"
33 #include "bcsubwindow.h"
34 #include "bcsynchronous.h"
35 #include "bctimer.h"
36 #include "bcwindowbase.h"
37 #include "bcwindowevents.h"
38 #include "bccmodels.h"
39 #include "bccolors.h"
40 #include "condition.h"
41 #include "cursors.h"
42 #include "bchash.h"
43 #include "fonts.h"
44 #include "keys.h"
45 #include "language.h"
46 #include "mutex.h"
47 #include "sizes.h"
48 #include "vframe.h"
49 #include "workarounds.h"
50
51 #ifdef HAVE_GL
52 #include <GL/gl.h>
53 #endif
54 #include <string.h>
55 #include <unistd.h>
56 #include <wchar.h>
57 #include <typeinfo>
58
59 #include <X11/extensions/Xinerama.h>
60 #include <X11/extensions/Xvlib.h>
61 #include <X11/extensions/shape.h>
62 #include <X11/XF86keysym.h>
63 #include <X11/Sunkeysym.h>
64
65 BC_ResizeCall::BC_ResizeCall(int w, int h)
66 {
67         this->w = w;
68         this->h = h;
69 }
70
71
72
73
74
75
76
77 int BC_WindowBase::shm_completion_event = -1;
78
79
80
81 BC_Resources BC_WindowBase::resources;
82
83 Window XGroupLeader = 0;
84
85 Mutex BC_KeyboardHandlerLock::keyboard_listener_mutex("keyboard_listener",0);
86 ArrayList<BC_KeyboardHandler*> BC_KeyboardHandler::listeners;
87
88 BC_WindowBase::BC_WindowBase()
89 {
90 //printf("BC_WindowBase::BC_WindowBase 1\n");
91         BC_WindowBase::initialize();
92 }
93
94 BC_WindowBase::~BC_WindowBase()
95 {
96 #ifdef SINGLE_THREAD
97         BC_Display::lock_display("BC_WindowBase::~BC_WindowBase");
98 #else
99         if(window_type == MAIN_WINDOW)
100                 lock_window("BC_WindowBase::~BC_WindowBase");
101 #endif
102
103 #ifdef HAVE_LIBXXF86VM
104         if(window_type == VIDMODE_SCALED_WINDOW && vm_switched) {
105                 restore_vm();
106         }
107 #endif
108         is_deleting = 1;
109
110         hide_tooltip();
111         if(window_type != MAIN_WINDOW)
112         {
113 // stop event input
114                 XSelectInput(top_level->display, this->win, 0);
115                 XSync(top_level->display,0);
116 #ifndef SINGLE_THREAD
117                 top_level->dequeue_events(win);
118 #endif
119 // drop active window refs to this
120                 if(top_level->active_menubar == this) top_level->active_menubar = 0;
121                 if(top_level->active_popup_menu == this) top_level->active_popup_menu = 0;
122                 if(top_level->active_subwindow == this) top_level->active_subwindow = 0;
123 // drop motion window refs to this
124                 if(top_level->motion_events && top_level->last_motion_win == this->win)
125                         top_level->motion_events = 0;
126
127 // Remove pointer from parent window to this
128                 parent_window->subwindows->remove(this);
129         }
130
131         if(grab_active) grab_active->active_grab = 0;
132         if(icon_window) delete icon_window;
133         if(window_type == POPUP_WINDOW)
134                 parent_window->remove_popup(this);
135
136 // Delete the subwindows
137         if(subwindows)
138         {
139                 while(subwindows->total)
140                 {
141 // Subwindow removes its own pointer
142                         delete subwindows->values[0];
143                 }
144                 delete subwindows;
145         }
146
147         delete pixmap;
148
149 //printf("delete glx=%08x, win=%08x %s\n", (unsigned)glx_win, (unsigned)win, title);
150 #ifdef HAVE_GL
151         if( get_resources()->get_synchronous() && glx_win != 0 ) {
152                 get_resources()->get_synchronous()->delete_window(this);
153         }
154 #endif
155         XDestroyWindow(top_level->display, win);
156
157         if(bg_pixmap && !shared_bg_pixmap) delete bg_pixmap;
158         if(icon_pixmap) delete icon_pixmap;
159         if(temp_bitmap) delete temp_bitmap;
160         top_level->active_bitmaps.remove_buffers(this);
161         if(_7segment_pixmaps)
162         {
163                 for(int i = 0; i < TOTAL_7SEGMENT; i++)
164                         delete _7segment_pixmaps[i];
165
166                 delete [] _7segment_pixmaps;
167         }
168
169
170
171         if(window_type == MAIN_WINDOW)
172         {
173                 XFreeGC(display, gc);
174                 static XFontStruct *BC_WindowBase::*xfont[] = {
175                          &BC_WindowBase::smallfont,
176                          &BC_WindowBase::mediumfont,
177                          &BC_WindowBase::largefont,
178                          &BC_WindowBase::bigfont,
179                          &BC_WindowBase::clockfont,
180                 };
181                 for( int i=sizeof(xfont)/sizeof(xfont[0]); --i>=0; )
182                         XFreeFont(display, this->*xfont[i]);
183
184 #ifdef HAVE_XFT
185 // prevents a bug when Xft closes with unrefd fonts
186                 FcPattern *defaults = FcPatternCreate();
187                 FcPatternAddInteger(defaults, "maxunreffonts", 0);
188                 XftDefaultSet(display, defaults);
189
190                 static void *BC_WindowBase::*xft_font[] = {
191                          &BC_WindowBase::smallfont_xft,
192                          &BC_WindowBase::mediumfont_xft,
193                          &BC_WindowBase::largefont_xft,
194                          &BC_WindowBase::bigfont_xft,
195                          &BC_WindowBase::bold_smallfont_xft,
196                          &BC_WindowBase::bold_mediumfont_xft,
197                          &BC_WindowBase::bold_largefont_xft,
198                          &BC_WindowBase::clockfont_xft,
199                 };
200                 for( int i=sizeof(xft_font)/sizeof(xft_font[0]); --i>=0; ) {
201                         XftFont *xft = (XftFont *)(this->*xft_font[i]);
202                         if( xft ) xftFontClose (display, xft);
203                 }
204 #endif
205                 finit_im();
206                 flush();
207                 sync_display();
208
209                 if( xinerama_info )
210                         XFree(xinerama_info);
211                 xinerama_screens = 0;
212                 xinerama_info = 0;
213                 if( xvideo_port_id >= 0 )
214                         XvUngrabPort(display, xvideo_port_id, CurrentTime);
215
216                 unlock_window();
217 // Must be last reference to display.
218 // _XftDisplayInfo needs a lock.
219                 get_resources()->create_window_lock->lock("BC_WindowBase::~BC_WindowBase");
220                 XCloseDisplay(display);
221                 get_resources()->create_window_lock->unlock();
222
223 // clipboard uses a different display connection
224                 clipboard->stop_clipboard();
225                 delete clipboard;
226         }
227
228         resize_history.remove_all_objects();
229
230 #ifndef SINGLE_THREAD
231         common_events.remove_all_objects();
232         delete event_lock;
233         delete event_condition;
234         delete init_lock;
235 #else
236         top_level->window_lock = 0;
237         BC_Display::unlock_display();
238 #endif
239         delete cursor_timer;
240
241 #if HAVE_GL
242         if( glx_fbcfgs_window ) XFree(glx_fbcfgs_window);
243         if( glx_fbcfgs_pbuffer) XFree(glx_fbcfgs_pbuffer);
244         if( glx_fbcfgs_pixmap ) XFree(glx_fbcfgs_pixmap);
245 #endif
246
247         UNSET_ALL_LOCKS(this)
248 }
249
250 int BC_WindowBase::initialize()
251 {
252         done = 0;
253         done_set = 0;
254         window_running = 0;
255         display_lock_owner = 0;
256         test_keypress = 0;
257         keys_return[0] = 0;
258         is_deleting = 0;
259         window_lock = 0;
260         resend_event_window = 0;
261         x = 0;
262         y = 0;
263         w = 0;
264         h = 0;
265         bg_color = -1;
266         line_width = 1;
267         line_dashes = 0;
268         top_level = 0;
269         parent_window = 0;
270         subwindows = 0;
271         xinerama_info = 0;
272         xinerama_screens = 0;
273         xvideo_port_id = -1;
274         video_on = 0;
275         motion_events = 0;
276         resize_events = 0;
277         translation_events = 0;
278         ctrl_mask = shift_mask = alt_mask = 0;
279         cursor_x = cursor_y = button_number = 0;
280         button_down = 0;
281         button_pressed = 0;
282         button_time1 = 0;
283         button_time2 = 0;
284         button_time3 = 0;
285         double_click = 0;
286         triple_click = 0;
287         event_win = 0;
288         last_motion_win = 0;
289         key_pressed = 0;
290         active_grab = 0;
291         grab_active = 0;
292         active_menubar = 0;
293         active_popup_menu = 0;
294         active_subwindow = 0;
295         cursor_entered = 0;
296         pixmap = 0;
297         bg_pixmap = 0;
298         _7segment_pixmaps = 0;
299         tooltip_text = 0;
300         force_tooltip = 0;
301 //      next_repeat_id = 0;
302         tooltip_popup = 0;
303         current_font = MEDIUMFONT;
304         current_color = BLACK;
305         current_cursor = ARROW_CURSOR;
306         hourglass_total = 0;
307         is_dragging = 0;
308         shared_bg_pixmap = 0;
309         icon_pixmap = 0;
310         icon_window = 0;
311         window_type = MAIN_WINDOW;
312         translation_count = 0;
313         x_correction = y_correction = 0;
314         temp_bitmap = 0;
315         tooltip_on = 0;
316         temp_cursor = 0;
317         toggle_value = 0;
318         toggle_drag = 0;
319         has_focus = 0;
320         is_hourglass = 0;
321         is_transparent = 0;
322 #ifdef HAVE_LIBXXF86VM
323         vm_switched = 0;
324 #endif
325         input_method = 0;
326         input_context = 0;
327
328         smallfont = 0;
329         mediumfont = 0;
330         largefont = 0;
331         bigfont = 0;
332         clockfont = 0;
333
334         smallfont_xft = 0;
335         mediumfont_xft = 0;
336         largefont_xft = 0;
337         bigfont_xft = 0;
338         clockfont_xft = 0;
339
340         bold_smallfont_xft = 0;
341         bold_mediumfont_xft = 0;
342         bold_largefont_xft = 0;
343 #ifdef SINGLE_THREAD
344         completion_lock = new Condition(0, "BC_WindowBase::completion_lock");
345 #else
346 // Need these right away since put_event is called before run_window sometimes.
347         event_lock = new Mutex("BC_WindowBase::event_lock");
348         event_condition = new Condition(0, "BC_WindowBase::event_condition");
349         init_lock = new Condition(0, "BC_WindowBase::init_lock");
350 #endif
351
352         cursor_timer = new Timer;
353         event_thread = 0;
354 #ifdef HAVE_GL
355         glx_fbcfgs_window = 0;  n_fbcfgs_window = 0;
356         glx_fbcfgs_pbuffer = 0; n_fbcfgs_pbuffer = 0;
357         glx_fbcfgs_pixmap = 0;  n_fbcfgs_pixmap = 0;
358
359         glx_fb_config = 0;
360         glx_win_context = 0;
361         glx_win = 0;
362 #endif
363
364         flash_enabled = 1;
365         win = 0;
366         return 0;
367 }
368
369
370
371 #define DEFAULT_EVENT_MASKS EnterWindowMask | \
372                         LeaveWindowMask | \
373                         ButtonPressMask | \
374                         ButtonReleaseMask | \
375                         PointerMotionMask | \
376                         FocusChangeMask
377
378
379 int BC_WindowBase::create_window(BC_WindowBase *parent_window, const char *title,
380                 int x, int y, int w, int h, int minw, int minh, int allow_resize,
381                 int private_color, int hide, int bg_color, const char *display_name,
382                 int window_type, BC_Pixmap *bg_pixmap, int group_it)
383 {
384         XSetWindowAttributes attr;
385         unsigned long mask;
386         XSizeHints size_hints;
387         int root_w;
388         int root_h;
389 #ifdef HAVE_LIBXXF86VM
390         int vm;
391 #endif
392
393         id = get_resources()->get_id();
394         if(parent_window) top_level = parent_window->top_level;
395         if( top_level ) lock_window("BC_WindowBase::create_window");
396         get_resources()->create_window_lock->lock("BC_WindowBase::create_window");
397
398 #ifdef HAVE_LIBXXF86VM
399         if(window_type == VIDMODE_SCALED_WINDOW)
400                 closest_vm(&vm,&w,&h);
401 #endif
402
403         this->x = x;
404         this->y = y;
405         this->w = w;
406         this->h = h;
407         this->bg_color = bg_color;
408         this->window_type = window_type;
409         this->hidden = hide;
410         this->private_color = private_color;
411         this->parent_window = parent_window;
412         this->bg_pixmap = bg_pixmap;
413         this->allow_resize = allow_resize;
414         if(display_name)
415                 strcpy(this->display_name, display_name);
416         else
417                 this->display_name[0] = 0;
418
419         put_title(title);
420         if(bg_pixmap) shared_bg_pixmap = 1;
421
422         subwindows = new BC_SubWindowList;
423
424         if(window_type == MAIN_WINDOW)
425         {
426                 top_level = this;
427                 parent_window = this;
428
429
430 #ifdef SINGLE_THREAD
431                 display = BC_Display::get_display(display_name);
432                 BC_Display::lock_display("BC_WindowBase::create_window");
433 //              BC_Display::display_global->new_window(this);
434 #else
435
436 // get the display connection
437
438 // This function must be the first Xlib
439 // function a multi-threaded program calls
440                 XInitThreads();
441                 display = init_display(display_name);
442                 if( shm_completion_event < 0 ) shm_completion_event =
443                         ShmCompletion + XShmGetEventBase(display);
444 #endif
445                 lock_window("BC_WindowBase::create_window 1");
446
447                 screen = DefaultScreen(display);
448                 rootwin = RootWindow(display, screen);
449 // window placement boundaries
450                 if( !xinerama_screens && XineramaIsActive(display) )
451                         xinerama_info = XineramaQueryScreens(display, &xinerama_screens);
452                 root_w = get_root_w(0);
453                 root_h = get_root_h(0);
454
455 #if HAVE_GL
456                 vis = get_glx_visual(display);
457                 if( !vis )
458 #endif
459                         vis = DefaultVisual(display, screen);
460
461                 default_depth = DefaultDepth(display, screen);
462
463                 client_byte_order = (*(const u_int32_t*)"a   ") & 0x00000001;
464                 server_byte_order = (XImageByteOrder(display) == MSBFirst) ? 0 : 1;
465
466
467 // This must be done before fonts to know if antialiasing is available.
468                 init_colors();
469 // get the resources
470                 if(resources.use_shm < 0) resources.initialize_display(this);
471                 x_correction = BC_DisplayInfo::get_left_border();
472                 y_correction = BC_DisplayInfo::get_top_border();
473
474 // clamp window placement
475                 if(this->x + this->w + x_correction > root_w)
476                         this->x = root_w - this->w - x_correction;
477                 if(this->y + this->h + y_correction > root_h)
478                         this->y = root_h - this->h - y_correction;
479                 if(this->x < 0) this->x = 0;
480                 if(this->y < 0) this->y = 0;
481
482                 if(this->bg_color == -1)
483                         this->bg_color = resources.get_bg_color();
484
485 // printf("bcwindowbase 1 %s\n", title);
486 // if(window_type == MAIN_WINDOW) sleep(1);
487 // printf("bcwindowbase 10\n");
488                 init_fonts();
489                 init_gc();
490                 init_cursors();
491
492 // Create the window
493                 mask = CWEventMask | CWBackPixel | CWColormap | CWCursor;
494
495                 attr.event_mask = DEFAULT_EVENT_MASKS |
496                         StructureNotifyMask |
497                         KeyPressMask |
498                         KeyReleaseMask;
499
500                 attr.background_pixel = get_color(this->bg_color);
501                 attr.colormap = cmap;
502                 attr.cursor = get_cursor_struct(ARROW_CURSOR);
503
504                 win = XCreateWindow(display, rootwin,
505                         this->x, this->y, this->w, this->h, 0,
506                         top_level->default_depth, InputOutput,
507                         vis, mask, &attr);
508                 XGetNormalHints(display, win, &size_hints);
509
510                 size_hints.flags = PSize | PMinSize | PMaxSize;
511                 size_hints.width = this->w;
512                 size_hints.height = this->h;
513                 size_hints.min_width = allow_resize ? minw : this->w;
514                 size_hints.max_width = allow_resize ? 32767 : this->w;
515                 size_hints.min_height = allow_resize ? minh : this->h;
516                 size_hints.max_height = allow_resize ? 32767 : this->h;
517                 if(x > -BC_INFINITY && x < BC_INFINITY)
518                 {
519                         size_hints.flags |= PPosition;
520                         size_hints.x = this->x;
521                         size_hints.y = this->y;
522                 }
523                 XSetWMProperties(display, win, 0, 0, 0, 0, &size_hints, 0, 0);
524                 get_atoms();
525                 set_title(title);
526 #ifndef SINGLE_THREAD
527                 clipboard = new BC_Clipboard(this);
528                 clipboard->start_clipboard();
529 #endif
530
531
532                 if (group_it)
533                 {
534                         Atom ClientLeaderXAtom;
535                         if (XGroupLeader == 0)
536                                 XGroupLeader = win;
537                         const char *instance_name = "cinelerra";
538                         const char *class_name = "Cinelerra";
539                         XClassHint *class_hints = XAllocClassHint();
540                         class_hints->res_name = (char*)instance_name;
541                         class_hints->res_class = (char*)class_name;
542                         XSetClassHint(top_level->display, win, class_hints);
543                         XFree(class_hints);
544                         ClientLeaderXAtom = XInternAtom(display, "WM_CLIENT_LEADER", True);
545                         XChangeProperty(display, win, ClientLeaderXAtom, XA_WINDOW, 32,
546                                 PropModeReplace, (unsigned char *)&XGroupLeader, true);
547                 }
548                 init_im();
549                 set_icon(get_resources()->default_icon);
550         }
551
552 #ifdef HAVE_LIBXXF86VM
553         if(window_type == VIDMODE_SCALED_WINDOW && vm != -1)
554         {
555                 scale_vm (vm);
556                 vm_switched = 1;
557         }
558 #endif
559
560 #ifdef HAVE_LIBXXF86VM
561         if(window_type == POPUP_WINDOW || window_type == VIDMODE_SCALED_WINDOW)
562 #else
563         if(window_type == POPUP_WINDOW)
564 #endif
565         {
566                 mask = CWEventMask | CWBackPixel | CWColormap |
567                         CWOverrideRedirect | CWSaveUnder | CWCursor;
568
569                 attr.event_mask = DEFAULT_EVENT_MASKS |
570                         KeyPressMask |
571                         KeyReleaseMask;
572
573                 if(this->bg_color == -1)
574                         this->bg_color = resources.get_bg_color();
575                 attr.background_pixel = top_level->get_color(bg_color);
576                 attr.colormap = top_level->cmap;
577                 if(top_level->is_hourglass)
578                         attr.cursor = top_level->get_cursor_struct(HOURGLASS_CURSOR);
579                 else
580                         attr.cursor = top_level->get_cursor_struct(ARROW_CURSOR);
581                 attr.override_redirect = True;
582                 attr.save_under = True;
583
584                 win = XCreateWindow(top_level->display,
585                         top_level->rootwin, this->x, this->y, this->w, this->h, 0,
586                         top_level->default_depth, InputOutput, top_level->vis, mask,
587                         &attr);
588                 top_level->add_popup(this);
589         }
590
591         if(window_type == SUB_WINDOW)
592         {
593                 mask = CWEventMask | CWBackPixel | CWCursor;
594                 attr.event_mask = DEFAULT_EVENT_MASKS;
595                 attr.background_pixel = top_level->get_color(this->bg_color);
596                 if(top_level->is_hourglass)
597                         attr.cursor = top_level->get_cursor_struct(HOURGLASS_CURSOR);
598                 else
599                         attr.cursor = top_level->get_cursor_struct(ARROW_CURSOR);
600                 win = XCreateWindow(top_level->display,
601                         parent_window->win, this->x, this->y, this->w, this->h, 0,
602                         top_level->default_depth, InputOutput, top_level->vis, mask,
603                         &attr);
604                 init_window_shape();
605                 if(!hidden) XMapWindow(top_level->display, win);
606         }
607
608 // Create pixmap for all windows
609         pixmap = new BC_Pixmap(this, this->w, this->h);
610
611 // Set up options for main window
612         if(window_type == MAIN_WINDOW)
613         {
614                 if(get_resources()->bg_image && !bg_pixmap && bg_color < 0)
615                 {
616                         this->bg_pixmap = new BC_Pixmap(this,
617                                 get_resources()->bg_image,
618                                 PIXMAP_OPAQUE);
619                 }
620
621                 if(!hidden) show_window();
622
623         }
624
625         draw_background(0, 0, this->w, this->h);
626
627         flash(-1, -1, -1, -1, 0);
628
629 // Set up options for popup window
630 #ifdef HAVE_LIBXXF86VM
631         if(window_type == POPUP_WINDOW || window_type == VIDMODE_SCALED_WINDOW)
632 #else
633         if(window_type == POPUP_WINDOW)
634 #endif
635         {
636                 init_window_shape();
637                 if(!hidden) show_window();
638         }
639         get_resources()->create_window_lock->unlock();
640         unlock_window();
641
642         return 0;
643 }
644
645 Display* BC_WindowBase::init_display(const char *display_name)
646 {
647         Display* display;
648
649         if(display_name && display_name[0] == 0) display_name = NULL;
650         if((display = XOpenDisplay(display_name)) == NULL) {
651                 printf("BC_WindowBase::init_display: cannot connect to X server %s\n",
652                         display_name);
653                 if(getenv("DISPLAY") == NULL) {
654                         printf(_("'DISPLAY' environment variable not set.\n"));
655                         exit(1);
656                 }
657 // Try again with default display.
658                 if((display = XOpenDisplay(0)) == NULL) {
659                         printf("BC_WindowBase::init_display: cannot connect to default X server.\n");
660                         exit(1);
661                 }
662         }
663
664         static int xsynch = -1;
665         if( xsynch < 0 ) {
666                 const char *cp = getenv("CIN_XSYNCH");
667                 xsynch = !cp ? 0 : atoi(cp);
668         }
669         if( xsynch > 0 )
670                 XSynchronize(display, True);
671
672         return display;
673 }
674
675 Display* BC_WindowBase::get_display()
676 {
677         return top_level->display;
678 }
679
680 int BC_WindowBase::get_screen()
681 {
682         return top_level->screen;
683 }
684
685 int BC_WindowBase::run_window()
686 {
687         done_set = done = 0;
688         return_value = 0;
689
690
691 // Events may have been sent before run_window so can't initialize them here.
692
693 #ifdef SINGLE_THREAD
694         set_repeat(get_resources()->tooltip_delay);
695         BC_Display::display_global->new_window(this);
696
697 // If the first window created, run the display loop in this thread.
698         if(BC_Display::display_global->is_first(this))
699         {
700                 BC_Display::unlock_display();
701                 BC_Display::display_global->loop();
702         }
703         else
704         {
705                 BC_Display::unlock_display();
706                 completion_lock->lock("BC_WindowBase::run_window");
707         }
708
709         BC_Display::lock_display("BC_WindowBase::run_window");
710         BC_Display::display_global->delete_window(this);
711
712         unset_all_repeaters();
713         hide_tooltip();
714         BC_Display::unlock_display();
715
716 #else // SINGLE_THREAD
717
718
719
720 // Start tooltips
721         set_repeat(get_resources()->tooltip_delay);
722
723 // Start X server events
724         event_thread = new BC_WindowEvents(this);
725         event_thread->start();
726
727 // Release wait lock
728         window_running = 1;
729         init_lock->unlock();
730
731 // Handle common events
732         while(!done)
733         {
734                 dispatch_event(0);
735         }
736
737         unset_all_repeaters();
738         hide_tooltip();
739         delete event_thread;
740         event_thread = 0;
741         event_condition->reset();
742         common_events.remove_all_objects();
743         window_running = 0;
744         done = 0;
745
746 #endif // SINGLE_THREAD
747
748         return return_value;
749 }
750
751 int BC_WindowBase::get_key_masks(unsigned int key_state)
752 {
753 // printf("BC_WindowBase::get_key_masks %llx\n",
754 // event->xkey.state);
755         ctrl_mask = (key_state & ControlMask) ? 1 : 0;  // ctrl key down
756         shift_mask = (key_state & ShiftMask) ? 1 : 0;   // shift key down
757         alt_mask = (key_state & Mod1Mask) ? 1 : 0;      // alt key down
758         return 0;
759 }
760
761
762 void BC_WindowBase::add_keyboard_listener(int(BC_WindowBase::*handler)(BC_WindowBase *))
763 {
764         BC_KeyboardHandlerLock set;
765         BC_KeyboardHandler::listeners.append(new BC_KeyboardHandler(handler, this));
766 }
767
768 void BC_WindowBase::del_keyboard_listener(int(BC_WindowBase::*handler)(BC_WindowBase *))
769 {
770         BC_KeyboardHandlerLock set;
771         int i = BC_KeyboardHandler::listeners.size();
772         while( --i >= 0 && BC_KeyboardHandler::listeners[i]->handler!=handler );
773         if( i >= 0 ) BC_KeyboardHandler::listeners.remove_object_number(i);
774 }
775
776 int BC_KeyboardHandler::run_event(BC_WindowBase *wp)
777 {
778         int result = (win->*handler)(wp);
779         return result;
780 }
781
782 int BC_KeyboardHandler::run_listeners(BC_WindowBase *wp)
783 {
784         int result = 0;
785         BC_KeyboardHandlerLock set;
786         for( int i=0; !result && i<listeners.size(); ++i ) {
787                 BC_KeyboardHandler *listener = listeners[i];
788                 result = listener->run_event(wp);
789         }
790         return result;
791 }
792
793 void BC_KeyboardHandler::kill_grabs()
794 {
795         BC_KeyboardHandlerLock set;
796         for( int i=0; i<listeners.size(); ++i ) {
797                 BC_WindowBase *win = listeners[i]->win;
798                 if( win->get_window_type() != POPUP_WINDOW ) continue;
799                 ((BC_Popup *)win)->ungrab_keyboard();
800         }
801 }
802
803 void BC_ActiveBitmaps::reque(XEvent *event)
804 {
805         XShmCompletionEvent *shm_ev = (XShmCompletionEvent *)event;
806         ShmSeg shmseg = shm_ev->shmseg;
807         Drawable drawable = shm_ev->drawable;
808 //printf("BC_BitmapImage::reque %08lx\n",shmseg);
809         active_lock.lock("BC_BitmapImage::reque");
810         BC_BitmapImage *bfr = first;
811         while( bfr && bfr->get_shmseg() != shmseg ) bfr = bfr->next;
812         if( bfr && bfr->drawable == drawable )
813                 remove_pointer(bfr);
814         active_lock.unlock();
815         if( !bfr ) {
816 // sadly, X reports two drawable completions and creates false reporting, so no boobytrap
817 //              printf("BC_BitmapImage::reque missed shmseg %08x, drawable %08x\n",
818 //                       (int)shmseg, (int)drawable);
819                 return;
820         }
821         if( bfr->drawable != drawable ) return;
822         if( bfr->is_zombie() ) { --BC_Bitmap::zombies; delete bfr; return; }
823         bfr->bitmap->reque(bfr);
824 }
825
826 void BC_ActiveBitmaps::insert(BC_BitmapImage *bfr, Drawable pixmap)
827 {
828         active_lock.lock("BC_BitmapImage::insert");
829         bfr->drawable = pixmap;
830         append(bfr);
831         active_lock.unlock();
832 }
833
834 void BC_ActiveBitmaps::remove_buffers(BC_WindowBase *wdw)
835 {
836         active_lock.lock("BC_ActiveBitmaps::remove");
837         for( BC_BitmapImage *nxt=0, *bfr=first; bfr; bfr=nxt ) {
838                 nxt = bfr->next;
839                 if( bfr->is_zombie() ) { --BC_Bitmap::zombies; delete bfr; continue; }
840                 if( bfr->bitmap->parent_window == wdw ) remove_pointer(bfr);
841         }
842         active_lock.unlock();
843 }
844
845 BC_ActiveBitmaps::BC_ActiveBitmaps()
846 {
847 }
848
849 BC_ActiveBitmaps::~BC_ActiveBitmaps()
850 {
851 }
852
853
854
855 int BC_WindowBase::keysym_lookup(XEvent *event)
856 {
857         for( int i = 0; i < KEYPRESSLEN; ++i ) keys_return[i] = 0;
858         for( int i = 0; i < 4; ++i ) wkey_string[i] = 0;
859
860         if( event->xany.send_event && !event->xany.serial ) {
861                 keysym = (KeySym) event->xkey.keycode;
862                 keys_return[0] = keysym;
863                 return 0;
864         }
865         wkey_string_length = 0;
866
867         if( input_context ) {
868                 wkey_string_length = XwcLookupString(input_context,
869                         (XKeyEvent*)event, wkey_string, 4, &keysym, 0);
870 //printf("keysym_lookup 1 %d %d %lx %x %x %x %x\n", wkey_string_length, keysym,
871 //  wkey_string[0], wkey_string[1], wkey_string[2], wkey_string[3]);
872
873                 Status stat;
874                 int ret = Xutf8LookupString(input_context, (XKeyEvent*)event,
875                                 keys_return, KEYPRESSLEN, &keysym, &stat);
876 //printf("keysym_lookup 2 %d %d %lx %x %x\n", ret, stat, keysym, keys_return[0], keys_return[1]);
877                 if( stat == XLookupBoth ) return ret;
878                 if( stat == XLookupKeySym ) return 0;
879         }
880         int ret = XLookupString((XKeyEvent*)event, keys_return, KEYPRESSLEN, &keysym, 0);
881         wkey_string_length = ret;
882         for( int i=0; i<ret; ++i ) wkey_string[i] = keys_return[i];
883         return ret;
884 }
885
886 pthread_t locking_task = (pthread_t)-1L;
887 int locking_event = -1;
888 int locking_message = -1;
889
890 int BC_WindowBase::dispatch_event(XEvent *event)
891 {
892         Window tempwin;
893         int result;
894         XClientMessageEvent *ptr;
895         int cancel_resize, cancel_translation;
896         const int debug = 0;
897
898         key_pressed = 0;
899
900 #ifndef SINGLE_THREAD
901 // If an event is waiting get it, otherwise
902 // wait for next event only if there are no compressed events.
903         if(get_event_count() ||
904                 (!motion_events && !resize_events && !translation_events))
905         {
906                 event = get_event();
907 // Lock out window deletions
908                 lock_window("BC_WindowBase::dispatch_event 1");
909 locking_event = event->type;
910 locking_task = pthread_self();
911 locking_message = event->xclient.message_type;
912         }
913         else
914 // Handle compressed events
915         {
916                 lock_window("BC_WindowBase::dispatch_event 2");
917                 if(resize_events)
918                         dispatch_resize_event(last_resize_w, last_resize_h);
919                 if(motion_events)
920                         dispatch_motion_event();
921                 if(translation_events)
922                         dispatch_translation_event();
923
924                 unlock_window();
925                 return 0;
926         }
927
928 #endif
929
930
931
932
933 //static const char *event_names[] = {
934 //  "Reply", "Error", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease", "MotionNotify",
935 //  "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut", "KeymapNotify", "Expose", "GraphicsExpose",
936 //  "NoExpose", "VisibilityNotify", "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify",
937 //  "MapRequest", "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
938 //  "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify", "SelectionClear",
939 //  "SelectionRequest", "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify",
940 //  "GenericEvent", "LASTEvent",
941 //};
942 //
943 //if(debug)
944 //if(event->type != ClientMessage) {
945 // printf("BC_WindowBase::dispatch_event %d %s %p %d (%s)\n",
946 //__LINE__, title, event, event->type,
947 //  event->type>=0 && event->type<sizeof(event_names)/sizeof(event_names[0]) ?
948 //   event_names[event->type] : "Unknown");
949 //}
950
951         if( active_grab ) {
952                 unlock_window();
953                 active_grab->lock_window("BC_WindowBase::dispatch_event 3");
954                 result = active_grab->grab_event(event);
955                 active_grab->unlock_window();
956                 if( result ) return result;
957                 lock_window("BC_WindowBase::dispatch_event 4");
958         }
959
960         switch(event->type) {
961         case ClientMessage:
962 // Clear the resize buffer
963                 if(resize_events) dispatch_resize_event(last_resize_w, last_resize_h);
964 // Clear the motion buffer since this can clear the window
965                 if(motion_events)
966                 {
967                         dispatch_motion_event();
968                 }
969
970                 ptr = (XClientMessageEvent*)event;
971
972
973                 if(ptr->message_type == ProtoXAtom &&
974                         (Atom)ptr->data.l[0] == DelWinXAtom)
975                 {
976                         close_event();
977                 }
978                 else
979                 if(ptr->message_type == RepeaterXAtom)
980                 {
981                         dispatch_repeat_event(ptr->data.l[0]);
982 // Make sure the repeater still exists.
983 //                              for(int i = 0; i < repeaters.total; i++)
984 //                              {
985 //                                      if(repeaters.values[i]->repeat_id == ptr->data.l[0])
986 //                                      {
987 //                                              dispatch_repeat_event_master(ptr->data.l[0]);
988 //                                              break;
989 //                                      }
990 //                              }
991                 }
992                 else
993                 if(ptr->message_type == SetDoneXAtom)
994                 {
995                         done = 1;
996                         } else
997                         { // We currently use X marshalling for xatom events, we can switch to something else later
998                                 receive_custom_xatoms((xatom_event *)ptr);
999                 }
1000                 break;
1001
1002         case FocusIn:
1003                 has_focus = 1;
1004                 dispatch_focus_in();
1005                 break;
1006
1007         case FocusOut:
1008                 has_focus = 0;
1009                 dispatch_focus_out();
1010                 break;
1011
1012 // Maximized
1013         case MapNotify:
1014                 break;
1015
1016 // Minimized
1017         case UnmapNotify:
1018                 break;
1019
1020         case ButtonPress:
1021                 if(motion_events)
1022                 {
1023                         dispatch_motion_event();
1024                 }
1025                 get_key_masks(event->xbutton.state);
1026                 cursor_x = event->xbutton.x;
1027                 cursor_y = event->xbutton.y;
1028                 button_number = event->xbutton.button;
1029
1030 //printf("BC_WindowBase::dispatch_event %d %d\n", __LINE__, button_number);
1031                 event_win = event->xany.window;
1032                 if (button_number < 6) {
1033                         if(button_number < 4)
1034                                 button_down = 1;
1035                         button_pressed = event->xbutton.button;
1036                         button_time1 = button_time2;
1037                         button_time2 = button_time3;
1038                         button_time3 = event->xbutton.time;
1039                         drag_x = cursor_x;
1040                         drag_y = cursor_y;
1041                         drag_win = event_win;
1042                         drag_x1 = cursor_x - get_resources()->drag_radius;
1043                         drag_x2 = cursor_x + get_resources()->drag_radius;
1044                         drag_y1 = cursor_y - get_resources()->drag_radius;
1045                         drag_y2 = cursor_y + get_resources()->drag_radius;
1046
1047                         if((long)(button_time3 - button_time1) < resources.double_click * 2)
1048                         {
1049                                 triple_click = 1;
1050                                 button_time3 = button_time2 = button_time1 = 0;
1051                         }
1052                         if((long)(button_time3 - button_time2) < resources.double_click)
1053                         {
1054                                 double_click = 1;
1055 //                              button_time3 = button_time2 = button_time1 = 0;
1056                         }
1057                         else
1058                         {
1059                                 triple_click = 0;
1060                                 double_click = 0;
1061                         }
1062
1063                         dispatch_button_press();
1064                 }
1065                 break;
1066
1067         case ButtonRelease:
1068                 if(motion_events)
1069                 {
1070                         dispatch_motion_event();
1071                 }
1072                 get_key_masks(event->xbutton.state);
1073                 button_number = event->xbutton.button;
1074                 event_win = event->xany.window;
1075                 if (button_number < 6)
1076                 {
1077                         if(button_number < 4)
1078                                 button_down = 0;
1079 //printf("BC_WindowBase::dispatch_event %d %d\n", __LINE__, button_number);
1080
1081                         dispatch_button_release();
1082                 }
1083                 break;
1084
1085         case Expose:
1086                 event_win = event->xany.window;
1087                 dispatch_expose_event();
1088                 break;
1089
1090         case MotionNotify:
1091                 get_key_masks(event->xmotion.state);
1092 // Dispatch previous motion event if this is a subsequent motion from a different window
1093                 if(motion_events && last_motion_win != event->xany.window)
1094                 {
1095                         dispatch_motion_event();
1096                 }
1097
1098 // Buffer the current motion
1099                 motion_events = 1;
1100                 last_motion_state = event->xmotion.state;
1101                 last_motion_x = event->xmotion.x;
1102                 last_motion_y = event->xmotion.y;
1103                 last_motion_win = event->xany.window;
1104                 break;
1105
1106         case ConfigureNotify:
1107 // printf("BC_WindowBase::dispatch_event %d win=%p this->win=%p\n",
1108 // __LINE__,
1109 // event->xany.window,
1110 // win);
1111 // dump_windows();
1112                 XTranslateCoordinates(top_level->display,
1113                         top_level->win,
1114                         top_level->rootwin,
1115                         0,
1116                         0,
1117                         &last_translate_x,
1118                         &last_translate_y,
1119                         &tempwin);
1120                 last_resize_w = event->xconfigure.width;
1121                 last_resize_h = event->xconfigure.height;
1122
1123                 cancel_resize = 0;
1124                 cancel_translation = 0;
1125
1126 // Resize history prevents responses to recursive resize requests
1127                 for(int i = 0; i < resize_history.total && !cancel_resize; i++)
1128                 {
1129                         if(resize_history.values[i]->w == last_resize_w &&
1130                                 resize_history.values[i]->h == last_resize_h)
1131                         {
1132                                 delete resize_history.values[i];
1133                                 resize_history.remove_number(i);
1134                                 cancel_resize = 1;
1135                         }
1136                 }
1137
1138                 if(last_resize_w == w && last_resize_h == h)
1139                         cancel_resize = 1;
1140
1141                 if(!cancel_resize)
1142                 {
1143                         resize_events = 1;
1144                 }
1145
1146                 if((last_translate_x == x && last_translate_y == y))
1147                         cancel_translation = 1;
1148
1149                 if(!cancel_translation)
1150                 {
1151                         translation_events = 1;
1152                 }
1153
1154                 translation_count++;
1155                 break;
1156
1157         case KeyPress:
1158                 get_key_masks(event->xkey.state);
1159                 keys_return[0] = 0;  keysym = -1;
1160                 if(XFilterEvent(event, win)) {
1161                         break;
1162                 }
1163                 if( keysym_lookup(event) < 0 ) {
1164                         printf("keysym %x\n", (uint32_t)keysym);
1165                         break;
1166                 }
1167
1168 //printf("BC_WindowBase::dispatch_event %d keysym=0x%x\n",
1169 //__LINE__,
1170 //keysym);
1171
1172 // block out control keys
1173                 if(keysym > 0xffe0 && keysym < 0xffff) break;
1174 // block out Alt_GR key
1175                 if(keysym == 0xfe03) break;
1176
1177                 if(test_keypress)
1178                          printf("BC_WindowBase::dispatch_event %x\n", (uint32_t)keysym);
1179
1180 #ifdef X_HAVE_UTF8_STRING
1181 //It's Ascii or UTF8?
1182 //              if (keysym != 0xffff && (keys_return[0] & 0xff) >= 0x7f )
1183 //printf("BC_WindowBase::dispatch_event %d %02x%02x\n", __LINE__, keys_return[0], keys_return[1]);
1184
1185                 if( ((keys_return[1] & 0xff) > 0x80) &&
1186                     ((keys_return[0] & 0xff) > 0xC0) ) {
1187 //printf("BC_WindowBase::dispatch_event %d\n", __LINE__);
1188                         key_pressed = keysym & 0xff;
1189                 }
1190                 else {
1191 #endif
1192 // shuttle speed codes
1193                 if( keysym >= SKEY_MIN && keysym <= SKEY_MAX ) {
1194                         key_pressed = keysym;
1195                 }
1196                 else switch( keysym ) {
1197 // block out extra keys
1198                 case XK_Alt_L:
1199                 case XK_Alt_R:
1200                 case XK_Shift_L:
1201                 case XK_Shift_R:
1202                 case XK_Control_L:
1203                 case XK_Control_R:
1204                         key_pressed = 0;
1205                         break;
1206
1207 // Translate key codes
1208                 case XK_Return:         key_pressed = RETURN;   break;
1209                 case XK_Up:             key_pressed = UP;       break;
1210                 case XK_Down:           key_pressed = DOWN;     break;
1211                 case XK_Left:           key_pressed = LEFT;     break;
1212                 case XK_Right:          key_pressed = RIGHT;    break;
1213                 case XK_Next:           key_pressed = PGDN;     break;
1214                 case XK_Prior:          key_pressed = PGUP;     break;
1215                 case XK_BackSpace:      key_pressed = BACKSPACE; break;
1216                 case XK_Escape:         key_pressed = ESC;      break;
1217                 case XK_Tab:
1218                         if(shift_down())
1219                                 key_pressed = LEFTTAB;
1220                         else
1221                                 key_pressed = TAB;
1222                         break;
1223                 case XK_ISO_Left_Tab:   key_pressed = LEFTTAB;  break;
1224                 case XK_underscore:     key_pressed = '_';      break;
1225                 case XK_asciitilde:     key_pressed = '~';      break;
1226                 case XK_Delete:         key_pressed = DELETE;   break;
1227                 case XK_Home:           key_pressed = HOME;     break;
1228                 case XK_End:            key_pressed = END;      break;
1229
1230 // number pad
1231                 case XK_KP_Enter:       key_pressed = KPENTER;  break;
1232                 case XK_KP_Add:         key_pressed = KPPLUS;   break;
1233                 case XK_KP_Subtract:    key_pressed = KPMINUS;  break;
1234                 case XK_KP_Multiply:    key_pressed = KPSTAR;   break;
1235                 case XK_KP_Divide:      key_pressed = KPSLASH;  break;
1236                 case XK_KP_1:
1237                 case XK_KP_End:         key_pressed = KP1;      break;
1238                 case XK_KP_2:
1239                 case XK_KP_Down:        key_pressed = KP2;      break;
1240                 case XK_KP_3:
1241                 case XK_KP_Page_Down:   key_pressed = KP3;      break;
1242                 case XK_KP_4:
1243                 case XK_KP_Left:        key_pressed = KP4;      break;
1244                 case XK_KP_5:
1245                 case XK_KP_Begin:       key_pressed = KP5;      break;
1246                 case XK_KP_6:
1247                 case XK_KP_Right:       key_pressed = KP6;      break;
1248                 case XK_KP_7:
1249                 case XK_KP_Home:        key_pressed = KP7;      break;
1250                 case XK_KP_8:
1251                 case XK_KP_Up:          key_pressed = KP8;      break;
1252                 case XK_KP_9:
1253                 case XK_KP_Page_Up:     key_pressed = KP9;      break;
1254                 case XK_KP_0:
1255                 case XK_KP_Insert:      key_pressed = KPINS;    break;
1256                 case XK_KP_Decimal:
1257                 case XK_KP_Delete:      key_pressed = KPDEL;    break;
1258
1259                 case XK_F1:             key_pressed = KEY_F1;   break;
1260                 case XK_F2:             key_pressed = KEY_F2;   break;
1261                 case XK_F3:             key_pressed = KEY_F3;   break;
1262                 case XK_F4:             key_pressed = KEY_F4;   break;
1263                 case XK_F5:             key_pressed = KEY_F5;   break;
1264                 case XK_F6:             key_pressed = KEY_F6;   break;
1265                 case XK_F7:             key_pressed = KEY_F7;   break;
1266                 case XK_F8:             key_pressed = KEY_F8;   break;
1267                 case XK_F9:             key_pressed = KEY_F9;   break;
1268                 case XK_F10:            key_pressed = KEY_F10;  break;
1269                 case XK_F11:            key_pressed = KEY_F11;  break;
1270                 case XK_F12:            key_pressed = KEY_F12;  break;
1271
1272                 case XK_Menu:           key_pressed = KPMENU;   break;  /* menu */
1273 // remote control
1274 // above        case XK_KP_Enter:       key_pressed = KPENTER;  break;  /* check */
1275                 case XF86XK_MenuKB:     key_pressed = KPMENU;   break;  /* menu */
1276 // intercepted  case XF86XK_PowerDown: key_pressed = KPPOWER;   break;  /* Power */
1277                 case XF86XK_Launch1:    key_pressed = KPTV;     break;  /* TV */
1278                 case XF86XK_Launch2:    key_pressed = KPDVD;    break;  /* DVD */
1279 // intercepted  case XF86XK_WWW:        key_pressed = KPWWEB;   break;  /* WEB */
1280                 case XF86XK_Launch3:    key_pressed = KPBOOK;   break;  /* book */
1281                 case XF86XK_Launch4:    key_pressed = KPHAND;   break;  /* hand */
1282                 case XF86XK_Reply:      key_pressed = KPTMR;    break;  /* timer */
1283                 case SunXK_Front:       key_pressed = KPMAXW;   break;  /* max */
1284 // above        case XK_Left:           key_pressed = LEFT;     break;  /* left */
1285 // above        case XK_Right:          key_pressed = RIGHT;    break;  /* right */
1286 // above        case XK_Down:           key_pressed = DOWN;     break;  /* down */
1287 // above        case XK_Up:             key_pressed = UP;       break;  /* up */
1288 // above        case XK_SPACE:          key_pressed = KPSPACE;  break;  /* ok */
1289 // intercepted  case XF86XK_AudioRaiseVolume: key_pressed = KPVOLU;     break;  /* VOL + */
1290 // intercepted  case XF86XK_AudioMute: key_pressed = KPMUTE;    break;  /* MUTE */
1291 // intercepted  case XF86XK_AudioLowerVolume: key_pressed = KPVOLD;     break;  /* VOL - */
1292                 case XF86XK_ScrollUp:   key_pressed = KPCHUP;   break;  /* CH + */
1293                 case XF86XK_ScrollDown: key_pressed = KPCHDN;   break;  /* CH - */
1294                 case XF86XK_AudioRecord: key_pressed = KPRECD;  break;  /* ( o) red */
1295                 case XF86XK_Forward:    key_pressed = KPPLAY;   break;  /* ( >) */
1296                 case XK_Redo:           key_pressed = KPFWRD;   break;  /* (>>) */
1297                 case XF86XK_Back:       key_pressed = KPBACK;   break;  /* (<<) */
1298                 case XK_Cancel:         key_pressed = KPSTOP;   break;  /* ([]) */
1299                 case XK_Pause:          key_pressed = KPAUSE;   break;  /* ('') */
1300
1301                 default:
1302                         key_pressed = keysym & 0xff;
1303 #ifdef X_HAVE_UTF8_STRING
1304 //printf("BC_WindowBase::dispatch_event %d\n", __LINE__);
1305                         keys_return[1] = 0;
1306 #endif
1307                         break;
1308                 }
1309 #ifdef X_HAVE_UTF8_STRING
1310                 }
1311                 key_pressed_utf8 = keys_return;
1312 #endif
1313
1314
1315                 result = 0;
1316                 if( top_level == this )
1317                         result = BC_KeyboardHandler::run_listeners(this);
1318
1319 //printf("BC_WindowBase::dispatch_event %d %d %x\n", shift_down(), alt_down(), key_pressed);
1320                 if( !result )
1321                         result = dispatch_keypress_event();
1322 // Handle some default keypresses
1323                 if(!result)
1324                 {
1325                         if(key_pressed == 'w' ||
1326                                 key_pressed == 'W')
1327                         {
1328                                 close_event();
1329                         }
1330                 }
1331                 break;
1332
1333         case KeyRelease:
1334                 XLookupString((XKeyEvent*)event, keys_return, 1, &keysym, 0);
1335                 dispatch_keyrelease_event();
1336 // printf("BC_WindowBase::dispatch_event KeyRelease keysym=0x%x keystate=0x%lld\n",
1337 // keysym, event->xkey.state);
1338                 break;
1339
1340         case LeaveNotify:
1341                 if( event->xcrossing.mode != NotifyNormal ) break;
1342                 cursor_entered = 0;
1343                 event_win = event->xany.window;
1344                 dispatch_cursor_leave();
1345                 break;
1346
1347         case EnterNotify:
1348                 if( event->xcrossing.mode != NotifyNormal ) break;
1349
1350                 if( !cursor_entered ) {
1351                         for( int i=0; i<popups.size(); ++i ) {  // popups always take focus
1352                                 if( popups[i]->win == event->xcrossing.window )
1353                                 cursor_entered = 1;
1354                         }
1355                         if( !cursor_entered && get_resources()->grab_input_focus &&
1356                             !event->xcrossing.focus && event->xcrossing.window == win ) {
1357                                 cursor_entered = 1;
1358                         }
1359                         if( cursor_entered )
1360                                 focus();
1361                 }
1362                 event_win = event->xany.window;
1363                 cursor_x = event->xcrossing.x;
1364                 cursor_y = event->xcrossing.y;
1365                 dispatch_cursor_enter();
1366                 break;
1367
1368         default:
1369                 break;
1370         }
1371 //printf("100 %s %p %d\n", title, event, event->type);
1372 //if(event->type != ClientMessage) dump();
1373
1374 #ifndef SINGLE_THREAD
1375         unlock_window();
1376         if(event) {
1377                 if( resend_event_window ) {
1378                         resend_event_window->put_event(event);
1379                         resend_event_window = 0;
1380                 }
1381                 else
1382                         delete event;
1383         }
1384 #else
1385 //      if(done) completion_lock->unlock();
1386 #endif
1387
1388 if(debug) printf("BC_WindowBase::dispatch_event this=%p %d\n", this, __LINE__);
1389         return 0;
1390 }
1391
1392 int BC_WindowBase::dispatch_expose_event()
1393 {
1394         int result = 0;
1395         for(int i = 0; i < subwindows->total && !result; i++)
1396         {
1397                 result = subwindows->values[i]->dispatch_expose_event();
1398         }
1399
1400 // Propagate to user
1401         if(!result) expose_event();
1402         return result;
1403 }
1404
1405 int BC_WindowBase::dispatch_resize_event(int w, int h)
1406 {
1407 // Can't store new w and h until the event is handles
1408 // because bcfilebox depends on the old w and h to
1409 // reposition widgets.
1410         if( window_type == MAIN_WINDOW ) {
1411                 flash_enabled = 0;
1412                 resize_events = 0;
1413
1414                 delete pixmap;
1415                 pixmap = new BC_Pixmap(this, w, h);
1416                 clear_box(0, 0, w, h);
1417         }
1418
1419 // Propagate to subwindows
1420         for(int i = 0; i < subwindows->total; i++) {
1421                 subwindows->values[i]->dispatch_resize_event(w, h);
1422         }
1423
1424 // Propagate to user
1425         resize_event(w, h);
1426
1427         if( window_type == MAIN_WINDOW ) {
1428                 this->w = w;
1429                 this->h = h;
1430                 dispatch_flash();
1431                 flush();
1432         }
1433         return 0;
1434 }
1435
1436 int BC_WindowBase::dispatch_flash()
1437 {
1438         flash_enabled = 1;
1439         for(int i = 0; i < subwindows->total; i++)
1440                 subwindows->values[i]->dispatch_flash();
1441         return flash(0);
1442 }
1443
1444 int BC_WindowBase::dispatch_translation_event()
1445 {
1446         translation_events = 0;
1447         if(window_type == MAIN_WINDOW)
1448         {
1449                 prev_x = x;
1450                 prev_y = y;
1451                 x = last_translate_x;
1452                 y = last_translate_y;
1453 // Correct for window manager offsets
1454                 x -= x_correction;
1455                 y -= y_correction;
1456         }
1457
1458         for(int i = 0; i < subwindows->total; i++)
1459         {
1460                 subwindows->values[i]->dispatch_translation_event();
1461         }
1462
1463         translation_event();
1464         return 0;
1465 }
1466
1467 int BC_WindowBase::dispatch_motion_event()
1468 {
1469         int result = 0;
1470         unhide_cursor();
1471
1472         if(top_level == this)
1473         {
1474                 motion_events = 0;
1475                 event_win = last_motion_win;
1476                 get_key_masks(last_motion_state);
1477
1478 // Test for grab
1479                 if(get_button_down() && !active_menubar && !active_popup_menu)
1480                 {
1481                         if(!result)
1482                         {
1483                                 cursor_x = last_motion_x;
1484                                 cursor_y = last_motion_y;
1485                                 result = dispatch_drag_motion();
1486                         }
1487
1488                         if(!result &&
1489                                 (last_motion_x < drag_x1 || last_motion_x >= drag_x2 ||
1490                                 last_motion_y < drag_y1 || last_motion_y >= drag_y2))
1491                         {
1492                                 cursor_x = drag_x;
1493                                 cursor_y = drag_y;
1494
1495                                 result = dispatch_drag_start();
1496                         }
1497                 }
1498
1499                 cursor_x = last_motion_x;
1500                 cursor_y = last_motion_y;
1501
1502 // printf("BC_WindowBase::dispatch_motion_event %d %p %p %p\n",
1503 // __LINE__,
1504 // active_menubar,
1505 // active_popup_menu,
1506 // active_subwindow);
1507
1508                 if(active_menubar && !result) result = active_menubar->dispatch_motion_event();
1509                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_motion_event();
1510                 if(active_subwindow && !result) result = active_subwindow->dispatch_motion_event();
1511         }
1512
1513 // Dispatch in stacking order
1514         for(int i = subwindows->size() - 1; i >= 0 && !result; i--)
1515         {
1516                 result = subwindows->values[i]->dispatch_motion_event();
1517         }
1518
1519         if(!result) result = cursor_motion_event();    // give to user
1520         return result;
1521 }
1522
1523 int BC_WindowBase::dispatch_keypress_event()
1524 {
1525         int result = 0;
1526         if(top_level == this)
1527         {
1528                 if(active_subwindow) result = active_subwindow->dispatch_keypress_event();
1529         }
1530
1531         for(int i = 0; i < subwindows->total && !result; i++)
1532         {
1533                 result = subwindows->values[i]->dispatch_keypress_event();
1534         }
1535
1536         if(!result) result = keypress_event();
1537
1538         return result;
1539 }
1540
1541 int BC_WindowBase::dispatch_keyrelease_event()
1542 {
1543         int result = 0;
1544         if(top_level == this)
1545         {
1546                 if(active_subwindow) result = active_subwindow->dispatch_keyrelease_event();
1547         }
1548
1549         for(int i = 0; i < subwindows->total && !result; i++)
1550         {
1551                 result = subwindows->values[i]->dispatch_keyrelease_event();
1552         }
1553
1554         if(!result) result = keyrelease_event();
1555
1556         return result;
1557 }
1558
1559 int BC_WindowBase::dispatch_focus_in()
1560 {
1561         for(int i = 0; i < subwindows->total; i++)
1562         {
1563                 subwindows->values[i]->dispatch_focus_in();
1564         }
1565
1566         focus_in_event();
1567
1568         return 0;
1569 }
1570
1571 int BC_WindowBase::dispatch_focus_out()
1572 {
1573         for(int i = 0; i < subwindows->total; i++)
1574         {
1575                 subwindows->values[i]->dispatch_focus_out();
1576         }
1577
1578         focus_out_event();
1579
1580         return 0;
1581 }
1582
1583 int BC_WindowBase::get_has_focus()
1584 {
1585         return top_level->has_focus;
1586 }
1587
1588 int BC_WindowBase::get_deleting()
1589 {
1590         if(is_deleting) return 1;
1591         if(parent_window && parent_window->get_deleting()) return 1;
1592         return 0;
1593 }
1594
1595 int BC_WindowBase::dispatch_button_press()
1596 {
1597         int result = 0;
1598
1599
1600         if(top_level == this)
1601         {
1602                 if(active_menubar) result = active_menubar->dispatch_button_press();
1603                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_button_press();
1604                 if(active_subwindow && !result) result = active_subwindow->dispatch_button_press();
1605         }
1606
1607         for(int i = 0; i < subwindows->total && !result; i++)
1608         {
1609                 result = subwindows->values[i]->dispatch_button_press();
1610         }
1611
1612         if(!result) result = button_press_event();
1613
1614
1615         return result;
1616 }
1617
1618 int BC_WindowBase::dispatch_button_release()
1619 {
1620         int result = 0;
1621         if(top_level == this)
1622         {
1623                 if(active_menubar) result = active_menubar->dispatch_button_release();
1624                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_button_release();
1625                 if(active_subwindow && !result) result = active_subwindow->dispatch_button_release();
1626                 if(!result && button_number != 4 && button_number != 5)
1627                         result = dispatch_drag_stop();
1628         }
1629
1630         for(int i = 0; i < subwindows->total && !result; i++)
1631         {
1632                 result = subwindows->values[i]->dispatch_button_release();
1633         }
1634
1635         if(!result)
1636         {
1637                 result = button_release_event();
1638         }
1639
1640         return result;
1641 }
1642
1643
1644 int BC_WindowBase::dispatch_repeat_event(int64_t duration)
1645 {
1646
1647 // all repeat event handlers get called and decide based on activity and duration
1648 // whether to respond
1649         for(int i = 0; i < subwindows->total; i++)
1650         {
1651                 subwindows->values[i]->dispatch_repeat_event(duration);
1652         }
1653
1654
1655         repeat_event(duration);
1656
1657
1658
1659 // Unlock next repeat signal
1660         if(window_type == MAIN_WINDOW)
1661         {
1662 #ifdef SINGLE_THREAD
1663                 BC_Display::display_global->unlock_repeaters(duration);
1664 #else
1665                 for(int i = 0; i < repeaters.total; i++)
1666                 {
1667                         if(repeaters.values[i]->delay == duration)
1668                         {
1669                                 repeaters.values[i]->repeat_lock->unlock();
1670                         }
1671                 }
1672 #endif
1673         }
1674         return 0;
1675 }
1676
1677 void BC_WindowBase::unhide_cursor()
1678 {
1679         if(is_transparent)
1680         {
1681                 is_transparent = 0;
1682                 if(top_level->is_hourglass)
1683                         set_cursor(HOURGLASS_CURSOR, 1, 0);
1684                 else
1685                         set_cursor(current_cursor, 1, 0);
1686         }
1687         cursor_timer->update();
1688 }
1689
1690
1691 void BC_WindowBase::update_video_cursor()
1692 {
1693         if(video_on && !is_transparent)
1694         {
1695                 if(cursor_timer->get_difference() > VIDEO_CURSOR_TIMEOUT && !is_transparent)
1696                 {
1697                         is_transparent = 1;
1698                         set_cursor(TRANSPARENT_CURSOR, 1, 1);
1699                         cursor_timer->update();
1700                 }
1701         }
1702         else
1703         {
1704                 cursor_timer->update();
1705         }
1706 }
1707
1708
1709 int BC_WindowBase::dispatch_cursor_leave()
1710 {
1711         unhide_cursor();
1712
1713         for(int i = 0; i < subwindows->total; i++)
1714         {
1715                 subwindows->values[i]->dispatch_cursor_leave();
1716         }
1717
1718         cursor_leave_event();
1719         return 0;
1720 }
1721
1722 int BC_WindowBase::dispatch_cursor_enter()
1723 {
1724         int result = 0;
1725
1726         unhide_cursor();
1727
1728         if(active_menubar) result = active_menubar->dispatch_cursor_enter();
1729         if(!result && active_popup_menu) result = active_popup_menu->dispatch_cursor_enter();
1730         if(!result && active_subwindow) result = active_subwindow->dispatch_cursor_enter();
1731
1732         for(int i = 0; !result && i < subwindows->total; i++)
1733         {
1734                 result = subwindows->values[i]->dispatch_cursor_enter();
1735         }
1736
1737         if(!result) result = cursor_enter_event();
1738         return result;
1739 }
1740
1741 int BC_WindowBase::cursor_enter_event()
1742 {
1743         return 0;
1744 }
1745
1746 int BC_WindowBase::cursor_leave_event()
1747 {
1748         return 0;
1749 }
1750
1751 int BC_WindowBase::close_event()
1752 {
1753         set_done(1);
1754         return 1;
1755 }
1756
1757 int BC_WindowBase::dispatch_drag_start()
1758 {
1759         int result = 0;
1760         if(active_menubar) result = active_menubar->dispatch_drag_start();
1761         if(!result && active_popup_menu) result = active_popup_menu->dispatch_drag_start();
1762         if(!result && active_subwindow) result = active_subwindow->dispatch_drag_start();
1763
1764         for(int i = 0; i < subwindows->total && !result; i++)
1765         {
1766                 result = subwindows->values[i]->dispatch_drag_start();
1767         }
1768
1769         if(!result) result = is_dragging = drag_start_event();
1770         return result;
1771 }
1772
1773 int BC_WindowBase::dispatch_drag_stop()
1774 {
1775         int result = 0;
1776
1777         for(int i = 0; i < subwindows->total && !result; i++)
1778         {
1779                 result = subwindows->values[i]->dispatch_drag_stop();
1780         }
1781
1782         if(is_dragging && !result)
1783         {
1784                 drag_stop_event();
1785                 is_dragging = 0;
1786                 result = 1;
1787         }
1788
1789         return result;
1790 }
1791
1792 int BC_WindowBase::dispatch_drag_motion()
1793 {
1794         int result = 0;
1795         for(int i = 0; i < subwindows->total && !result; i++)
1796         {
1797                 result = subwindows->values[i]->dispatch_drag_motion();
1798         }
1799
1800         if(is_dragging && !result)
1801         {
1802                 drag_motion_event();
1803                 result = 1;
1804         }
1805
1806         return result;
1807 }
1808
1809
1810 int BC_WindowBase::show_tooltip(const char *text, int x, int y, int w, int h)
1811 {
1812 // default text
1813         int forced = !text ? force_tooltip : 1;
1814         if( !text ) text = tooltip_text;
1815         if( !text || (!forced && !get_resources()->tooltips_enabled) ) {
1816                 top_level->hide_tooltip();
1817                 return 1;
1818         }
1819 // default w,h
1820         if(w < 0) w = get_text_width(MEDIUMFONT, text)  + TOOLTIP_MARGIN * 2;
1821         if(h < 0) h = get_text_height(MEDIUMFONT, text) + TOOLTIP_MARGIN * 2;
1822 // default x,y (win relative)
1823         if( x < 0 ) x = get_w();
1824         if( y < 0 ) y = get_h();
1825         int wx, wy;
1826         get_root_coordinates(x, y, &wx, &wy);
1827 // keep the tip inside the window/display
1828         int x0 = top_level->get_x(), x1 = x0 + top_level->get_w();
1829         int x2 = top_level->get_screen_x(0, -1) + top_level->get_screen_w(0, -1);
1830         if( x1 > x2 ) x1 = x2;
1831         if( wx < x0 ) wx = x0;
1832         if( wx >= (x1-=w) ) wx = x1;
1833         int y0 = top_level->get_y(), y1 = y0 + top_level->get_h();
1834         int y2 = top_level->get_root_h(0);
1835         if( y1 > y2 ) y1 = y2;
1836         if( wy < y0 ) wy = y0;
1837         if( wy >= (y1-=h) ) wy = y1;
1838 // avoid tip under cursor (flickers)
1839         int abs_x, abs_y;
1840         get_abs_cursor(abs_x,abs_y, 0);
1841         if( wx < abs_x && abs_x < wx+w && wy < abs_y && abs_y < wy+h ) {
1842                 if( wx-abs_x < wy-abs_y )
1843                         wx = abs_x+1;
1844                 else
1845                         wy = abs_y+1;
1846         }
1847         if( !tooltip_on ) {
1848                 tooltip_on = 1;
1849                 tooltip_popup = new BC_Popup(top_level, wx, wy, w, h,
1850                                 get_resources()->tooltip_bg_color);
1851         }
1852         else
1853                 tooltip_popup->reposition_window(wx, wy, w, h);
1854
1855         draw_tooltip(text);
1856         tooltip_popup->flash();
1857         tooltip_popup->flush();
1858         return 0;
1859 }
1860
1861 int BC_WindowBase::hide_tooltip()
1862 {
1863         if(subwindows)
1864                 for(int i = 0; i < subwindows->total; i++)
1865                 {
1866                         subwindows->values[i]->hide_tooltip();
1867                 }
1868
1869         if(tooltip_on)
1870         {
1871                 tooltip_on = 0;
1872                 delete tooltip_popup;
1873                 tooltip_popup = 0;
1874         }
1875         return 0;
1876 }
1877
1878 const char *BC_WindowBase::get_tooltip()
1879 {
1880         return tooltip_text;
1881 }
1882
1883 int BC_WindowBase::set_tooltip(const char *text)
1884 {
1885         tooltip_text = text;
1886
1887 // Update existing tooltip if it is visible
1888         if(tooltip_on)
1889         {
1890                 draw_tooltip();
1891                 tooltip_popup->flash();
1892         }
1893         return 0;
1894 }
1895 // signal the event handler to repeat
1896 int BC_WindowBase::set_repeat(int64_t duration)
1897 {
1898         if(duration <= 0)
1899         {
1900                 printf("BC_WindowBase::set_repeat duration=%jd\n", duration);
1901                 return 0;
1902         }
1903         if(window_type != MAIN_WINDOW) return top_level->set_repeat(duration);
1904
1905 #ifdef SINGLE_THREAD
1906         BC_Display::display_global->set_repeat(this, duration);
1907 #else
1908 // test repeater database for duplicates
1909         for(int i = 0; i < repeaters.total; i++)
1910         {
1911 // Already exists
1912                 if(repeaters.values[i]->delay == duration)
1913                 {
1914                         repeaters.values[i]->start_repeating(this);
1915                         return 0;
1916                 }
1917         }
1918
1919         BC_Repeater *repeater = new BC_Repeater(this, duration);
1920         repeater->initialize();
1921         repeaters.append(repeater);
1922         repeater->start_repeating();
1923 #endif
1924         return 0;
1925 }
1926
1927 int BC_WindowBase::unset_repeat(int64_t duration)
1928 {
1929         if(window_type != MAIN_WINDOW) return top_level->unset_repeat(duration);
1930
1931 #ifdef SINGLE_THREAD
1932         BC_Display::display_global->unset_repeat(this, duration);
1933 #else
1934         for(int i = 0; i < repeaters.total; i++)
1935         {
1936                 if(repeaters.values[i]->delay == duration)
1937                 {
1938                         repeaters.values[i]->stop_repeating();
1939                 }
1940         }
1941 #endif
1942         return 0;
1943 }
1944
1945
1946 int BC_WindowBase::unset_all_repeaters()
1947 {
1948 #ifdef SINGLE_THREAD
1949         BC_Display::display_global->unset_all_repeaters(this);
1950 #else
1951         for(int i = 0; i < repeaters.total; i++)
1952         {
1953                 repeaters.values[i]->stop_repeating();
1954         }
1955         repeaters.remove_all_objects();
1956 #endif
1957         return 0;
1958 }
1959
1960 // long BC_WindowBase::get_repeat_id()
1961 // {
1962 //      return top_level->next_repeat_id++;
1963 // }
1964
1965 XEvent *BC_WindowBase::new_xevent()
1966 {
1967         XEvent *event = new XEvent;
1968         memset(event, 0, sizeof(*event));
1969         return event;
1970 }
1971
1972 #ifndef SINGLE_THREAD
1973 int BC_WindowBase::arm_repeat(int64_t duration)
1974 {
1975         XEvent *event = new_xevent();
1976         XClientMessageEvent *ptr = (XClientMessageEvent*)event;
1977         ptr->type = ClientMessage;
1978         ptr->message_type = RepeaterXAtom;
1979         ptr->format = 32;
1980         ptr->data.l[0] = duration;
1981
1982 // Couldn't use XSendEvent since it locked up randomly.
1983         put_event(event);
1984         return 0;
1985 }
1986 #endif
1987
1988 int BC_WindowBase::receive_custom_xatoms(xatom_event *event)
1989 {
1990         return 0;
1991 }
1992
1993 int BC_WindowBase::send_custom_xatom(xatom_event *event)
1994 {
1995 #ifndef SINGLE_THREAD
1996         XEvent *myevent = new_xevent();
1997         XClientMessageEvent *ptr = (XClientMessageEvent*)myevent;
1998         ptr->type = ClientMessage;
1999         ptr->message_type = event->message_type;
2000         ptr->format = event->format;
2001         ptr->data.l[0] = event->data.l[0];
2002         ptr->data.l[1] = event->data.l[1];
2003         ptr->data.l[2] = event->data.l[2];
2004         ptr->data.l[3] = event->data.l[3];
2005         ptr->data.l[4] = event->data.l[4];
2006
2007         put_event(myevent);
2008 #endif
2009         return 0;
2010 }
2011
2012
2013
2014 Atom BC_WindowBase::create_xatom(const char *atom_name)
2015 {
2016         return XInternAtom(display, atom_name, False);
2017 }
2018
2019 int BC_WindowBase::get_atoms()
2020 {
2021         SetDoneXAtom =  XInternAtom(display, "BC_REPEAT_EVENT", False);
2022         RepeaterXAtom = XInternAtom(display, "BC_CLOSE_EVENT", False);
2023         DestroyAtom =   XInternAtom(display, "BC_DESTROY_WINDOW", False);
2024         DelWinXAtom =   XInternAtom(display, "WM_DELETE_WINDOW", False);
2025         if( (ProtoXAtom = XInternAtom(display, "WM_PROTOCOLS", False)) != 0 )
2026                 XChangeProperty(display, win, ProtoXAtom, XA_ATOM, 32,
2027                         PropModeReplace, (unsigned char *)&DelWinXAtom, True);
2028         return 0;
2029
2030 }
2031
2032
2033 void BC_WindowBase::init_cursors()
2034 {
2035         arrow_cursor = XCreateFontCursor(display, XC_top_left_arrow);
2036         cross_cursor = XCreateFontCursor(display, XC_crosshair);
2037         ibeam_cursor = XCreateFontCursor(display, XC_xterm);
2038         vseparate_cursor = XCreateFontCursor(display, XC_sb_v_double_arrow);
2039         hseparate_cursor = XCreateFontCursor(display, XC_sb_h_double_arrow);
2040         move_cursor = XCreateFontCursor(display, XC_fleur);
2041         left_cursor = XCreateFontCursor(display, XC_sb_left_arrow);
2042         right_cursor = XCreateFontCursor(display, XC_sb_right_arrow);
2043         upright_arrow_cursor = XCreateFontCursor(display, XC_arrow);
2044         upleft_resize_cursor = XCreateFontCursor(display, XC_top_left_corner);
2045         upright_resize_cursor = XCreateFontCursor(display, XC_top_right_corner);
2046         downleft_resize_cursor = XCreateFontCursor(display, XC_bottom_left_corner);
2047         downright_resize_cursor = XCreateFontCursor(display, XC_bottom_right_corner);
2048         hourglass_cursor = XCreateFontCursor(display, XC_watch);
2049         grabbed_cursor = create_grab_cursor();
2050
2051         static char cursor_data[] = { 0,0,0,0, 0,0,0,0 };
2052         Colormap colormap = DefaultColormap(display, screen);
2053         Pixmap pixmap_bottom = XCreateBitmapFromData(display,
2054                 rootwin, cursor_data, 8, 8);
2055         XColor black, dummy;
2056         XAllocNamedColor(display, colormap, "black", &black, &dummy);
2057         transparent_cursor = XCreatePixmapCursor(display,
2058                 pixmap_bottom, pixmap_bottom, &black, &black, 0, 0);
2059 //      XDefineCursor(display, win, transparent_cursor);
2060         XFreePixmap(display, pixmap_bottom);
2061 }
2062
2063 int BC_WindowBase::evaluate_color_model(int client_byte_order, int server_byte_order, int depth)
2064 {
2065         int color_model = BC_TRANSPARENCY;
2066         switch(depth)
2067         {
2068                 case 8:
2069                         color_model = BC_RGB8;
2070                         break;
2071                 case 16:
2072                         color_model = (server_byte_order == client_byte_order) ? BC_RGB565 : BC_BGR565;
2073                         break;
2074                 case 24:
2075                         color_model = server_byte_order ? BC_BGR888 : BC_RGB888;
2076                         break;
2077                 case 32:
2078                         color_model = server_byte_order ? BC_BGR8888 : BC_ARGB8888;
2079                         break;
2080         }
2081         return color_model;
2082 }
2083
2084 int BC_WindowBase::init_colors()
2085 {
2086         total_colors = 0;
2087         current_color_value = current_color_pixel = 0;
2088
2089 // Get the real depth
2090         char *data = 0;
2091         XImage *ximage;
2092         ximage = XCreateImage(top_level->display,
2093                                         top_level->vis,
2094                                         top_level->default_depth,
2095                                         ZPixmap,
2096                                         0,
2097                                         data,
2098                                         16,
2099                                         16,
2100                                         8,
2101                                         0);
2102         bits_per_pixel = ximage->bits_per_pixel;
2103         XDestroyImage(ximage);
2104
2105         color_model = evaluate_color_model(client_byte_order,
2106                 server_byte_order,
2107                 bits_per_pixel);
2108 // Get the color model
2109         switch(color_model)
2110         {
2111                 case BC_RGB8:
2112                         if(private_color) {
2113                                 cmap = XCreateColormap(display, rootwin, vis, AllocNone);
2114                                 create_private_colors();
2115                         }
2116                         else {
2117                                 cmap = DefaultColormap(display, screen);
2118                                 create_shared_colors();
2119                         }
2120
2121                         allocate_color_table();
2122                         break;
2123
2124                 default:
2125                         //cmap = DefaultColormap(display, screen);
2126                         cmap = XCreateColormap(display, rootwin, vis, AllocNone );
2127                         break;
2128         }
2129         return 0;
2130 }
2131
2132 int BC_WindowBase::create_private_colors()
2133 {
2134         int color;
2135         total_colors = 256;
2136
2137         for(int i = 0; i < 255; i++)
2138         {
2139                 color = (i & 0xc0) << 16;
2140                 color += (i & 0x38) << 10;
2141                 color += (i & 0x7) << 5;
2142                 color_table[i][0] = color;
2143         }
2144         create_shared_colors();        // overwrite the necessary colors on the table
2145         return 0;
2146 }
2147
2148
2149 int BC_WindowBase::create_color(int color)
2150 {
2151         if(total_colors == 256)
2152         {
2153 // replace the closest match with an exact match
2154                 color_table[get_color_rgb8(color)][0] = color;
2155         }
2156         else
2157         {
2158 // add the color to the table
2159                 color_table[total_colors][0] = color;
2160                 total_colors++;
2161         }
2162         return 0;
2163 }
2164
2165 int BC_WindowBase::create_shared_colors()
2166 {
2167         create_color(BLACK);
2168         create_color(WHITE);
2169
2170         create_color(LTGREY);
2171         create_color(MEGREY);
2172         create_color(MDGREY);
2173         create_color(DKGREY);
2174
2175         create_color(LTCYAN);
2176         create_color(MECYAN);
2177         create_color(MDCYAN);
2178         create_color(DKCYAN);
2179
2180         create_color(LTGREEN);
2181         create_color(GREEN);
2182         create_color(DKGREEN);
2183
2184         create_color(LTPINK);
2185         create_color(PINK);
2186         create_color(RED);
2187
2188         create_color(LTBLUE);
2189         create_color(BLUE);
2190         create_color(DKBLUE);
2191
2192         create_color(LTYELLOW);
2193         create_color(MEYELLOW);
2194         create_color(MDYELLOW);
2195         create_color(DKYELLOW);
2196
2197         create_color(LTPURPLE);
2198         create_color(MEPURPLE);
2199         create_color(MDPURPLE);
2200         create_color(DKPURPLE);
2201
2202         create_color(FGGREY);
2203         create_color(MNBLUE);
2204         create_color(ORANGE);
2205         create_color(FTGREY);
2206
2207         return 0;
2208 }
2209
2210 Cursor BC_WindowBase::create_grab_cursor()
2211 {
2212         int iw = 23, iw1 = iw-1, iw2 = iw/2;
2213         int ih = 23, ih1 = ih-1, ih2 = ih/2;
2214         VFrame grab(iw,ih,BC_RGB888);
2215         grab.clear_frame();
2216         grab.set_pixel_color(RED);   // fg
2217         grab.draw_smooth(iw2,0,   iw1,0,   iw1,ih2);
2218         grab.draw_smooth(iw1,ih2, iw1,ih1, iw2,ih1);
2219         grab.draw_smooth(iw2,ih1, 0,ih1,   0,ih2);
2220         grab.draw_smooth(0,ih2,   0,0,     iw2,0);
2221         grab.set_pixel_color(WHITE); // bg
2222         grab.draw_line(0,ih2,     iw2-2,ih2);
2223         grab.draw_line(iw2+2,ih2, iw1,ih2);
2224         grab.draw_line(iw2,0,     iw2,ih2-2);
2225         grab.draw_line(iw2,ih2+2, iw2,ih1);
2226
2227         int bpl = (iw+7)/8, isz = bpl * ih;
2228         char img[isz];  memset(img, 0, isz);
2229         char msk[isz];  memset(msk, 0, isz);
2230         unsigned char **rows = grab.get_rows();
2231         for( int iy=0; iy<ih; ++iy ) {
2232                 char *op = img + iy*bpl;
2233                 char *mp = msk + iy*bpl;
2234                 unsigned char *ip = rows[iy];
2235                 for( int ix=0; ix<iw; ++ix,ip+=3 ) {
2236                         if( ip[0] ) mp[ix>>3] |= (1<<(ix&7));
2237                         if( !ip[1] ) op[ix>>3] |= (1<<(ix&7));
2238                 }
2239         }
2240         unsigned long white_pix = WhitePixel(display, screen);
2241         unsigned long black_pix = BlackPixel(display, screen);
2242         Pixmap img_xpm = XCreatePixmapFromBitmapData(display, rootwin,
2243                 img, iw,ih, white_pix,black_pix, 1);
2244         Pixmap msk_xpm = XCreatePixmapFromBitmapData(display, rootwin,
2245                 msk, iw,ih, white_pix,black_pix, 1);
2246
2247         XColor fc, bc;
2248         fc.flags = bc.flags = DoRed | DoGreen | DoBlue;
2249         fc.red = 0xffff; fc.green = fc.blue = 0;  // fg
2250         bc.red = 0xffff; bc.green = 0xffff; bc.blue = 0x0000;     // bg
2251         Cursor cursor = XCreatePixmapCursor(display, img_xpm,msk_xpm, &fc,&bc, iw2,ih2);
2252         XFreePixmap(display, img_xpm);
2253         XFreePixmap(display, msk_xpm);
2254         return cursor;
2255 }
2256
2257 int BC_WindowBase::allocate_color_table()
2258 {
2259         int red, green, blue, color;
2260         XColor col;
2261
2262         for(int i = 0; i < total_colors; i++)
2263         {
2264                 color = color_table[i][0];
2265                 red = (color & 0xFF0000) >> 16;
2266                 green = (color & 0x00FF00) >> 8;
2267                 blue = color & 0xFF;
2268
2269                 col.flags = DoRed | DoGreen | DoBlue;
2270                 col.red   = red<<8   | red;
2271                 col.green = green<<8 | green;
2272                 col.blue  = blue<<8  | blue;
2273
2274                 XAllocColor(display, cmap, &col);
2275                 color_table[i][1] = col.pixel;
2276         }
2277
2278         XInstallColormap(display, cmap);
2279         return 0;
2280 }
2281
2282 int BC_WindowBase::init_window_shape()
2283 {
2284         if(bg_pixmap && bg_pixmap->use_alpha())
2285         {
2286                 XShapeCombineMask(top_level->display,
2287                         this->win, ShapeBounding, 0, 0,
2288                         bg_pixmap->get_alpha(), ShapeSet);
2289         }
2290         return 0;
2291 }
2292
2293
2294 int BC_WindowBase::init_gc()
2295 {
2296         unsigned long gcmask;
2297         gcmask = GCFont | GCGraphicsExposures;
2298
2299         XGCValues gcvalues;
2300         gcvalues.font = mediumfont->fid;        // set the font
2301         gcvalues.graphics_exposures = 0;        // prevent expose events for every redraw
2302         gc = XCreateGC(display, rootwin, gcmask, &gcvalues);
2303
2304 // gcmask = GCCapStyle | GCJoinStyle;
2305 // XGetGCValues(display, gc, gcmask, &gcvalues);
2306 // printf("BC_WindowBase::init_gc %d %d %d\n", __LINE__, gcvalues.cap_style, gcvalues.join_style);
2307         return 0;
2308 }
2309
2310 int BC_WindowBase::init_fonts()
2311 {
2312         if( !(smallfont = XLoadQueryFont(display, _(resources.small_font))) )
2313                 if( !(smallfont = XLoadQueryFont(display, _(resources.small_font2))) )
2314                         smallfont = XLoadQueryFont(display, "fixed");
2315         if( !(mediumfont = XLoadQueryFont(display, _(resources.medium_font))) )
2316                 if( !(mediumfont = XLoadQueryFont(display, _(resources.medium_font2))) )
2317                         mediumfont = XLoadQueryFont(display, "fixed");
2318         if( !(largefont = XLoadQueryFont(display, _(resources.large_font))) )
2319                 if( !(largefont = XLoadQueryFont(display, _(resources.large_font2))) )
2320                         largefont = XLoadQueryFont(display, "fixed");
2321         if( !(bigfont = XLoadQueryFont(display, _(resources.big_font))) )
2322                 if( !(bigfont = XLoadQueryFont(display, _(resources.big_font2))) )
2323                         bigfont = XLoadQueryFont(display, "fixed");
2324
2325         if((clockfont = XLoadQueryFont(display, _(resources.clock_font))) == NULL)
2326                 if((clockfont = XLoadQueryFont(display, _(resources.clock_font2))) == NULL)
2327                         clockfont = XLoadQueryFont(display, "fixed");
2328
2329         init_xft();
2330         if(get_resources()->use_fontset)
2331         {
2332                 char **m, *d;
2333                 int n;
2334
2335 // FIXME: should check the m,d,n values
2336                 smallfontset = XCreateFontSet(display, resources.small_fontset, &m, &n, &d);
2337                 if( !smallfontset )
2338                         smallfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2339                 mediumfontset = XCreateFontSet(display, resources.medium_fontset, &m, &n, &d);
2340                 if( !mediumfontset )
2341                         mediumfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2342                 largefontset = XCreateFontSet(display, resources.large_fontset, &m, &n, &d);
2343                 if( !largefontset )
2344                         largefontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2345                 bigfontset = XCreateFontSet(display, resources.big_fontset, &m, &n, &d);
2346                 if( !bigfontset )
2347                         bigfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2348                 clockfontset = XCreateFontSet(display, resources.clock_fontset, &m, &n, &d);
2349                 if( !clockfontset )
2350                         clockfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2351                 if(clockfontset && bigfontset && largefontset && mediumfontset && smallfontset) {
2352                         curr_fontset = mediumfontset;
2353                         get_resources()->use_fontset = 1;
2354                 }
2355                 else {
2356                         curr_fontset = 0;
2357                         get_resources()->use_fontset = 0;
2358                 }
2359         }
2360
2361         return 0;
2362 }
2363
2364 void BC_WindowBase::init_xft()
2365 {
2366 #ifdef HAVE_XFT
2367         if( !get_resources()->use_xft ) return;
2368 // apparently, xft is not reentrant, more than this is needed
2369 static Mutex xft_init_lock("BC_WindowBase::xft_init_lock", 0);
2370 xft_init_lock.lock("BC_WindowBase::init_xft");
2371         if(!(smallfont_xft =
2372                 (resources.small_font_xft[0] == '-' ?
2373                         xftFontOpenXlfd(display, screen, resources.small_font_xft) :
2374                         xftFontOpenName(display, screen, resources.small_font_xft))) )
2375                 if(!(smallfont_xft =
2376                         xftFontOpenXlfd(display, screen, resources.small_font_xft2)))
2377                         smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2378         if(!(mediumfont_xft =
2379                 (resources.medium_font_xft[0] == '-' ?
2380                         xftFontOpenXlfd(display, screen, resources.medium_font_xft) :
2381                         xftFontOpenName(display, screen, resources.medium_font_xft))) )
2382                 if(!(mediumfont_xft =
2383                         xftFontOpenXlfd(display, screen, resources.medium_font_xft2)))
2384                         mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2385         if(!(largefont_xft =
2386                 (resources.large_font_xft[0] == '-' ?
2387                         xftFontOpenXlfd(display, screen, resources.large_font_xft) :
2388                         xftFontOpenName(display, screen, resources.large_font_xft))) )
2389                 if(!(largefont_xft =
2390                         xftFontOpenXlfd(display, screen, resources.large_font_xft2)))
2391                         largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
2392         if(!(bigfont_xft =
2393                 (resources.big_font_xft[0] == '-' ?
2394                         xftFontOpenXlfd(display, screen, resources.big_font_xft) :
2395                         xftFontOpenName(display, screen, resources.big_font_xft))) )
2396                 if(!(bigfont_xft =
2397                         xftFontOpenXlfd(display, screen, resources.big_font_xft2)))
2398                         bigfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2399         if(!(clockfont_xft =
2400                 (resources.clock_font_xft[0] == '-' ?
2401                         xftFontOpenXlfd(display, screen, resources.clock_font_xft) :
2402                         xftFontOpenName(display, screen, resources.clock_font_xft))) )
2403                 clockfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2404
2405
2406         if(!(bold_smallfont_xft =
2407                 (resources.small_b_font_xft[0] == '-' ?
2408                         xftFontOpenXlfd(display, screen, resources.small_b_font_xft) :
2409                         xftFontOpenName(display, screen, resources.small_b_font_xft))) )
2410                 bold_smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2411         if(!(bold_mediumfont_xft =
2412                 (resources.medium_b_font_xft[0] == '-' ?
2413                         xftFontOpenXlfd(display, screen, resources.medium_b_font_xft) :
2414                         xftFontOpenName(display, screen, resources.medium_b_font_xft))) )
2415                 bold_mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2416         if(!(bold_largefont_xft =
2417                 (resources.large_b_font_xft[0] == '-' ?
2418                         xftFontOpenXlfd(display, screen, resources.large_b_font_xft) :
2419                         xftFontOpenName(display, screen, resources.large_b_font_xft))) )
2420                 bold_largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
2421
2422         if( !smallfont_xft || !mediumfont_xft || !largefont_xft || !bigfont_xft ||
2423             !bold_largefont_xft || !bold_mediumfont_xft || !bold_largefont_xft ||
2424             !clockfont_xft ) {
2425                 printf("BC_WindowBase::init_fonts: no xft fonts found:"
2426                         " %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n",
2427                         resources.small_font_xft, smallfont_xft,
2428                         resources.medium_font_xft, mediumfont_xft,
2429                         resources.large_font_xft, largefont_xft,
2430                         resources.big_font_xft, bigfont_xft,
2431                         resources.clock_font_xft, clockfont_xft,
2432                         resources.small_b_font_xft, bold_smallfont_xft,
2433                         resources.medium_b_font_xft, bold_mediumfont_xft,
2434                         resources.large_b_font_xft, bold_largefont_xft);
2435                 get_resources()->use_xft = 0;
2436                 exit(1);
2437         }
2438 // _XftDisplayInfo needs a lock.
2439         xftDefaultHasRender(display);
2440 xft_init_lock.unlock();
2441 #endif // HAVE_XFT
2442 }
2443
2444 void BC_WindowBase::init_im()
2445 {
2446         XIMStyles *xim_styles;
2447         XIMStyle xim_style;
2448
2449         if(!(input_method = XOpenIM(display, NULL, NULL, NULL)))
2450         {
2451                 printf("BC_WindowBase::init_im: Could not open input method.\n");
2452                 exit(1);
2453         }
2454         if(XGetIMValues(input_method, XNQueryInputStyle, &xim_styles, NULL) ||
2455                         xim_styles == NULL)
2456         {
2457                 printf("BC_WindowBase::init_im: Input method doesn't support any styles.\n");
2458                 XCloseIM(input_method);
2459                 exit(1);
2460         }
2461
2462         xim_style = 0;
2463         for(int z = 0;  z < xim_styles->count_styles;  z++)
2464         {
2465                 if(xim_styles->supported_styles[z] == (XIMPreeditNothing | XIMStatusNothing))
2466                 {
2467                         xim_style = xim_styles->supported_styles[z];
2468                         break;
2469                 }
2470         }
2471         XFree(xim_styles);
2472
2473         if(xim_style == 0)
2474         {
2475                 printf("BC_WindowBase::init_im: Input method doesn't support the style we need.\n");
2476                 XCloseIM(input_method);
2477                 exit(1);
2478         }
2479
2480         input_context = XCreateIC(input_method, XNInputStyle, xim_style,
2481                 XNClientWindow, win, XNFocusWindow, win, NULL);
2482         if(!input_context)
2483         {
2484                 printf("BC_WindowBase::init_im: Failed to create input context.\n");
2485                 XCloseIM(input_method);
2486                 exit(1);
2487         }
2488 }
2489
2490 void BC_WindowBase::finit_im()
2491 {
2492         if( input_context ) {
2493                 XDestroyIC(input_context);
2494                 input_context = 0;
2495         }
2496         if( input_method ) {
2497                 XCloseIM(input_method);
2498                 input_method = 0;
2499         }
2500 }
2501
2502
2503 int BC_WindowBase::get_color(int64_t color)
2504 {
2505 // return pixel of color
2506 // use this only for drawing subwindows not for bitmaps
2507          int i, test, difference;
2508
2509         switch(color_model)
2510         {
2511         case BC_RGB8:
2512                 if(private_color)
2513                         return get_color_rgb8(color);
2514 // test last color looked up
2515                 if(current_color_value == color)
2516                         return current_color_pixel;
2517
2518 // look up in table
2519                 current_color_value = color;
2520                 for(i = 0; i < total_colors; i++)
2521                 {
2522                         if(color_table[i][0] == color)
2523                         {
2524                                 current_color_pixel = color_table[i][1];
2525                                 return current_color_pixel;
2526                         }
2527                 }
2528
2529 // find nearest match
2530                 difference = 0xFFFFFF;
2531
2532                 for(i = 0; i < total_colors; i++)
2533                 {
2534                         test = abs((int)(color_table[i][0] - color));
2535
2536                         if(test < difference)
2537                         {
2538                                 current_color_pixel = color_table[i][1];
2539                                 difference = test;
2540                         }
2541                 }
2542                 return current_color_pixel;
2543
2544         case BC_RGB565:
2545                 return get_color_rgb16(color);
2546
2547         case BC_BGR565:
2548                 return get_color_bgr16(color);
2549
2550         case BC_RGB888:
2551         case BC_BGR888:
2552                 return client_byte_order == server_byte_order ?
2553                         color : get_color_bgr24(color);
2554
2555         default:
2556                 return color;
2557         }
2558         return 0;
2559 }
2560
2561 int BC_WindowBase::get_color_rgb8(int color)
2562 {
2563         int pixel;
2564
2565         pixel = (color & 0xc00000) >> 16;
2566         pixel += (color & 0xe000) >> 10;
2567         pixel += (color & 0xe0) >> 5;
2568         return pixel;
2569 }
2570
2571 int64_t BC_WindowBase::get_color_rgb16(int color)
2572 {
2573         int64_t result;
2574         result = (color & 0xf80000) >> 8;
2575         result += (color & 0xfc00) >> 5;
2576         result += (color & 0xf8) >> 3;
2577
2578         return result;
2579 }
2580
2581 int64_t BC_WindowBase::get_color_bgr16(int color)
2582 {
2583         int64_t result;
2584         result = (color & 0xf80000) >> 19;
2585         result += (color & 0xfc00) >> 5;
2586         result += (color & 0xf8) << 8;
2587
2588         return result;
2589 }
2590
2591 int64_t BC_WindowBase::get_color_bgr24(int color)
2592 {
2593         int64_t result;
2594         result = (color & 0xff) << 16;
2595         result += (color & 0xff00);
2596         result += (color & 0xff0000) >> 16;
2597         return result;
2598 }
2599
2600 void BC_WindowBase::start_video()
2601 {
2602         cursor_timer->update();
2603         video_on = 1;
2604 //      set_color(BLACK);
2605 //      draw_box(0, 0, get_w(), get_h());
2606 //      flash();
2607 }
2608
2609 void BC_WindowBase::stop_video()
2610 {
2611         video_on = 0;
2612         unhide_cursor();
2613 }
2614
2615
2616
2617 int64_t BC_WindowBase::get_color()
2618 {
2619         return top_level->current_color;
2620 }
2621
2622 void BC_WindowBase::set_color(int64_t color)
2623 {
2624         top_level->current_color = color;
2625         XSetForeground(top_level->display,
2626                 top_level->gc,
2627                 top_level->get_color(color));
2628 }
2629
2630 void BC_WindowBase::set_opaque()
2631 {
2632         XSetFunction(top_level->display, top_level->gc, GXcopy);
2633 }
2634
2635 void BC_WindowBase::set_inverse()
2636 {
2637         XSetFunction(top_level->display, top_level->gc, GXxor);
2638 }
2639
2640 void BC_WindowBase::set_line_width(int value)
2641 {
2642         this->line_width = value;
2643         XSetLineAttributes(top_level->display, top_level->gc, value,    /* line_width */
2644                 line_dashes == 0 ? LineSolid : LineOnOffDash,           /* line_style */
2645                 line_dashes == 0 ? CapRound : CapNotLast,               /* cap_style */
2646                 JoinMiter);                                             /* join_style */
2647
2648         if(line_dashes > 0) {
2649                 const char dashes = line_dashes;
2650                 XSetDashes(top_level->display, top_level->gc, 0, &dashes, 1);
2651         }
2652
2653 // XGCValues gcvalues;
2654 // unsigned long gcmask;
2655 // gcmask = GCCapStyle | GCJoinStyle;
2656 // XGetGCValues(top_level->display, top_level->gc, gcmask, &gcvalues);
2657 // printf("BC_WindowBase::set_line_width %d %d %d\n", __LINE__, gcvalues.cap_style, gcvalues.join_style);
2658 }
2659
2660 void BC_WindowBase::set_line_dashes(int value)
2661 {
2662         line_dashes = value;
2663 // call XSetLineAttributes
2664         set_line_width(line_width);
2665 }
2666
2667
2668 Cursor BC_WindowBase::get_cursor_struct(int cursor)
2669 {
2670         switch(cursor)
2671         {
2672                 case ARROW_CURSOR:         return top_level->arrow_cursor;
2673                 case CROSS_CURSOR:         return top_level->cross_cursor;
2674                 case IBEAM_CURSOR:         return top_level->ibeam_cursor;
2675                 case VSEPARATE_CURSOR:     return top_level->vseparate_cursor;
2676                 case HSEPARATE_CURSOR:     return top_level->hseparate_cursor;
2677                 case MOVE_CURSOR:          return top_level->move_cursor;
2678                 case LEFT_CURSOR:          return top_level->left_cursor;
2679                 case RIGHT_CURSOR:         return top_level->right_cursor;
2680                 case UPRIGHT_ARROW_CURSOR: return top_level->upright_arrow_cursor;
2681                 case UPLEFT_RESIZE:        return top_level->upleft_resize_cursor;
2682                 case UPRIGHT_RESIZE:       return top_level->upright_resize_cursor;
2683                 case DOWNLEFT_RESIZE:      return top_level->downleft_resize_cursor;
2684                 case DOWNRIGHT_RESIZE:     return top_level->downright_resize_cursor;
2685                 case HOURGLASS_CURSOR:     return top_level->hourglass_cursor;
2686                 case TRANSPARENT_CURSOR:   return top_level->transparent_cursor;
2687                 case GRABBED_CURSOR:       return top_level->grabbed_cursor;
2688         }
2689         return 0;
2690 }
2691
2692 void BC_WindowBase::set_cursor(int cursor, int override, int flush)
2693 {
2694 // inherit cursor from parent
2695         if(cursor < 0)
2696         {
2697                 XUndefineCursor(top_level->display, win);
2698                 current_cursor = cursor;
2699         }
2700         else
2701 // don't change cursor if overridden
2702         if((!top_level->is_hourglass && !is_transparent) ||
2703                 override)
2704         {
2705                 XDefineCursor(top_level->display, win, get_cursor_struct(cursor));
2706                 if(flush) this->flush();
2707         }
2708
2709         if(!override) current_cursor = cursor;
2710 }
2711
2712 void BC_WindowBase::set_x_cursor(int cursor)
2713 {
2714         temp_cursor = XCreateFontCursor(top_level->display, cursor);
2715         XDefineCursor(top_level->display, win, temp_cursor);
2716         current_cursor = cursor;
2717         flush();
2718 }
2719
2720 int BC_WindowBase::get_cursor()
2721 {
2722         return current_cursor;
2723 }
2724
2725 void BC_WindowBase::start_hourglass()
2726 {
2727         top_level->start_hourglass_recursive();
2728         top_level->flush();
2729 }
2730
2731 void BC_WindowBase::stop_hourglass()
2732 {
2733         top_level->stop_hourglass_recursive();
2734         top_level->flush();
2735 }
2736
2737 void BC_WindowBase::start_hourglass_recursive()
2738 {
2739         if(this == top_level)
2740         {
2741                 hourglass_total++;
2742                 is_hourglass = 1;
2743         }
2744
2745         if(!is_transparent)
2746         {
2747                 set_cursor(HOURGLASS_CURSOR, 1, 0);
2748                 for(int i = 0; i < subwindows->total; i++)
2749                 {
2750                         subwindows->values[i]->start_hourglass_recursive();
2751                 }
2752         }
2753 }
2754
2755 void BC_WindowBase::stop_hourglass_recursive()
2756 {
2757         if(this == top_level)
2758         {
2759                 if(hourglass_total == 0) return;
2760                 top_level->hourglass_total--;
2761         }
2762
2763         if(!top_level->hourglass_total)
2764         {
2765                 top_level->is_hourglass = 0;
2766
2767 // Cause set_cursor to perform change
2768                 if(!is_transparent)
2769                         set_cursor(current_cursor, 1, 0);
2770
2771                 for(int i = 0; i < subwindows->total; i++)
2772                 {
2773                         subwindows->values[i]->stop_hourglass_recursive();
2774                 }
2775         }
2776 }
2777
2778
2779
2780
2781 XFontStruct* BC_WindowBase::get_font_struct(int font)
2782 {
2783 // Clear out unrelated flags
2784         if(font & BOLDFACE) font ^= BOLDFACE;
2785
2786         switch(font) {
2787                 case SMALLFONT:  return top_level->smallfont;  break;
2788                 case MEDIUMFONT: return top_level->mediumfont; break;
2789                 case LARGEFONT:  return top_level->largefont;  break;
2790                 case BIGFONT:    return top_level->bigfont;    break;
2791                 case CLOCKFONT:  return top_level->clockfont;  break;
2792         }
2793         return 0;
2794 }
2795
2796 XFontSet BC_WindowBase::get_fontset(int font)
2797 {
2798         XFontSet fs = 0;
2799
2800         if(get_resources()->use_fontset)
2801         {
2802                 switch(font & 0xff) {
2803                         case SMALLFONT:  fs = top_level->smallfontset; break;
2804                         case MEDIUMFONT: fs = top_level->mediumfontset; break;
2805                         case LARGEFONT:  fs = top_level->largefontset; break;
2806                         case BIGFONT:    fs = top_level->bigfontset;   break;
2807                         case CLOCKFONT: fs = top_level->clockfontset; break;
2808                 }
2809         }
2810
2811         return fs;
2812 }
2813
2814 #ifdef HAVE_XFT
2815 XftFont* BC_WindowBase::get_xft_struct(int font)
2816 {
2817         switch(font) {
2818                 case SMALLFONT:    return (XftFont*)top_level->smallfont_xft;
2819                 case MEDIUMFONT:   return (XftFont*)top_level->mediumfont_xft;
2820                 case LARGEFONT:    return (XftFont*)top_level->largefont_xft;
2821                 case BIGFONT:      return (XftFont*)top_level->bigfont_xft;
2822                 case CLOCKFONT:    return (XftFont*)top_level->clockfont_xft;
2823                 case MEDIUMFONT_3D: return (XftFont*)top_level->bold_mediumfont_xft;
2824                 case SMALLFONT_3D:  return (XftFont*)top_level->bold_smallfont_xft;
2825                 case LARGEFONT_3D:  return (XftFont*)top_level->bold_largefont_xft;
2826         }
2827
2828         return 0;
2829 }
2830 #endif
2831
2832
2833 int BC_WindowBase::get_current_font()
2834 {
2835         return top_level->current_font;
2836 }
2837
2838 void BC_WindowBase::set_font(int font)
2839 {
2840         top_level->current_font = font;
2841
2842 #ifdef HAVE_XFT
2843         if(get_resources()->use_xft) {}
2844         else
2845 #endif
2846         if(get_resources()->use_fontset) {
2847                 set_fontset(font);
2848         }
2849
2850         if(get_font_struct(font))
2851         {
2852                 XSetFont(top_level->display, top_level->gc, get_font_struct(font)->fid);
2853         }
2854
2855         return;
2856 }
2857
2858 void BC_WindowBase::set_fontset(int font)
2859 {
2860         XFontSet fs = 0;
2861
2862         if(get_resources()->use_fontset) {
2863                 switch(font) {
2864                         case SMALLFONT:  fs = top_level->smallfontset; break;
2865                         case MEDIUMFONT: fs = top_level->mediumfontset; break;
2866                         case LARGEFONT:  fs = top_level->largefontset; break;
2867                         case BIGFONT:    fs = top_level->bigfontset;   break;
2868                         case CLOCKFONT:  fs = top_level->clockfontset; break;
2869                 }
2870         }
2871
2872         curr_fontset = fs;
2873 }
2874
2875
2876 XFontSet BC_WindowBase::get_curr_fontset(void)
2877 {
2878         if(get_resources()->use_fontset)
2879                 return curr_fontset;
2880         return 0;
2881 }
2882
2883 int BC_WindowBase::get_single_text_width(int font, const char *text, int length)
2884 {
2885 #ifdef HAVE_XFT
2886         if(get_resources()->use_xft && get_xft_struct(font))
2887         {
2888                 XGlyphInfo extents;
2889 #ifdef X_HAVE_UTF8_STRING
2890                 if(get_resources()->locale_utf8)
2891                 {
2892                         xftTextExtentsUtf8(top_level->display,
2893                                 get_xft_struct(font),
2894                                 (const XftChar8 *)text,
2895                                 length,
2896                                 &extents);
2897                 }
2898                 else
2899 #endif
2900                 {
2901                         xftTextExtents8(top_level->display,
2902                                 get_xft_struct(font),
2903                                 (const XftChar8 *)text,
2904                                 length,
2905                                 &extents);
2906                 }
2907                 return extents.xOff;
2908         }
2909         else
2910 #endif
2911         if(get_resources()->use_fontset && top_level->get_fontset(font))
2912                 return XmbTextEscapement(top_level->get_fontset(font), text, length);
2913         else
2914         if(get_font_struct(font))
2915                 return XTextWidth(get_font_struct(font), text, length);
2916         else
2917         {
2918                 int w = 0;
2919                 switch(font)
2920                 {
2921                         case MEDIUM_7SEGMENT:
2922                                 return get_resources()->medium_7segment[0]->get_w() * length;
2923                                 break;
2924
2925                         default:
2926                                 return 0;
2927                 }
2928                 return w;
2929         }
2930 }
2931
2932 int BC_WindowBase::get_text_width(int font, const char *text, int length)
2933 {
2934         int i, j, w = 0, line_w = 0;
2935         if(length < 0) length = strlen(text);
2936
2937         for(i = 0, j = 0; i <= length; i++)
2938         {
2939                 line_w = 0;
2940                 if(text[i] == '\n')
2941                 {
2942                         line_w = get_single_text_width(font, &text[j], i - j);
2943                         j = i + 1;
2944                 }
2945                 else
2946                 if(text[i] == 0)
2947                 {
2948                         line_w = get_single_text_width(font, &text[j], length - j);
2949                 }
2950                 if(line_w > w) w = line_w;
2951         }
2952
2953         if(i > length && w == 0)
2954         {
2955                 w = get_single_text_width(font, text, length);
2956         }
2957
2958         return w;
2959 }
2960
2961 int BC_WindowBase::get_text_width(int font, const wchar_t *text, int length)
2962 {
2963         int i, j, w = 0;
2964         if( length < 0 ) length = wcslen(text);
2965
2966         for( i=j=0; i<length && text[i]; ++i ) {
2967                 if( text[i] != '\n' ) continue;
2968                 if( i > j ) {
2969                         int lw = get_single_text_width(font, &text[j], i-j);
2970                         if( w < lw ) w = lw;
2971                 }
2972                 j = i + 1;
2973         }
2974         if( j < length ) {
2975                 int lw = get_single_text_width(font, &text[j], length-j);
2976                 if( w < lw ) w = lw;
2977         }
2978
2979         return w;
2980 }
2981
2982 int BC_WindowBase::get_text_ascent(int font)
2983 {
2984 #ifdef HAVE_XFT
2985         XftFont *fstruct;
2986         if( (fstruct = get_xft_struct(font)) != 0 )
2987                 return fstruct->ascent;
2988 #endif
2989         if(get_resources()->use_fontset && top_level->get_fontset(font))
2990         {
2991                 XFontSetExtents *extents;
2992
2993                 extents = XExtentsOfFontSet(top_level->get_fontset(font));
2994                 return -extents->max_logical_extent.y;
2995         }
2996
2997         if(get_font_struct(font))
2998                 return top_level->get_font_struct(font)->ascent;
2999
3000         switch(font) {
3001                 case MEDIUM_7SEGMENT:
3002                         return get_resources()->medium_7segment[0]->get_h();
3003         }
3004         return 0;
3005 }
3006
3007 int BC_WindowBase::get_text_descent(int font)
3008 {
3009 #ifdef HAVE_XFT
3010         XftFont *fstruct;
3011         if( (fstruct = get_xft_struct(font)) != 0 )
3012                 return fstruct->descent;
3013 #endif
3014         if(get_resources()->use_fontset && top_level->get_fontset(font)) {
3015                 XFontSetExtents *extents;
3016                 extents = XExtentsOfFontSet(top_level->get_fontset(font));
3017                 return (extents->max_logical_extent.height
3018                         + extents->max_logical_extent.y);
3019         }
3020
3021         if(get_font_struct(font))
3022                 return top_level->get_font_struct(font)->descent;
3023
3024         return 0;
3025 }
3026
3027 int BC_WindowBase::get_text_height(int font, const char *text)
3028 {
3029         int rowh;
3030 #ifdef HAVE_XFT
3031         XftFont *fstruct;
3032         if( (fstruct = get_xft_struct(font)) != 0 )
3033                 rowh = fstruct->height;
3034         else
3035 #endif
3036                 rowh = get_text_ascent(font) + get_text_descent(font);
3037
3038         if(!text) return rowh;
3039
3040 // Add height of lines
3041         int h = 0, i, length = strlen(text);
3042         for(i = 0; i <= length; i++)
3043         {
3044                 if(text[i] == '\n')
3045                         h++;
3046                 else
3047                 if(text[i] == 0)
3048                         h++;
3049         }
3050         return h * rowh;
3051 }
3052
3053 BC_Bitmap* BC_WindowBase::new_bitmap(int w, int h, int color_model)
3054 {
3055         if(color_model < 0) color_model = top_level->get_color_model();
3056         return new BC_Bitmap(top_level, w, h, color_model);
3057 }
3058
3059 void BC_WindowBase::init_wait()
3060 {
3061 #ifndef SINGLE_THREAD
3062         if(window_type != MAIN_WINDOW)
3063                 top_level->init_wait();
3064         init_lock->lock("BC_WindowBase::init_wait");
3065         init_lock->unlock();
3066 #endif
3067 }
3068
3069 int BC_WindowBase::accel_available(int color_model, int lock_it)
3070 {
3071         if( window_type != MAIN_WINDOW )
3072                 return top_level->accel_available(color_model, lock_it);
3073         if( lock_it )
3074                 lock_window("BC_WindowBase::accel_available");
3075
3076         switch(color_model) {
3077         case BC_YUV420P:
3078                 grab_port_id(color_model);
3079                 break;
3080
3081         case BC_YUV422:
3082                 grab_port_id(color_model);
3083                 break;
3084
3085         default:
3086                 break;
3087         }
3088
3089         if( lock_it )
3090                 unlock_window();
3091 //printf("BC_WindowBase::accel_available %d %d\n", color_model, xvideo_port_id);
3092         return xvideo_port_id >= 0 ? 1 : 0;
3093 }
3094
3095
3096 int BC_WindowBase::grab_port_id(int color_model)
3097 {
3098         if( !get_resources()->use_xvideo ||     // disabled
3099             !get_resources()->use_shm )         // Only local server is fast enough.
3100                 return -1;
3101         if( xvideo_port_id >= 0 )
3102                 return xvideo_port_id;
3103
3104         unsigned int ver, rev, reqBase, eventBase, errorBase;
3105         if( Success != XvQueryExtension(display, // XV extension is available
3106                     &ver, &rev, &reqBase, &eventBase, &errorBase) )
3107                 return -1;
3108
3109 // XV adaptors are available
3110         unsigned int numAdapt = 0;
3111         XvAdaptorInfo *info = 0;
3112         XvQueryAdaptors(display, DefaultRootWindow(display), &numAdapt, &info);
3113         if( !numAdapt )
3114                 return -1;
3115
3116 // Translate from color_model to X color model
3117         int x_color_model = BC_CModels::bc_to_x(color_model);
3118
3119 // Get adaptor with desired color model
3120         for( int i = 0; i < (int)numAdapt && xvideo_port_id == -1; i++) {
3121                 if( !(info[i].type & XvImageMask) || !info[i].num_ports ) continue;
3122 // adaptor supports XvImages
3123                 int numFormats = 0, numPorts = info[i].num_ports;
3124                 XvImageFormatValues *formats =
3125                         XvListImageFormats(display, info[i].base_id, &numFormats);
3126                 if( !formats ) continue;
3127
3128                 for( int j=0; j<numFormats && xvideo_port_id<0; ++j ) {
3129                         if( formats[j].id != x_color_model ) continue;
3130 // this adaptor supports the desired format, grab a port
3131                         for( int k=0; k<numPorts; ++k ) {
3132                                 if( Success == XvGrabPort(top_level->display,
3133                                         info[i].base_id+k, CurrentTime) ) {
3134 //printf("BC_WindowBase::grab_port_id %llx\n", info[i].base_id);
3135                                         xvideo_port_id = info[i].base_id + k;
3136                                         break;
3137                                 }
3138                         }
3139                 }
3140                 XFree(formats);
3141         }
3142
3143         XvFreeAdaptorInfo(info);
3144
3145         return xvideo_port_id;
3146 }
3147
3148
3149 int BC_WindowBase::show_window(int flush)
3150 {
3151         for(int i = 0; i < subwindows->size(); i++)
3152         {
3153                 subwindows->get(i)->show_window(0);
3154         }
3155
3156         XMapWindow(top_level->display, win);
3157         if(flush) XFlush(top_level->display);
3158 //      XSync(top_level->display, 0);
3159         hidden = 0;
3160         return 0;
3161 }
3162
3163 int BC_WindowBase::hide_window(int flush)
3164 {
3165         for(int i = 0; i < subwindows->size(); i++)
3166         {
3167                 subwindows->get(i)->hide_window(0);
3168         }
3169
3170         XUnmapWindow(top_level->display, win);
3171         if(flush) this->flush();
3172         hidden = 1;
3173         return 0;
3174 }
3175
3176 BC_MenuBar* BC_WindowBase::add_menubar(BC_MenuBar *menu_bar)
3177 {
3178         subwindows->append((BC_SubWindow*)menu_bar);
3179
3180         menu_bar->parent_window = this;
3181         menu_bar->top_level = this->top_level;
3182         menu_bar->initialize();
3183         return menu_bar;
3184 }
3185
3186 BC_WindowBase* BC_WindowBase::add_popup(BC_WindowBase *window)
3187 {
3188 //printf("BC_WindowBase::add_popup window=%p win=%p\n", window, window->win);
3189         if(this != top_level) return top_level->add_popup(window);
3190         popups.append(window);
3191         return window;
3192 }
3193
3194 void BC_WindowBase::remove_popup(BC_WindowBase *window)
3195 {
3196 //printf("BC_WindowBase::remove_popup %d size=%d window=%p win=%p\n", __LINE__, popups.size(), window, window->win);