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