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