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