c57cd6203c8f6569a1b24b6feb7c2cfbb7d9f6a9
[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 #ifdef HAVE_XV
61 #include <X11/extensions/Xvlib.h>
62 #endif
63 #include <X11/extensions/shape.h>
64 #include <X11/XF86keysym.h>
65 #include <X11/Sunkeysym.h>
66
67 BC_ResizeCall::BC_ResizeCall(int w, int h)
68 {
69         this->w = w;
70         this->h = h;
71 }
72
73
74 int BC_WindowBase::shm_completion_event = -1;
75 BC_Resources *BC_WindowBase::resources = 0;
76 Window XGroupLeader = 0;
77
78 Mutex BC_KeyboardHandlerLock::keyboard_listener_mutex("keyboard_listener",0);
79 ArrayList<BC_KeyboardHandler*> BC_KeyboardHandler::listeners;
80
81 BC_WindowBase::BC_WindowBase()
82 {
83 //printf("BC_WindowBase::BC_WindowBase 1\n");
84         BC_WindowBase::initialize();
85 }
86
87 BC_WindowBase::~BC_WindowBase()
88 {
89 #ifdef SINGLE_THREAD
90         BC_Display::lock_display("BC_WindowBase::~BC_WindowBase");
91 #else
92         if(window_type == MAIN_WINDOW)
93                 lock_window("BC_WindowBase::~BC_WindowBase");
94 #endif
95
96 #ifdef HAVE_LIBXXF86VM
97         if(window_type == VIDMODE_SCALED_WINDOW && vm_switched) {
98                 restore_vm();
99         }
100 #endif
101         is_deleting = 1;
102
103         hide_tooltip();
104         if(window_type != MAIN_WINDOW)
105         {
106 // stop event input
107                 XSelectInput(top_level->display, this->win, 0);
108                 XSync(top_level->display,0);
109 #ifndef SINGLE_THREAD
110                 top_level->dequeue_events(win);
111 #endif
112 // drop active window refs to this
113                 if(top_level->active_menubar == this) top_level->active_menubar = 0;
114                 if(top_level->active_popup_menu == this) top_level->active_popup_menu = 0;
115                 if(top_level->active_subwindow == this) top_level->active_subwindow = 0;
116 // drop motion window refs to this
117                 if(top_level->motion_events && top_level->last_motion_win == this->win)
118                         top_level->motion_events = 0;
119
120 // Remove pointer from parent window to this
121                 parent_window->subwindows->remove(this);
122         }
123
124         if(grab_active) grab_active->active_grab = 0;
125         if(icon_window) delete icon_window;
126         if(window_type == POPUP_WINDOW)
127                 parent_window->remove_popup(this);
128
129 // Delete the subwindows
130         if(subwindows)
131         {
132                 while(subwindows->total)
133                 {
134 // Subwindow removes its own pointer
135                         delete subwindows->values[0];
136                 }
137                 delete subwindows;
138         }
139
140         delete pixmap;
141
142 //printf("delete glx=%08x, win=%08x %s\n", (unsigned)glx_win, (unsigned)win, title);
143 #ifdef HAVE_GL
144         if( get_resources()->get_synchronous() && glx_win != 0 ) {
145                 if( window_type == MAIN_WINDOW )
146                         unlock_window();
147                 get_resources()->get_synchronous()->delete_window(this);
148                 if( window_type == MAIN_WINDOW )
149                         lock_window("BC_WindowBase::delete_window");
150         }
151 #endif
152         XDestroyWindow(top_level->display, win);
153
154         if(bg_pixmap && !shared_bg_pixmap) delete bg_pixmap;
155         if(icon_pixmap) delete icon_pixmap;
156         if(temp_bitmap) delete temp_bitmap;
157         top_level->active_bitmaps.remove_buffers(this);
158         if(_7segment_pixmaps)
159         {
160                 for(int i = 0; i < TOTAL_7SEGMENT; i++)
161                         delete _7segment_pixmaps[i];
162
163                 delete [] _7segment_pixmaps;
164         }
165
166
167
168         if(window_type == MAIN_WINDOW)
169         {
170                 XFreeGC(display, gc);
171                 static XFontStruct *BC_WindowBase::*xfont[] = {
172                          &BC_WindowBase::smallfont,
173                          &BC_WindowBase::mediumfont,
174                          &BC_WindowBase::largefont,
175                          &BC_WindowBase::bigfont,
176                          &BC_WindowBase::clockfont,
177                 };
178                 for( int i=sizeof(xfont)/sizeof(xfont[0]); --i>=0; )
179                         XFreeFont(display, this->*xfont[i]);
180
181 #ifdef HAVE_XFT
182 // prevents a bug when Xft closes with unrefd fonts
183                 FcPattern *defaults = FcPatternCreate();
184                 FcPatternAddInteger(defaults, "maxunreffonts", 0);
185                 XftDefaultSet(display, defaults);
186
187                 static void *BC_WindowBase::*xft_font[] = {
188                          &BC_WindowBase::smallfont_xft,
189                          &BC_WindowBase::mediumfont_xft,
190                          &BC_WindowBase::largefont_xft,
191                          &BC_WindowBase::bigfont_xft,
192                          &BC_WindowBase::bold_smallfont_xft,
193                          &BC_WindowBase::bold_mediumfont_xft,
194                          &BC_WindowBase::bold_largefont_xft,
195                          &BC_WindowBase::clockfont_xft,
196                 };
197                 for( int i=sizeof(xft_font)/sizeof(xft_font[0]); --i>=0; ) {
198                         XftFont *xft = (XftFont *)(this->*xft_font[i]);
199                         if( xft ) xftFontClose (display, xft);
200                 }
201 #endif
202                 finit_im();
203                 flush();
204                 sync_display();
205
206                 if( xinerama_info )
207                         XFree(xinerama_info);
208                 xinerama_screens = 0;
209                 xinerama_info = 0;
210 #ifdef HAVE_XV
211                 if( xvideo_port_id >= 0 )
212                         XvUngrabPort(display, xvideo_port_id, CurrentTime);
213 #endif
214                 unlock_window();
215 // Must be last reference to display.
216 // _XftDisplayInfo needs a lock.
217                 get_resources()->create_window_lock->lock("BC_WindowBase::~BC_WindowBase");
218                 XCloseDisplay(display);
219                 get_resources()->create_window_lock->unlock();
220
221 // clipboard uses a different display connection
222                 clipboard->stop_clipboard();
223                 delete clipboard;
224         }
225
226         resize_history.remove_all_objects();
227         delete grab_lock;
228
229 #ifndef SINGLE_THREAD
230         common_events.remove_all_objects();
231         delete event_lock;
232         delete event_condition;
233         delete init_lock;
234 #else
235         top_level->window_lock = 0;
236         BC_Display::unlock_display();
237 #endif
238         delete cursor_timer;
239
240 #if HAVE_GL
241         if( glx_fbcfgs_window ) XFree(glx_fbcfgs_window);
242         if( glx_fbcfgs_pbuffer) XFree(glx_fbcfgs_pbuffer);
243         if( glx_fbcfgs_pixmap ) XFree(glx_fbcfgs_pixmap);
244 #endif
245
246         UNSET_ALL_LOCKS(this)
247 }
248
249 int BC_WindowBase::initialize()
250 {
251         done = 0;
252         done_set = 0;
253         window_running = 0;
254         display_lock_owner = 0;
255         test_keypress = 0;
256         keys_return[0] = 0;
257         context_help_keyword[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         grab_lock = new Mutex("BC_WindowBase::grab_lock");
352
353         cursor_timer = new Timer;
354         event_thread = 0;
355 #ifdef HAVE_GL
356         glx_fbcfgs_window = 0;  n_fbcfgs_window = 0;
357         glx_fbcfgs_pbuffer = 0; n_fbcfgs_pbuffer = 0;
358         glx_fbcfgs_pixmap = 0;  n_fbcfgs_pixmap = 0;
359
360         glx_fb_config = 0;
361         glx_win_context = 0;
362         glx_win = 0;
363 #endif
364
365         flash_enabled = 1;
366         win = 0;
367         return 0;
368 }
369
370
371
372 #define DEFAULT_EVENT_MASKS EnterWindowMask | \
373                         LeaveWindowMask | \
374                         ButtonPressMask | \
375                         ButtonReleaseMask | \
376                         PointerMotionMask | \
377                         FocusChangeMask
378
379
380 int BC_WindowBase::create_window(BC_WindowBase *parent_window, const char *title,
381                 int x, int y, int w, int h, int minw, int minh, int allow_resize,
382                 int private_color, int hide, int bg_color, const char *display_name,
383                 int window_type, BC_Pixmap *bg_pixmap, int group_it)
384 {
385         XSetWindowAttributes attr;
386         unsigned long mask;
387         XSizeHints size_hints;
388         int root_w;
389         int root_h;
390 #ifdef HAVE_LIBXXF86VM
391         int vm;
392 #endif
393
394         id = get_resources()->get_id();
395         if(parent_window) top_level = parent_window->top_level;
396         if( top_level ) lock_window("BC_WindowBase::create_window");
397         get_resources()->create_window_lock->lock("BC_WindowBase::create_window");
398
399 #ifdef HAVE_LIBXXF86VM
400         if(window_type == VIDMODE_SCALED_WINDOW)
401                 closest_vm(&vm,&w,&h);
402 #endif
403
404         this->x = x;
405         this->y = y;
406         this->w = w;
407         this->h = h;
408         this->bg_color = bg_color;
409         this->window_type = window_type;
410         this->hidden = hide;
411         this->private_color = private_color;
412         this->parent_window = parent_window;
413         this->bg_pixmap = bg_pixmap;
414         this->allow_resize = allow_resize;
415         if(display_name)
416                 strcpy(this->display_name, display_name);
417         else
418                 this->display_name[0] = 0;
419
420         put_title(title);
421         if(bg_pixmap) shared_bg_pixmap = 1;
422
423         subwindows = new BC_SubWindowList;
424
425         if(window_type == MAIN_WINDOW)
426         {
427                 top_level = this;
428                 parent_window = this;
429
430
431 #ifdef SINGLE_THREAD
432                 display = BC_Display::get_display(display_name);
433                 BC_Display::lock_display("BC_WindowBase::create_window");
434 //              BC_Display::display_global->new_window(this);
435 #else
436
437 // get the display connection
438
439 // This function must be the first Xlib
440 // function a multi-threaded program calls
441                 XInitThreads();
442                 display = init_display(display_name);
443                 if( shm_completion_event < 0 ) shm_completion_event =
444                         ShmCompletion + XShmGetEventBase(display);
445 #endif
446                 lock_window("BC_WindowBase::create_window 1");
447
448                 screen = DefaultScreen(display);
449                 rootwin = RootWindow(display, screen);
450 // window placement boundaries
451                 if( !xinerama_screens && XineramaIsActive(display) )
452                         xinerama_info = XineramaQueryScreens(display, &xinerama_screens);
453                 root_w = get_root_w(0);
454                 root_h = get_root_h(0);
455
456 #if HAVE_GL
457                 vis = get_glx_visual(display);
458                 if( !vis )
459 #endif
460                 {
461                         int mask = VisualDepthMask | VisualClassMask;
462                         static XVisualInfo vinfo;
463                         memset(&vinfo, 0, sizeof(vinfo));
464                         vinfo.depth = 24;
465                         vinfo.c_class = TrueColor;
466                         int nitems = 0;
467                         XVisualInfo *vis_info = XGetVisualInfo(display, mask, &vinfo, &nitems);
468                         vis = vis_info && nitems>0 ? vis_info[0].visual : 0;
469                         if( vis_info ) XFree(vis_info);
470                 }
471                 if( !vis )
472                         vis = DefaultVisual(display, screen);
473                 default_depth = DefaultDepth(display, screen);
474
475                 client_byte_order = (*(const u_int32_t*)"a   ") & 0x00000001;
476                 server_byte_order = (XImageByteOrder(display) == MSBFirst) ? 0 : 1;
477
478
479 // This must be done before fonts to know if antialiasing is available.
480                 init_colors();
481 // get the resources
482                 if(resources->use_shm < 0) resources->initialize_display(this);
483                 x_correction = BC_DisplayInfo::get_left_border();
484                 y_correction = BC_DisplayInfo::get_top_border();
485
486 // clamp window placement
487                 if(this->x + this->w + x_correction > root_w)
488                         this->x = root_w - this->w - x_correction;
489                 if(this->y + this->h + y_correction > root_h)
490                         this->y = root_h - this->h - y_correction;
491                 if(this->x < 0) this->x = 0;
492                 if(this->y < 0) this->y = 0;
493
494                 if(this->bg_color == -1)
495                         this->bg_color = resources->get_bg_color();
496
497 // printf("bcwindowbase 1 %s\n", title);
498 // if(window_type == MAIN_WINDOW) sleep(1);
499 // printf("bcwindowbase 10\n");
500                 init_fonts();
501                 init_gc();
502                 init_cursors();
503
504 // Create the window
505                 mask = CWEventMask | CWBackPixel | CWColormap | CWCursor;
506
507                 attr.event_mask = DEFAULT_EVENT_MASKS |
508                         StructureNotifyMask |
509                         KeyPressMask |
510                         KeyReleaseMask;
511
512                 attr.background_pixel = get_color(this->bg_color);
513                 attr.colormap = cmap;
514                 attr.cursor = get_cursor_struct(ARROW_CURSOR);
515
516                 win = XCreateWindow(display, rootwin,
517                         this->x, this->y, this->w, this->h, 0,
518                         top_level->default_depth, InputOutput,
519                         vis, mask, &attr);
520                 XGetNormalHints(display, win, &size_hints);
521
522                 size_hints.flags = PSize | PMinSize | PMaxSize;
523                 size_hints.width = this->w;
524                 size_hints.height = this->h;
525                 size_hints.min_width = allow_resize ? minw : this->w;
526                 size_hints.max_width = allow_resize ? 32767 : this->w;
527                 size_hints.min_height = allow_resize ? minh : this->h;
528                 size_hints.max_height = allow_resize ? 32767 : this->h;
529                 if(x > -BC_INFINITY && x < BC_INFINITY)
530                 {
531                         size_hints.flags |= PPosition;
532                         size_hints.x = this->x;
533                         size_hints.y = this->y;
534                 }
535                 XSetWMProperties(display, win, 0, 0, 0, 0, &size_hints, 0, 0);
536                 get_atoms();
537                 set_title(title);
538 #ifndef SINGLE_THREAD
539                 clipboard = new BC_Clipboard(this);
540                 clipboard->start_clipboard();
541 #endif
542
543
544                 if (group_it)
545                 {
546                         Atom ClientLeaderXAtom;
547                         if (XGroupLeader == 0)
548                                 XGroupLeader = win;
549                         const char *instance_name = "cinelerra";
550                         const char *class_name = "Cinelerra";
551                         XClassHint *class_hints = XAllocClassHint();
552                         class_hints->res_name = (char*)instance_name;
553                         class_hints->res_class = (char*)class_name;
554                         XSetClassHint(top_level->display, win, class_hints);
555                         XFree(class_hints);
556                         ClientLeaderXAtom = XInternAtom(display, "WM_CLIENT_LEADER", True);
557                         XChangeProperty(display, win, ClientLeaderXAtom, XA_WINDOW, 32,
558                                 PropModeReplace, (unsigned char *)&XGroupLeader, true);
559                 }
560                 init_im();
561                 set_icon(get_resources()->default_icon);
562         }
563
564 #ifdef HAVE_LIBXXF86VM
565         if(window_type == VIDMODE_SCALED_WINDOW && vm != -1)
566         {
567                 scale_vm (vm);
568                 vm_switched = 1;
569         }
570 #endif
571
572 #ifdef HAVE_LIBXXF86VM
573         if(window_type == POPUP_WINDOW || window_type == VIDMODE_SCALED_WINDOW)
574 #else
575         if(window_type == POPUP_WINDOW)
576 #endif
577         {
578                 mask = CWEventMask | CWBackPixel | CWColormap |
579                         CWOverrideRedirect | CWSaveUnder | CWCursor;
580
581                 attr.event_mask = DEFAULT_EVENT_MASKS | ExposureMask |
582                         KeyPressMask | KeyReleaseMask;
583
584                 if(this->bg_color == -1)
585                         this->bg_color = resources->get_bg_color();
586                 attr.background_pixel = top_level->get_color(bg_color);
587                 attr.colormap = top_level->cmap;
588                 if(top_level->is_hourglass)
589                         attr.cursor = top_level->get_cursor_struct(HOURGLASS_CURSOR);
590                 else
591                         attr.cursor = top_level->get_cursor_struct(ARROW_CURSOR);
592                 attr.override_redirect = True;
593                 attr.save_under = True;
594
595                 win = XCreateWindow(top_level->display,
596                         top_level->rootwin, this->x, this->y, this->w, this->h, 0,
597                         top_level->default_depth, InputOutput, top_level->vis, mask,
598                         &attr);
599                 top_level->add_popup(this);
600         }
601
602         if(window_type == SUB_WINDOW)
603         {
604                 mask = CWEventMask | CWBackPixel | CWCursor;
605                 attr.event_mask = DEFAULT_EVENT_MASKS;
606                 attr.background_pixel = top_level->get_color(this->bg_color);
607                 if(top_level->is_hourglass)
608                         attr.cursor = top_level->get_cursor_struct(HOURGLASS_CURSOR);
609                 else
610                         attr.cursor = top_level->get_cursor_struct(ARROW_CURSOR);
611                 win = XCreateWindow(top_level->display,
612                         parent_window->win, this->x, this->y, this->w, this->h, 0,
613                         top_level->default_depth, InputOutput, top_level->vis, mask,
614                         &attr);
615                 init_window_shape();
616                 if(!hidden) XMapWindow(top_level->display, win);
617         }
618
619 // Create pixmap for all windows
620         pixmap = new BC_Pixmap(this, this->w, this->h);
621
622 // Set up options for main window
623         if(window_type == MAIN_WINDOW)
624         {
625                 if(get_resources()->bg_image && !bg_pixmap && bg_color < 0)
626                 {
627                         this->bg_pixmap = new BC_Pixmap(this,
628                                 get_resources()->bg_image,
629                                 PIXMAP_OPAQUE);
630                 }
631
632                 if(!hidden) show_window();
633                 init_glyphs();
634         }
635
636         draw_background(0, 0, this->w, this->h);
637
638         flash(-1, -1, -1, -1, 0);
639
640 // Set up options for popup window
641 #ifdef HAVE_LIBXXF86VM
642         if(window_type == POPUP_WINDOW || window_type == VIDMODE_SCALED_WINDOW)
643 #else
644         if(window_type == POPUP_WINDOW)
645 #endif
646         {
647                 init_window_shape();
648                 if(!hidden) show_window();
649         }
650         get_resources()->create_window_lock->unlock();
651         unlock_window();
652
653         return 0;
654 }
655
656 Display* BC_WindowBase::init_display(const char *display_name)
657 {
658         Display* display;
659
660         if(display_name && display_name[0] == 0) display_name = NULL;
661         if((display = XOpenDisplay(display_name)) == NULL) {
662                 printf("BC_WindowBase::init_display: cannot connect to X server %s\n",
663                         display_name);
664                 if(getenv("DISPLAY") == NULL) {
665                         printf(_("'DISPLAY' environment variable not set.\n"));
666                         exit(1);
667                 }
668 // Try again with default display.
669                 if((display = XOpenDisplay(0)) == NULL) {
670                         printf("BC_WindowBase::init_display: cannot connect to default X server.\n");
671                         exit(1);
672                 }
673         }
674
675         static int xsynch = -1;
676         if( xsynch < 0 ) {
677                 const char *cp = getenv("CIN_XSYNCH");
678                 xsynch = !cp ? 0 : atoi(cp);
679         }
680         if( xsynch > 0 )
681                 XSynchronize(display, True);
682
683         return display;
684 }
685
686 Display* BC_WindowBase::get_display()
687 {
688         return top_level->display;
689 }
690
691 int BC_WindowBase::get_screen()
692 {
693         return top_level->screen;
694 }
695
696 int BC_WindowBase::run_window()
697 {
698         done_set = done = 0;
699         return_value = 0;
700
701
702 // Events may have been sent before run_window so can't initialize them here.
703
704 #ifdef SINGLE_THREAD
705         set_repeat(get_resources()->tooltip_delay);
706         BC_Display::display_global->new_window(this);
707
708 // If the first window created, run the display loop in this thread.
709         if(BC_Display::display_global->is_first(this))
710         {
711                 BC_Display::unlock_display();
712                 BC_Display::display_global->loop();
713         }
714         else
715         {
716                 BC_Display::unlock_display();
717                 completion_lock->lock("BC_WindowBase::run_window");
718         }
719
720         BC_Display::lock_display("BC_WindowBase::run_window");
721         BC_Display::display_global->delete_window(this);
722
723         unset_all_repeaters();
724         hide_tooltip();
725         BC_Display::unlock_display();
726
727 #else // SINGLE_THREAD
728
729
730
731 // Start tooltips
732         set_repeat(get_resources()->tooltip_delay);
733
734 // Start X server events
735         event_thread = new BC_WindowEvents(this);
736         event_thread->start();
737
738 // Release wait lock
739         window_running = 1;
740         init_lock->unlock();
741
742 // Handle common events
743         while( !done ) {
744                 dispatch_event();
745         }
746
747         unset_all_repeaters();
748         hide_tooltip();
749         delete event_thread;
750         event_thread = 0;
751         event_condition->reset();
752         common_events.remove_all_objects();
753         window_running = 0;
754         done = 0;
755
756 #endif // SINGLE_THREAD
757
758         return return_value;
759 }
760
761 int BC_WindowBase::get_key_masks(unsigned int key_state)
762 {
763 // printf("BC_WindowBase::get_key_masks %llx\n",
764 // event->xkey.state);
765         ctrl_mask = (key_state & ControlMask) ? 1 : 0;  // ctrl key down
766         shift_mask = (key_state & ShiftMask) ? 1 : 0;   // shift key down
767         alt_mask = (key_state & Mod1Mask) ? 1 : 0;      // alt key down
768         return 0;
769 }
770
771
772 void BC_WindowBase::add_keyboard_listener(int(BC_WindowBase::*handler)(BC_WindowBase *))
773 {
774         BC_KeyboardHandlerLock set;
775         BC_KeyboardHandler::listeners.append(new BC_KeyboardHandler(handler, this));
776 }
777
778 void BC_WindowBase::del_keyboard_listener(int(BC_WindowBase::*handler)(BC_WindowBase *))
779 {
780         BC_KeyboardHandlerLock set;
781         int i = BC_KeyboardHandler::listeners.size();
782         while( --i >= 0 && BC_KeyboardHandler::listeners[i]->handler!=handler );
783         if( i >= 0 ) BC_KeyboardHandler::listeners.remove_object_number(i);
784 }
785
786 int BC_KeyboardHandler::run_event(BC_WindowBase *wp)
787 {
788         int result = (win->*handler)(wp);
789         return result;
790 }
791
792 int BC_KeyboardHandler::run_listeners(BC_WindowBase *wp)
793 {
794         int result = 0;
795         BC_KeyboardHandlerLock set;
796         for( int i=0; !result && i<listeners.size(); ++i ) {
797                 BC_KeyboardHandler *listener = listeners[i];
798                 result = listener->run_event(wp);
799         }
800         return result;
801 }
802
803 void BC_KeyboardHandler::kill_grabs()
804 {
805         BC_KeyboardHandlerLock set;
806         for( int i=0; i<listeners.size(); ++i ) {
807                 BC_WindowBase *win = listeners[i]->win;
808                 if( win->get_window_type() != POPUP_WINDOW ) continue;
809                 ((BC_Popup *)win)->ungrab_keyboard();
810         }
811 }
812
813 void BC_ActiveBitmaps::reque(XEvent *event)
814 {
815         XShmCompletionEvent *shm_ev = (XShmCompletionEvent *)event;
816         ShmSeg shmseg = shm_ev->shmseg;
817         Drawable drawable = shm_ev->drawable;
818 //printf("BC_BitmapImage::reque %08lx\n",shmseg);
819         active_lock.lock("BC_BitmapImage::reque");
820         BC_BitmapImage *bfr = first;
821         while( bfr && bfr->get_shmseg() != shmseg ) bfr = bfr->next;
822         if( bfr && bfr->drawable == drawable )
823                 remove_pointer(bfr);
824         active_lock.unlock();
825         if( !bfr ) {
826 // sadly, X reports two drawable completions and creates false reporting, so no boobytrap
827 //              printf("BC_BitmapImage::reque missed shmseg %08x, drawable %08x\n",
828 //                       (int)shmseg, (int)drawable);
829                 return;
830         }
831         if( bfr->drawable != drawable ) return;
832         if( bfr->is_zombie() ) { --BC_Bitmap::zombies; delete bfr; return; }
833         bfr->bitmap->reque(bfr);
834 }
835
836 void BC_ActiveBitmaps::insert(BC_BitmapImage *bfr, Drawable pixmap)
837 {
838         active_lock.lock("BC_BitmapImage::insert");
839         bfr->drawable = pixmap;
840         append(bfr);
841         active_lock.unlock();
842 }
843
844 void BC_ActiveBitmaps::remove_buffers(BC_WindowBase *wdw)
845 {
846         active_lock.lock("BC_ActiveBitmaps::remove");
847         for( BC_BitmapImage *nxt=0, *bfr=first; bfr; bfr=nxt ) {
848                 nxt = bfr->next;
849                 if( bfr->is_zombie() ) { --BC_Bitmap::zombies; delete bfr; continue; }
850                 if( bfr->bitmap->parent_window == wdw ) remove_pointer(bfr);
851         }
852         active_lock.unlock();
853 }
854
855 BC_ActiveBitmaps::BC_ActiveBitmaps()
856 {
857 }
858
859 BC_ActiveBitmaps::~BC_ActiveBitmaps()
860 {
861 }
862
863
864
865 int BC_WindowBase::keysym_lookup(XEvent *event)
866 {
867         for( int i = 0; i < KEYPRESSLEN; ++i ) keys_return[i] = 0;
868         for( int i = 0; i < 4; ++i ) wkey_string[i] = 0;
869
870         if( event->xany.send_event && !event->xany.serial ) {
871                 keysym = (KeySym) event->xkey.keycode;
872                 keys_return[0] = keysym;
873                 return 0;
874         }
875         wkey_string_length = 0;
876
877         if( input_context ) {
878                 wchar_t wkey[4];
879                 wkey_string_length = XwcLookupString(input_context,
880                         (XKeyEvent*)event, wkey, 4, &keysym, 0);
881                 for( int i=0; i<wkey_string_length; ++i )
882                         wkey_string[i] = wkey[i];
883 //printf("keysym_lookup 1 %d %d %lx %x %x %x %x\n", wkey_string_length, keysym,
884 //  wkey_string[0], wkey_string[1], wkey_string[2], wkey_string[3]);
885
886                 Status stat;
887                 int ret = Xutf8LookupString(input_context, (XKeyEvent*)event,
888                                 keys_return, KEYPRESSLEN, &keysym, &stat);
889 //printf("keysym_lookup 2 %d %d %lx %x %x\n", ret, stat, keysym, keys_return[0], keys_return[1]);
890                 if( stat == XLookupBoth ) return ret;
891                 if( stat == XLookupKeySym ) return 0;
892         }
893         int ret = XLookupString((XKeyEvent*)event, keys_return, KEYPRESSLEN, &keysym, 0);
894         wkey_string_length = ret;
895         for( int i=0; i<ret; ++i ) wkey_string[i] = keys_return[i];
896         return ret;
897 }
898
899 pthread_t locking_task = (pthread_t)-1L;
900 int locking_event = -1;
901 int locking_message = -1;
902
903 int BC_WindowBase::dispatch_event()
904 {
905         Window tempwin;
906         int result;
907         XClientMessageEvent *ptr;
908         int cancel_resize, cancel_translation;
909         volatile static int debug = 0;
910         XEvent *event;
911
912         key_pressed = 0;
913
914 #ifndef SINGLE_THREAD
915 // If an event is waiting get it, otherwise
916 // wait for next event only if there are no compressed events.
917         if(get_event_count() ||
918                 (!motion_events && !resize_events && !translation_events))
919         {
920                 event = get_event();
921 // Lock out window deletions
922                 lock_window("BC_WindowBase::dispatch_event 1");
923 locking_event = event->type;
924 locking_task = pthread_self();
925 locking_message = event->xclient.message_type;
926         }
927         else
928 // Handle compressed events
929         {
930                 lock_window("BC_WindowBase::dispatch_event 2");
931                 if(resize_events)
932                         dispatch_resize_event(last_resize_w, last_resize_h);
933                 if(motion_events)
934                         dispatch_motion_event();
935                 if(translation_events)
936                         dispatch_translation_event();
937
938                 unlock_window();
939                 return 0;
940         }
941
942 #endif
943
944
945
946
947 if( debug && event->type != ClientMessage ) {
948  static const char *event_names[] = {
949   "Reply", "Error", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease", "MotionNotify",
950   "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut", "KeymapNotify", "Expose", "GraphicsExpose",
951   "NoExpose", "VisibilityNotify", "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify",
952   "MapRequest", "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
953   "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify", "SelectionClear",
954   "SelectionRequest", "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify",
955   "GenericEvent", "LASTEvent",
956  };
957  const int nevents = sizeof(event_names)/sizeof(event_names[0]);
958
959  printf("BC_WindowBase::dispatch_event %d %s %p %d (%s)\n", __LINE__,
960   title, event, event->type, event->type>=0 && event->type<nevents ?
961    event_names[event->type] : "Unknown");
962 }
963
964         if( active_grab ) {
965                 grab_lock->lock("BC_WindowBase::dispatch_event 3");
966                 unlock_window();
967                 result = 0;
968                 if( active_grab ) {
969                         active_grab->lock_window("BC_WindowBase::dispatch_event 3");
970                         result = active_grab->grab_event(event);
971                         active_grab->unlock_window();
972                 }
973                 grab_lock->unlock();
974                 if( result ) return result;
975                 lock_window("BC_WindowBase::dispatch_event 4");
976         }
977
978         switch(event->type) {
979         case ClientMessage:
980 // Clear the resize buffer
981                 if( resize_events )
982                         dispatch_resize_event(last_resize_w, last_resize_h);
983 // Clear the motion buffer since this can clear the window
984                 if( motion_events )
985                         dispatch_motion_event();
986
987                 ptr = (XClientMessageEvent*)event;
988                 if( ptr->message_type == ProtoXAtom &&
989                     (Atom)ptr->data.l[0] == DelWinXAtom ) {
990                         close_event();
991                 }
992                 else if( ptr->message_type == RepeaterXAtom ) {
993                         dispatch_repeat_event(ptr->data.l[0]);
994                 }
995                 else if( ptr->message_type == SetDoneXAtom ) {
996                         done = 1;
997                 }
998                 else {
999                         receive_custom_xatoms((xatom_event *)ptr);
1000                 }
1001                 break;
1002
1003         case FocusIn:
1004                 has_focus = 1;
1005                 dispatch_focus_in();
1006                 break;
1007
1008         case FocusOut:
1009                 has_focus = 0;
1010                 dispatch_focus_out();
1011                 break;
1012
1013 // Maximized
1014         case MapNotify:
1015                 break;
1016
1017 // Minimized
1018         case UnmapNotify:
1019                 break;
1020
1021         case ButtonPress:
1022                 if(motion_events)
1023                 {
1024                         dispatch_motion_event();
1025                 }
1026                 get_key_masks(event->xbutton.state);
1027                 cursor_x = event->xbutton.x;
1028                 cursor_y = event->xbutton.y;
1029                 button_number = event->xbutton.button;
1030
1031 //printf("BC_WindowBase::dispatch_event %d %d\n", __LINE__, button_number);
1032                 event_win = event->xany.window;
1033                 if( button_number < 6 ) {
1034                         if( button_number < 4 )
1035                                 button_down = 1;
1036                         button_pressed = event->xbutton.button;
1037                         button_time1 = button_time2;
1038                         button_time2 = button_time3;
1039                         button_time3 = event->xbutton.time;
1040                         drag_x = cursor_x;
1041                         drag_y = cursor_y;
1042                         drag_win = event_win;
1043                         drag_x1 = cursor_x - get_resources()->drag_radius;
1044                         drag_x2 = cursor_x + get_resources()->drag_radius;
1045                         drag_y1 = cursor_y - get_resources()->drag_radius;
1046                         drag_y2 = cursor_y + get_resources()->drag_radius;
1047                         if( button_number < 4 ) {
1048                                 if((long)(button_time3 - button_time1) < resources->double_click * 2) {
1049                                         triple_click = 1;
1050                                         button_time3 = button_time2 = button_time1 = 0;
1051                                 }
1052                                 if((long)(button_time3 - button_time2) < resources->double_click) {
1053                                         double_click = 1;
1054 //                                      button_time3 = button_time2 = button_time1 = 0;
1055                                 }
1056                                 else {
1057                                         triple_click = 0;
1058                                         double_click = 0;
1059                                 }
1060                         }
1061                         dispatch_button_press();
1062                 }
1063                 break;
1064
1065         case ButtonRelease:
1066                 if(motion_events)
1067                 {
1068                         dispatch_motion_event();
1069                 }
1070                 get_key_masks(event->xbutton.state);
1071                 button_number = event->xbutton.button;
1072                 event_win = event->xany.window;
1073                 if (button_number < 6)
1074                 {
1075                         if(button_number < 4)
1076                                 button_down = 0;
1077 //printf("BC_WindowBase::dispatch_event %d %d\n", __LINE__, button_number);
1078
1079                         dispatch_button_release();
1080                 }
1081                 break;
1082
1083         case Expose:
1084                 event_win = event->xany.window;
1085                 result = 0;
1086                 for( int i=0; !result && i<popups.size(); ++i ) {  // popups take focus
1087                         if( popups[i]->win == event_win )
1088                                 result = popups[i]->dispatch_expose_event();
1089                 }
1090                 if( !result )
1091                         result = dispatch_expose_event();
1092                 break;
1093
1094         case MotionNotify:
1095                 get_key_masks(event->xmotion.state);
1096 // Dispatch previous motion event if this is a subsequent motion from a different window
1097                 if(motion_events && last_motion_win != event->xany.window)
1098                 {
1099                         dispatch_motion_event();
1100                 }
1101
1102 // Buffer the current motion
1103                 motion_events = 1;
1104                 last_motion_state = event->xmotion.state;
1105                 last_motion_x = event->xmotion.x;
1106                 last_motion_y = event->xmotion.y;
1107                 last_motion_win = event->xany.window;
1108                 break;
1109
1110         case ConfigureNotify:
1111 // printf("BC_WindowBase::dispatch_event %d win=%p this->win=%p\n",
1112 // __LINE__,
1113 // event->xany.window,
1114 // win);
1115 // dump_windows();
1116                 XTranslateCoordinates(top_level->display,
1117                         top_level->win,
1118                         top_level->rootwin,
1119                         0,
1120                         0,
1121                         &last_translate_x,
1122                         &last_translate_y,
1123                         &tempwin);
1124                 last_resize_w = event->xconfigure.width;
1125                 last_resize_h = event->xconfigure.height;
1126
1127                 cancel_resize = 0;
1128                 cancel_translation = 0;
1129
1130 // Resize history prevents responses to recursive resize requests
1131                 for(int i = 0; i < resize_history.total && !cancel_resize; i++)
1132                 {
1133                         if(resize_history.values[i]->w == last_resize_w &&
1134                                 resize_history.values[i]->h == last_resize_h)
1135                         {
1136                                 delete resize_history.values[i];
1137                                 resize_history.remove_number(i);
1138                                 cancel_resize = 1;
1139                         }
1140                 }
1141
1142                 if(last_resize_w == w && last_resize_h == h)
1143                         cancel_resize = 1;
1144
1145                 if(!cancel_resize)
1146                 {
1147                         resize_events = 1;
1148                 }
1149
1150                 if((last_translate_x == x && last_translate_y == y))
1151                         cancel_translation = 1;
1152
1153                 if(!cancel_translation)
1154                 {
1155                         translation_events = 1;
1156                 }
1157
1158                 translation_count++;
1159                 break;
1160
1161         case KeyPress:
1162                 get_key_masks(event->xkey.state);
1163                 keys_return[0] = 0;  keysym = -1;
1164                 if(XFilterEvent(event, win)) {
1165                         break;
1166                 }
1167                 if( keysym_lookup(event) < 0 ) {
1168                         printf("keysym %x\n", (uint32_t)keysym);
1169                         break;
1170                 }
1171
1172 //printf("BC_WindowBase::dispatch_event %d keysym=0x%x\n",
1173 //__LINE__,
1174 //keysym);
1175
1176 // block out control keys
1177                 if(keysym > 0xffe0 && keysym < 0xffff) break;
1178 // block out Alt_GR key
1179                 if(keysym == 0xfe03) break;
1180
1181                 if(test_keypress)
1182                          printf("BC_WindowBase::dispatch_event %x\n", (uint32_t)keysym);
1183
1184 #ifdef X_HAVE_UTF8_STRING
1185 //It's Ascii or UTF8?
1186 //              if (keysym != 0xffff && (keys_return[0] & 0xff) >= 0x7f )
1187 //printf("BC_WindowBase::dispatch_event %d %02x%02x\n", __LINE__, keys_return[0], keys_return[1]);
1188
1189                 if( ((keys_return[1] & 0xff) > 0x80) &&
1190                     ((keys_return[0] & 0xff) > 0xC0) ) {
1191 //printf("BC_WindowBase::dispatch_event %d\n", __LINE__);
1192                         key_pressed = keysym & 0xff;
1193                 }
1194                 else {
1195 #endif
1196 // shuttle speed codes
1197                 if( keysym >= SKEY_MIN && keysym <= SKEY_MAX ) {
1198                         key_pressed = keysym;
1199                 }
1200                 else switch( keysym ) {
1201 // block out extra keys
1202                 case XK_Alt_L:
1203                 case XK_Alt_R:
1204                 case XK_Shift_L:
1205                 case XK_Shift_R:
1206                 case XK_Control_L:
1207                 case XK_Control_R:
1208                         key_pressed = 0;
1209                         break;
1210
1211 // Translate key codes
1212                 case XK_Return:         key_pressed = RETURN;   break;
1213                 case XK_Up:             key_pressed = UP;       break;
1214                 case XK_Down:           key_pressed = DOWN;     break;
1215                 case XK_Left:           key_pressed = LEFT;     break;
1216                 case XK_Right:          key_pressed = RIGHT;    break;
1217                 case XK_Next:           key_pressed = PGDN;     break;
1218                 case XK_Prior:          key_pressed = PGUP;     break;
1219                 case XK_BackSpace:      key_pressed = BACKSPACE; break;
1220                 case XK_Escape:         key_pressed = ESC;      break;
1221                 case XK_Tab:
1222                         if(shift_down())
1223                                 key_pressed = LEFTTAB;
1224                         else
1225                                 key_pressed = TAB;
1226                         break;
1227                 case XK_ISO_Left_Tab:   key_pressed = LEFTTAB;  break;
1228                 case XK_underscore:     key_pressed = '_';      break;
1229                 case XK_asciitilde:     key_pressed = '~';      break;
1230                 case XK_Delete:         key_pressed = DELETE;   break;
1231                 case XK_Home:           key_pressed = HOME;     break;
1232                 case XK_End:            key_pressed = END;      break;
1233
1234 // number pad
1235                 case XK_KP_Enter:       key_pressed = KPENTER;  break;
1236                 case XK_KP_Add:         key_pressed = KPPLUS;   break;
1237                 case XK_KP_Subtract:    key_pressed = KPMINUS;  break;
1238                 case XK_KP_Multiply:    key_pressed = KPSTAR;   break;
1239                 case XK_KP_Divide:      key_pressed = KPSLASH;  break;
1240                 case XK_KP_1:
1241                 case XK_KP_End:         key_pressed = KP1;      break;
1242                 case XK_KP_2:
1243                 case XK_KP_Down:        key_pressed = KP2;      break;
1244                 case XK_KP_3:
1245                 case XK_KP_Page_Down:   key_pressed = KP3;      break;
1246                 case XK_KP_4:
1247                 case XK_KP_Left:        key_pressed = KP4;      break;
1248                 case XK_KP_5:
1249                 case XK_KP_Begin:       key_pressed = KP5;      break;
1250                 case XK_KP_6:
1251                 case XK_KP_Right:       key_pressed = KP6;      break;
1252                 case XK_KP_7:
1253                 case XK_KP_Home:        key_pressed = KP7;      break;
1254                 case XK_KP_8:
1255                 case XK_KP_Up:          key_pressed = KP8;      break;
1256                 case XK_KP_9:
1257                 case XK_KP_Page_Up:     key_pressed = KP9;      break;
1258                 case XK_KP_0:
1259                 case XK_KP_Insert:      key_pressed = KPINS;    break;
1260                 case XK_KP_Decimal:
1261                 case XK_KP_Delete:      key_pressed = KPDEL;    break;
1262
1263                 case XK_F1:             key_pressed = KEY_F1;   break;
1264                 case XK_F2:             key_pressed = KEY_F2;   break;
1265                 case XK_F3:             key_pressed = KEY_F3;   break;
1266                 case XK_F4:             key_pressed = KEY_F4;   break;
1267                 case XK_F5:             key_pressed = KEY_F5;   break;
1268                 case XK_F6:             key_pressed = KEY_F6;   break;
1269                 case XK_F7:             key_pressed = KEY_F7;   break;
1270                 case XK_F8:             key_pressed = KEY_F8;   break;
1271                 case XK_F9:             key_pressed = KEY_F9;   break;
1272                 case XK_F10:            key_pressed = KEY_F10;  break;
1273                 case XK_F11:            key_pressed = KEY_F11;  break;
1274                 case XK_F12:            key_pressed = KEY_F12;  break;
1275 // activates remote
1276                 case XK_Menu:           key_pressed = KPMENU;   break;  /* menu */
1277
1278                 default:
1279                         key_pressed = keysym & 0xff;
1280 #ifdef X_HAVE_UTF8_STRING
1281 //printf("BC_WindowBase::dispatch_event %d\n", __LINE__);
1282                         keys_return[1] = 0;
1283 #endif
1284                         break;
1285                 }
1286 #ifdef X_HAVE_UTF8_STRING
1287                 }
1288                 key_pressed_utf8 = keys_return;
1289 #endif
1290
1291
1292                 result = 0;
1293                 if( top_level == this )
1294                         result = BC_KeyboardHandler::run_listeners(this);
1295
1296 //printf("BC_WindowBase::dispatch_event %d %d %x\n", shift_down(), alt_down(), key_pressed);
1297                 if( !result )
1298                         result = dispatch_keypress_event();
1299 // Handle some default keypresses
1300                 if(!result)
1301                 {
1302                         if(key_pressed == 'w' ||
1303                                 key_pressed == 'W')
1304                         {
1305                                 close_event();
1306                         }
1307                 }
1308                 break;
1309
1310         case KeyRelease:
1311                 XLookupString((XKeyEvent*)event, keys_return, 1, &keysym, 0);
1312                 dispatch_keyrelease_event();
1313 // printf("BC_WindowBase::dispatch_event KeyRelease keysym=0x%x keystate=0x%lld\n",
1314 // keysym, event->xkey.state);
1315                 break;
1316
1317         case LeaveNotify:
1318                 if( event->xcrossing.mode != NotifyNormal ) break;
1319                 cursor_entered = 0;
1320                 event_win = event->xany.window;
1321                 dispatch_cursor_leave();
1322                 break;
1323
1324         case EnterNotify:
1325                 if( event->xcrossing.mode != NotifyNormal ) break;
1326
1327                 if( !cursor_entered ) {
1328                         for( int i=0; i<popups.size(); ++i ) {  // popups always take focus
1329                                 if( popups[i]->win == event->xcrossing.window )
1330                                 cursor_entered = 1;
1331                         }
1332                         if( !cursor_entered && get_resources()->grab_input_focus &&
1333                             !event->xcrossing.focus && event->xcrossing.window == win ) {
1334                                 cursor_entered = 1;
1335                         }
1336                         if( cursor_entered )
1337                                 focus();
1338                 }
1339                 event_win = event->xany.window;
1340                 cursor_x = event->xcrossing.x;
1341                 cursor_y = event->xcrossing.y;
1342                 dispatch_cursor_enter();
1343                 break;
1344
1345         default:
1346                 break;
1347         }
1348 //printf("100 %s %p %d\n", title, event, event->type);
1349 //if(event->type != ClientMessage) dump();
1350
1351 #ifndef SINGLE_THREAD
1352         unlock_window();
1353         if(event) {
1354                 if( resend_event_window ) {
1355                         resend_event_window->put_event(event);
1356                         resend_event_window = 0;
1357                 }
1358                 else
1359                         delete event;
1360         }
1361 #else
1362 //      if(done) completion_lock->unlock();
1363 #endif
1364
1365 if(debug) printf("BC_WindowBase::dispatch_event this=%p %d\n", this, __LINE__);
1366         return 0;
1367 }
1368
1369 int BC_WindowBase::dispatch_expose_event()
1370 {
1371         int result = 0;
1372         for(int i = 0; i < subwindows->total && !result; i++)
1373         {
1374                 result = subwindows->values[i]->dispatch_expose_event();
1375         }
1376
1377 // Propagate to user
1378         if(!result) expose_event();
1379         return result;
1380 }
1381
1382 int BC_WindowBase::dispatch_resize_event(int w, int h)
1383 {
1384 // Can't store new w and h until the event is handles
1385 // because bcfilebox depends on the old w and h to
1386 // reposition widgets.
1387         if( window_type == MAIN_WINDOW ) {
1388                 flash_enabled = 0;
1389                 resize_events = 0;
1390
1391                 delete pixmap;
1392                 pixmap = new BC_Pixmap(this, w, h);
1393                 clear_box(0, 0, w, h);
1394         }
1395
1396 // Propagate to subwindows
1397         for(int i = 0; i < subwindows->total; i++) {
1398                 subwindows->values[i]->dispatch_resize_event(w, h);
1399         }
1400
1401 // Propagate to user
1402         resize_event(w, h);
1403
1404         if( window_type == MAIN_WINDOW ) {
1405                 this->w = w;
1406                 this->h = h;
1407                 dispatch_flash();
1408                 flush();
1409         }
1410         return 0;
1411 }
1412
1413 int BC_WindowBase::dispatch_flash()
1414 {
1415         flash_enabled = 1;
1416         for(int i = 0; i < subwindows->total; i++)
1417                 subwindows->values[i]->dispatch_flash();
1418         return flash(0);
1419 }
1420
1421 int BC_WindowBase::dispatch_translation_event()
1422 {
1423         translation_events = 0;
1424         if(window_type == MAIN_WINDOW)
1425         {
1426                 prev_x = x;
1427                 prev_y = y;
1428                 x = last_translate_x;
1429                 y = last_translate_y;
1430 // Correct for window manager offsets
1431                 x -= x_correction;
1432                 y -= y_correction;
1433         }
1434
1435         for(int i = 0; i < subwindows->total; i++)
1436         {
1437                 subwindows->values[i]->dispatch_translation_event();
1438         }
1439
1440         translation_event();
1441         return 0;
1442 }
1443
1444 int BC_WindowBase::dispatch_motion_event()
1445 {
1446         int result = 0;
1447         unhide_cursor();
1448
1449         if(top_level == this)
1450         {
1451                 motion_events = 0;
1452                 event_win = last_motion_win;
1453                 get_key_masks(last_motion_state);
1454
1455 // Test for grab
1456                 if(get_button_down() && !active_menubar && !active_popup_menu)
1457                 {
1458                         if(!result)
1459                         {
1460                                 cursor_x = last_motion_x;
1461                                 cursor_y = last_motion_y;
1462                                 result = dispatch_drag_motion();
1463                         }
1464
1465                         if(!result &&
1466                                 (last_motion_x < drag_x1 || last_motion_x >= drag_x2 ||
1467                                 last_motion_y < drag_y1 || last_motion_y >= drag_y2))
1468                         {
1469                                 cursor_x = drag_x;
1470                                 cursor_y = drag_y;
1471
1472                                 result = dispatch_drag_start();
1473                         }
1474                 }
1475
1476                 cursor_x = last_motion_x;
1477                 cursor_y = last_motion_y;
1478
1479 // printf("BC_WindowBase::dispatch_motion_event %d %p %p %p\n",
1480 // __LINE__,
1481 // active_menubar,
1482 // active_popup_menu,
1483 // active_subwindow);
1484
1485                 if(active_menubar && !result) result = active_menubar->dispatch_motion_event();
1486                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_motion_event();
1487                 if(active_subwindow && !result) result = active_subwindow->dispatch_motion_event();
1488         }
1489
1490 // Dispatch in stacking order
1491         for(int i = subwindows->size() - 1; i >= 0 && !result; i--)
1492         {
1493                 result = subwindows->values[i]->dispatch_motion_event();
1494         }
1495
1496         if(!result) result = cursor_motion_event();    // give to user
1497         return result;
1498 }
1499
1500 int BC_WindowBase::dispatch_keypress_event()
1501 {
1502         int result = 0;
1503         if(top_level == this)
1504         {
1505                 if(active_subwindow) result = active_subwindow->dispatch_keypress_event();
1506         }
1507
1508         for(int i = 0; i < subwindows->total && !result; i++)
1509         {
1510                 result = subwindows->values[i]->dispatch_keypress_event();
1511         }
1512
1513         if(!result) result = keypress_event();
1514
1515         return result;
1516 }
1517
1518 int BC_WindowBase::dispatch_keyrelease_event()
1519 {
1520         int result = 0;
1521         if(top_level == this)
1522         {
1523                 if(active_subwindow) result = active_subwindow->dispatch_keyrelease_event();
1524         }
1525
1526         for(int i = 0; i < subwindows->total && !result; i++)
1527         {
1528                 result = subwindows->values[i]->dispatch_keyrelease_event();
1529         }
1530
1531         if(!result) result = keyrelease_event();
1532
1533         return result;
1534 }
1535
1536 int BC_WindowBase::dispatch_focus_in()
1537 {
1538         for(int i = 0; i < subwindows->total; i++)
1539         {
1540                 subwindows->values[i]->dispatch_focus_in();
1541         }
1542
1543         focus_in_event();
1544
1545         return 0;
1546 }
1547
1548 int BC_WindowBase::dispatch_focus_out()
1549 {
1550         for(int i = 0; i < subwindows->total; i++)
1551         {
1552                 subwindows->values[i]->dispatch_focus_out();
1553         }
1554
1555         focus_out_event();
1556
1557         return 0;
1558 }
1559
1560 int BC_WindowBase::get_has_focus()
1561 {
1562         return top_level->has_focus;
1563 }
1564
1565 int BC_WindowBase::get_deleting()
1566 {
1567         if(is_deleting) return 1;
1568         if(parent_window && parent_window->get_deleting()) return 1;
1569         return 0;
1570 }
1571
1572 int BC_WindowBase::dispatch_button_press()
1573 {
1574         int result = 0;
1575
1576
1577         if(top_level == this)
1578         {
1579                 if(active_menubar) result = active_menubar->dispatch_button_press();
1580                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_button_press();
1581                 if(active_subwindow && !result) result = active_subwindow->dispatch_button_press();
1582         }
1583
1584         for(int i = 0; i < subwindows->total && !result; i++)
1585         {
1586                 result = subwindows->values[i]->dispatch_button_press();
1587         }
1588
1589         if(!result) result = button_press_event();
1590
1591
1592         return result;
1593 }
1594
1595 int BC_WindowBase::dispatch_button_release()
1596 {
1597         int result = 0;
1598         if(top_level == this)
1599         {
1600                 if(active_menubar) result = active_menubar->dispatch_button_release();
1601                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_button_release();
1602                 if(active_subwindow && !result) result = active_subwindow->dispatch_button_release();
1603                 if(!result && button_number != 4 && button_number != 5)
1604                         result = dispatch_drag_stop();
1605         }
1606
1607         for(int i = 0; i < subwindows->total && !result; i++)
1608         {
1609                 result = subwindows->values[i]->dispatch_button_release();
1610         }
1611
1612         if(!result)
1613         {
1614                 result = button_release_event();
1615         }
1616
1617         return result;
1618 }
1619
1620
1621 int BC_WindowBase::dispatch_repeat_event(int64_t duration)
1622 {
1623
1624 // all repeat event handlers get called and decide based on activity and duration
1625 // whether to respond
1626         for(int i = 0; i < subwindows->total; i++)
1627         {
1628                 subwindows->values[i]->dispatch_repeat_event(duration);
1629         }
1630
1631
1632         repeat_event(duration);
1633
1634
1635
1636 // Unlock next repeat signal
1637         if(window_type == MAIN_WINDOW)
1638         {
1639 #ifdef SINGLE_THREAD
1640                 BC_Display::display_global->unlock_repeaters(duration);
1641 #else
1642                 for(int i = 0; i < repeaters.total; i++)
1643                 {
1644                         if(repeaters.values[i]->delay == duration)
1645                         {
1646                                 repeaters.values[i]->repeat_lock->unlock();
1647                         }
1648                 }
1649 #endif
1650         }
1651         return 0;
1652 }
1653
1654 void BC_WindowBase::unhide_cursor()
1655 {
1656         if(is_transparent)
1657         {
1658                 is_transparent = 0;
1659                 if(top_level->is_hourglass)
1660                         set_cursor(HOURGLASS_CURSOR, 1, 0);
1661                 else
1662                         set_cursor(current_cursor, 1, 0);
1663         }
1664         cursor_timer->update();
1665 }
1666
1667
1668 void BC_WindowBase::update_video_cursor()
1669 {
1670         if(video_on && !is_transparent)
1671         {
1672                 if(cursor_timer->get_difference() > VIDEO_CURSOR_TIMEOUT && !is_transparent)
1673                 {
1674                         is_transparent = 1;
1675                         set_cursor(TRANSPARENT_CURSOR, 1, 1);
1676                         cursor_timer->update();
1677                 }
1678         }
1679         else
1680         {
1681                 cursor_timer->update();
1682         }
1683 }
1684
1685
1686 int BC_WindowBase::dispatch_cursor_leave()
1687 {
1688         unhide_cursor();
1689
1690         for(int i = 0; i < subwindows->total; i++)
1691         {
1692                 subwindows->values[i]->dispatch_cursor_leave();
1693         }
1694
1695         cursor_leave_event();
1696         return 0;
1697 }
1698
1699 int BC_WindowBase::dispatch_cursor_enter()
1700 {
1701         int result = 0;
1702
1703         unhide_cursor();
1704
1705         if(active_menubar) result = active_menubar->dispatch_cursor_enter();
1706         if(!result && active_popup_menu) result = active_popup_menu->dispatch_cursor_enter();
1707         if(!result && active_subwindow) result = active_subwindow->dispatch_cursor_enter();
1708
1709         for(int i = 0; !result && i < subwindows->total; i++)
1710         {
1711                 result = subwindows->values[i]->dispatch_cursor_enter();
1712         }
1713
1714         if(!result) result = cursor_enter_event();
1715         return result;
1716 }
1717
1718 int BC_WindowBase::cursor_enter_event()
1719 {
1720         return 0;
1721 }
1722
1723 int BC_WindowBase::cursor_leave_event()
1724 {
1725         return 0;
1726 }
1727
1728 int BC_WindowBase::close_event()
1729 {
1730         set_done(1);
1731         return 1;
1732 }
1733
1734 // *** CONTEXT_HELP ***
1735 // This basic implementation serves solely for context help.
1736 // We are handling Alt/H only. Any subclass requiring more sophisticated
1737 // processing of keystrokes has to provide its own overloaded handler.
1738 int BC_WindowBase::keypress_event()
1739 {
1740 //      printf("BC_WindowBase::keypress_event: %d\n", get_keypress());
1741         return context_help_check_and_show();
1742 }
1743
1744 // The stuff up to END_CONTEXT_HELP serves solely for context help
1745 void BC_WindowBase::context_help_set_keyword(const char *keyword)
1746 {
1747         if (keyword) strcpy(context_help_keyword, keyword);
1748 }
1749
1750 const char *BC_WindowBase::context_help_get_keyword()
1751 {
1752 // If we have context_help_keyword defined here, return it.
1753 // Otherwise recursively track widget hierarchy up to top_level
1754 // and return the nearest found nonempty keyword.
1755 // If nothing found, the special keyword "TOC" is returned.
1756         if (context_help_keyword[0] || this == top_level ||
1757             this == parent_window || ! parent_window) {
1758                 if (! context_help_keyword[0] && this == top_level)
1759                         context_help_set_keyword("TOC");
1760                 return context_help_keyword;
1761         }
1762         return parent_window->context_help_get_keyword();
1763 }
1764
1765 void BC_WindowBase::context_help_show(const char *keyword)
1766 {
1767         char cmd[BCTEXTLEN];
1768
1769         if (! keyword) return;
1770
1771         sprintf(cmd, "\"%s/doc/ContextManual.pl\" \"%s\"", getenv("CIN_DAT"),
1772                 keyword);
1773 //      printf("BC_WindowBase::context_help_show(%s):\n%s\n", keyword, cmd);
1774
1775 // ContextManual.pl starts browser in background, so system() should not block
1776         system(cmd);
1777 }
1778
1779 void BC_WindowBase::context_help_show()
1780 {
1781         context_help_show(context_help_get_keyword());
1782 }
1783
1784 int BC_WindowBase::context_help_check_and_show(const char *keyword)
1785 {
1786         if (! keyword)    return 0;
1787         if (! keyword[0]) return 0;
1788
1789 // We are handling Alt/H only
1790         if (get_keypress() != 'h' || ! alt_down()) return 0;
1791
1792 // Restrict cursor location to that widget keystroke occured in
1793         if (! is_tooltip_event_win() || ! cursor_inside()) return 0;
1794
1795         context_help_show(keyword);
1796         return 1;
1797 }
1798
1799 int BC_WindowBase::context_help_check_and_show()
1800 {
1801         const char *keyword;
1802
1803 // We are handling Alt/H only
1804         if (get_keypress() != 'h' || ! alt_down()) return 0;
1805
1806 // Restrict cursor location, so any subwindow can define its own help keyword
1807         if (! is_tooltip_event_win() || ! cursor_inside()) return 0;
1808
1809 // If a widget has not defined its own help keyword, the parent can provide it
1810         keyword = context_help_get_keyword();
1811         if (! keyword[0]) return 0;
1812
1813         context_help_show(keyword);
1814         return 1;
1815 }
1816 // *** END_CONTEXT_HELP ***
1817
1818 int BC_WindowBase::dispatch_drag_start()
1819 {
1820         int result = 0;
1821         if(active_menubar) result = active_menubar->dispatch_drag_start();
1822         if(!result && active_popup_menu) result = active_popup_menu->dispatch_drag_start();
1823         if(!result && active_subwindow) result = active_subwindow->dispatch_drag_start();
1824
1825         for(int i = 0; i < subwindows->total && !result; i++)
1826         {
1827                 result = subwindows->values[i]->dispatch_drag_start();
1828         }
1829
1830         if(!result) result = is_dragging = drag_start_event();
1831         return result;
1832 }
1833
1834 int BC_WindowBase::dispatch_drag_stop()
1835 {
1836         int result = 0;
1837
1838         for(int i = 0; i < subwindows->total && !result; i++)
1839         {
1840                 result = subwindows->values[i]->dispatch_drag_stop();
1841         }
1842
1843         if(is_dragging && !result)
1844         {
1845                 drag_stop_event();
1846                 is_dragging = 0;
1847                 result = 1;
1848         }
1849
1850         return result;
1851 }
1852
1853 int BC_WindowBase::dispatch_drag_motion()
1854 {
1855         int result = 0;
1856         for(int i = 0; i < subwindows->total && !result; i++)
1857         {
1858                 result = subwindows->values[i]->dispatch_drag_motion();
1859         }
1860
1861         if(is_dragging && !result)
1862         {
1863                 drag_motion_event();
1864                 result = 1;
1865         }
1866
1867         return result;
1868 }
1869
1870
1871 int BC_WindowBase::show_tooltip(const char *text, int x, int y, int w, int h)
1872 {
1873 // default text
1874         int forced = !text ? force_tooltip : 1;
1875         if( !text ) text = tooltip_text;
1876         if( !text || (!forced && !get_resources()->tooltips_enabled) ) {
1877                 top_level->hide_tooltip();
1878                 return 1;
1879         }
1880 // default w,h
1881         if(w < 0) w = get_text_width(MEDIUMFONT, text)  + TOOLTIP_MARGIN * 2;
1882         if(h < 0) h = get_text_height(MEDIUMFONT, text) + TOOLTIP_MARGIN * 2;
1883 // default x,y (win relative)
1884         if( x < 0 ) x = get_w();
1885         if( y < 0 ) y = get_h();
1886         int wx, wy;
1887         get_root_coordinates(x, y, &wx, &wy);
1888 // keep the tip inside the window/display
1889         int x0 = top_level->get_x(), x1 = x0 + top_level->get_w();
1890         int x2 = top_level->get_screen_x(0, -1) + top_level->get_screen_w(0, -1);
1891         if( x1 > x2 ) x1 = x2;
1892         if( wx < x0 ) wx = x0;
1893         if( wx >= (x1-=w) ) wx = x1;
1894         int y0 = top_level->get_y(), y1 = y0 + top_level->get_h();
1895         int y2 = top_level->get_root_h(0);
1896         if( y1 > y2 ) y1 = y2;
1897         if( wy < y0 ) wy = y0;
1898         if( wy >= (y1-=h) ) wy = y1;
1899 // avoid tip under cursor (flickers)
1900         int abs_x, abs_y;
1901         get_abs_cursor(abs_x,abs_y, 0);
1902         if( wx < abs_x && abs_x < wx+w && wy < abs_y && abs_y < wy+h ) {
1903                 if( wx-abs_x < wy-abs_y )
1904                         wx = abs_x+1;
1905                 else
1906                         wy = abs_y+1;
1907         }
1908         if( !tooltip_on ) {
1909                 tooltip_on = 1;
1910                 tooltip_popup = new BC_Popup(top_level, wx, wy, w, h,
1911                                 get_resources()->tooltip_bg_color);
1912         }
1913         else
1914                 tooltip_popup->reposition_window(wx, wy, w, h);
1915
1916         draw_tooltip(text);
1917         tooltip_popup->flash();
1918         tooltip_popup->flush();
1919         return 0;
1920 }
1921
1922 int BC_WindowBase::hide_tooltip()
1923 {
1924         if(subwindows)
1925                 for(int i = 0; i < subwindows->total; i++)
1926                 {
1927                         subwindows->values[i]->hide_tooltip();
1928                 }
1929
1930         if(tooltip_on)
1931         {
1932                 tooltip_on = 0;
1933                 delete tooltip_popup;
1934                 tooltip_popup = 0;
1935         }
1936         return 0;
1937 }
1938
1939 const char *BC_WindowBase::get_tooltip()
1940 {
1941         return tooltip_text;
1942 }
1943
1944 int BC_WindowBase::set_tooltip(const char *text)
1945 {
1946         tooltip_text = text;
1947
1948 // Update existing tooltip if it is visible
1949         if(tooltip_on)
1950         {
1951                 draw_tooltip();
1952                 tooltip_popup->flash();
1953         }
1954         return 0;
1955 }
1956 // signal the event handler to repeat
1957 int BC_WindowBase::set_repeat(int64_t duration)
1958 {
1959         if(duration <= 0)
1960         {
1961                 printf("BC_WindowBase::set_repeat duration=%jd\n", duration);
1962                 return 0;
1963         }
1964         if(window_type != MAIN_WINDOW) return top_level->set_repeat(duration);
1965
1966 #ifdef SINGLE_THREAD
1967         BC_Display::display_global->set_repeat(this, duration);
1968 #else
1969 // test repeater database for duplicates
1970         for(int i = 0; i < repeaters.total; i++)
1971         {
1972 // Already exists
1973                 if(repeaters.values[i]->delay == duration)
1974                 {
1975                         repeaters.values[i]->start_repeating(this);
1976                         return 0;
1977                 }
1978         }
1979
1980         BC_Repeater *repeater = new BC_Repeater(this, duration);
1981         repeater->initialize();
1982         repeaters.append(repeater);
1983         repeater->start_repeating();
1984 #endif
1985         return 0;
1986 }
1987
1988 int BC_WindowBase::unset_repeat(int64_t duration)
1989 {
1990         if(window_type != MAIN_WINDOW) return top_level->unset_repeat(duration);
1991
1992 #ifdef SINGLE_THREAD
1993         BC_Display::display_global->unset_repeat(this, duration);
1994 #else
1995         for(int i = 0; i < repeaters.total; i++)
1996         {
1997                 if(repeaters.values[i]->delay == duration)
1998                 {
1999                         repeaters.values[i]->stop_repeating();
2000                 }
2001         }
2002 #endif
2003         return 0;
2004 }
2005
2006
2007 int BC_WindowBase::unset_all_repeaters()
2008 {
2009 #ifdef SINGLE_THREAD
2010         BC_Display::display_global->unset_all_repeaters(this);
2011 #else
2012         for(int i = 0; i < repeaters.total; i++)
2013         {
2014                 repeaters.values[i]->stop_repeating();
2015         }
2016         repeaters.remove_all_objects();
2017 #endif
2018         return 0;
2019 }
2020
2021 // long BC_WindowBase::get_repeat_id()
2022 // {
2023 //      return top_level->next_repeat_id++;
2024 // }
2025
2026 XEvent *BC_WindowBase::new_xevent()
2027 {
2028         XEvent *event = new XEvent;
2029         memset(event, 0, sizeof(*event));
2030         return event;
2031 }
2032
2033 #ifndef SINGLE_THREAD
2034 int BC_WindowBase::arm_repeat(int64_t duration)
2035 {
2036         XEvent *event = new_xevent();
2037         XClientMessageEvent *ptr = (XClientMessageEvent*)event;
2038         ptr->type = ClientMessage;
2039         ptr->message_type = RepeaterXAtom;
2040         ptr->format = 32;
2041         ptr->data.l[0] = duration;
2042
2043 // Couldn't use XSendEvent since it locked up randomly.
2044         put_event(event);
2045         return 0;
2046 }
2047 #endif
2048
2049 int BC_WindowBase::receive_custom_xatoms(xatom_event *event)
2050 {
2051         return 0;
2052 }
2053
2054 int BC_WindowBase::send_custom_xatom(xatom_event *event)
2055 {
2056 #ifndef SINGLE_THREAD
2057         XEvent *myevent = new_xevent();
2058         XClientMessageEvent *ptr = (XClientMessageEvent*)myevent;
2059         ptr->type = ClientMessage;
2060         ptr->message_type = event->message_type;
2061         ptr->format = event->format;
2062         ptr->data.l[0] = event->data.l[0];
2063         ptr->data.l[1] = event->data.l[1];
2064         ptr->data.l[2] = event->data.l[2];
2065         ptr->data.l[3] = event->data.l[3];
2066         ptr->data.l[4] = event->data.l[4];
2067
2068         put_event(myevent);
2069 #endif
2070         return 0;
2071 }
2072
2073
2074
2075 Atom BC_WindowBase::create_xatom(const char *atom_name)
2076 {
2077         return XInternAtom(display, atom_name, False);
2078 }
2079
2080 int BC_WindowBase::get_atoms()
2081 {
2082         SetDoneXAtom =  XInternAtom(display, "BC_REPEAT_EVENT", False);
2083         RepeaterXAtom = XInternAtom(display, "BC_CLOSE_EVENT", False);
2084         DestroyAtom =   XInternAtom(display, "BC_DESTROY_WINDOW", False);
2085         DelWinXAtom =   XInternAtom(display, "WM_DELETE_WINDOW", False);
2086         if( (ProtoXAtom = XInternAtom(display, "WM_PROTOCOLS", False)) != 0 )
2087                 XChangeProperty(display, win, ProtoXAtom, XA_ATOM, 32,
2088                         PropModeReplace, (unsigned char *)&DelWinXAtom, True);
2089         return 0;
2090
2091 }
2092
2093
2094 void BC_WindowBase::init_cursors()
2095 {
2096         arrow_cursor = XCreateFontCursor(display, XC_top_left_arrow);
2097         cross_cursor = XCreateFontCursor(display, XC_crosshair);
2098         ibeam_cursor = XCreateFontCursor(display, XC_xterm);
2099         vseparate_cursor = XCreateFontCursor(display, XC_sb_v_double_arrow);
2100         hseparate_cursor = XCreateFontCursor(display, XC_sb_h_double_arrow);
2101         move_cursor = XCreateFontCursor(display, XC_fleur);
2102         left_cursor = XCreateFontCursor(display, XC_sb_left_arrow);
2103         right_cursor = XCreateFontCursor(display, XC_sb_right_arrow);
2104         upright_arrow_cursor = XCreateFontCursor(display, XC_arrow);
2105         upleft_resize_cursor = XCreateFontCursor(display, XC_top_left_corner);
2106         upright_resize_cursor = XCreateFontCursor(display, XC_top_right_corner);
2107         downleft_resize_cursor = XCreateFontCursor(display, XC_bottom_left_corner);
2108         downright_resize_cursor = XCreateFontCursor(display, XC_bottom_right_corner);
2109         hourglass_cursor = XCreateFontCursor(display, XC_watch);
2110         grabbed_cursor = create_grab_cursor();
2111
2112         static char cursor_data[] = { 0,0,0,0, 0,0,0,0 };
2113         Colormap colormap = DefaultColormap(display, screen);
2114         Pixmap pixmap_bottom = XCreateBitmapFromData(display,
2115                 rootwin, cursor_data, 8, 8);
2116         XColor black, dummy;
2117         XAllocNamedColor(display, colormap, "black", &black, &dummy);
2118         transparent_cursor = XCreatePixmapCursor(display,
2119                 pixmap_bottom, pixmap_bottom, &black, &black, 0, 0);
2120 //      XDefineCursor(display, win, transparent_cursor);
2121         XFreePixmap(display, pixmap_bottom);
2122 }
2123
2124 int BC_WindowBase::evaluate_color_model(int client_byte_order, int server_byte_order, int depth)
2125 {
2126         int color_model = BC_TRANSPARENCY;
2127         switch(depth)
2128         {
2129                 case 8:
2130                         color_model = BC_RGB8;
2131                         break;
2132                 case 16:
2133                         color_model = (server_byte_order == client_byte_order) ? BC_RGB565 : BC_BGR565;
2134                         break;
2135                 case 24:
2136                         color_model = server_byte_order ? BC_BGR888 : BC_RGB888;
2137                         break;
2138                 case 32:
2139                         color_model = server_byte_order ? BC_BGR8888 : BC_ARGB8888;
2140                         break;
2141         }
2142         return color_model;
2143 }
2144
2145 int BC_WindowBase::init_colors()
2146 {
2147         total_colors = 0;
2148         current_color_value = current_color_pixel = 0;
2149
2150 // Get the real depth
2151         char *data = 0;
2152         XImage *ximage;
2153         ximage = XCreateImage(top_level->display,
2154                         top_level->vis, top_level->default_depth,
2155                         ZPixmap, 0, data, 16, 16, 8, 0);
2156         bits_per_pixel = ximage->bits_per_pixel;
2157         XDestroyImage(ximage);
2158
2159         color_model = evaluate_color_model(client_byte_order,
2160                 server_byte_order,
2161                 bits_per_pixel);
2162 // Get the color model
2163         switch(color_model)
2164         {
2165                 case BC_RGB8:
2166                         if(private_color) {
2167                                 cmap = XCreateColormap(display, rootwin, vis, AllocNone);
2168                                 create_private_colors();
2169                         }
2170                         else {
2171                                 cmap = DefaultColormap(display, screen);
2172                                 create_shared_colors();
2173                         }
2174
2175                         allocate_color_table();
2176                         break;
2177
2178                 default:
2179                         //cmap = DefaultColormap(display, screen);
2180                         cmap = XCreateColormap(display, rootwin, vis, AllocNone );
2181                         break;
2182         }
2183         return 0;
2184 }
2185
2186 int BC_WindowBase::create_private_colors()
2187 {
2188         int color;
2189         total_colors = 256;
2190
2191         for(int i = 0; i < 255; i++)
2192         {
2193                 color = (i & 0xc0) << 16;
2194                 color += (i & 0x38) << 10;
2195                 color += (i & 0x7) << 5;
2196                 color_table[i][0] = color;
2197         }
2198         create_shared_colors();        // overwrite the necessary colors on the table
2199         return 0;
2200 }
2201
2202
2203 int BC_WindowBase::create_color(int color)
2204 {
2205         if(total_colors == 256)
2206         {
2207 // replace the closest match with an exact match
2208                 color_table[get_color_rgb8(color)][0] = color;
2209         }
2210         else
2211         {
2212 // add the color to the table
2213                 color_table[total_colors][0] = color;
2214                 total_colors++;
2215         }
2216         return 0;
2217 }
2218
2219 int BC_WindowBase::create_shared_colors()
2220 {
2221         create_color(BLACK);
2222         create_color(WHITE);
2223
2224         create_color(LTGREY);
2225         create_color(MEGREY);
2226         create_color(MDGREY);
2227         create_color(DKGREY);
2228
2229         create_color(LTCYAN);
2230         create_color(MECYAN);
2231         create_color(MDCYAN);
2232         create_color(DKCYAN);
2233
2234         create_color(LTGREEN);
2235         create_color(GREEN);
2236         create_color(DKGREEN);
2237
2238         create_color(LTPINK);
2239         create_color(PINK);
2240         create_color(RED);
2241
2242         create_color(LTBLUE);
2243         create_color(BLUE);
2244         create_color(DKBLUE);
2245
2246         create_color(LTYELLOW);
2247         create_color(MEYELLOW);
2248         create_color(MDYELLOW);
2249         create_color(DKYELLOW);
2250
2251         create_color(LTPURPLE);
2252         create_color(MEPURPLE);
2253         create_color(MDPURPLE);
2254         create_color(DKPURPLE);
2255
2256         create_color(FGGREY);
2257         create_color(MNBLUE);
2258         create_color(ORANGE);
2259         create_color(FTGREY);
2260
2261         return 0;
2262 }
2263
2264 Cursor BC_WindowBase::create_grab_cursor()
2265 {
2266         int iw = 23, iw1 = iw-1, iw2 = iw/2;
2267         int ih = 23, ih1 = ih-1, ih2 = ih/2;
2268         VFrame grab(iw,ih,BC_RGB888);
2269         grab.clear_frame();
2270         grab.set_pixel_color(RED);   // fg
2271         grab.draw_smooth(iw2,0,   iw1,0,   iw1,ih2);
2272         grab.draw_smooth(iw1,ih2, iw1,ih1, iw2,ih1);
2273         grab.draw_smooth(iw2,ih1, 0,ih1,   0,ih2);
2274         grab.draw_smooth(0,ih2,   0,0,     iw2,0);
2275         grab.set_pixel_color(WHITE); // bg
2276         grab.draw_line(0,ih2,     iw2-2,ih2);
2277         grab.draw_line(iw2+2,ih2, iw1,ih2);
2278         grab.draw_line(iw2,0,     iw2,ih2-2);
2279         grab.draw_line(iw2,ih2+2, iw2,ih1);
2280
2281         int bpl = (iw+7)/8, isz = bpl * ih;
2282         char img[isz];  memset(img, 0, isz);
2283         char msk[isz];  memset(msk, 0, isz);
2284         unsigned char **rows = grab.get_rows();
2285         for( int iy=0; iy<ih; ++iy ) {
2286                 char *op = img + iy*bpl;
2287                 char *mp = msk + iy*bpl;
2288                 unsigned char *ip = rows[iy];
2289                 for( int ix=0; ix<iw; ++ix,ip+=3 ) {
2290                         if( ip[0] ) mp[ix>>3] |= (1<<(ix&7));
2291                         if( !ip[1] ) op[ix>>3] |= (1<<(ix&7));
2292                 }
2293         }
2294         unsigned long white_pix = WhitePixel(display, screen);
2295         unsigned long black_pix = BlackPixel(display, screen);
2296         Pixmap img_xpm = XCreatePixmapFromBitmapData(display, rootwin,
2297                 img, iw,ih, white_pix,black_pix, 1);
2298         Pixmap msk_xpm = XCreatePixmapFromBitmapData(display, rootwin,
2299                 msk, iw,ih, white_pix,black_pix, 1);
2300
2301         XColor fc, bc;
2302         fc.flags = bc.flags = DoRed | DoGreen | DoBlue;
2303         fc.red = 0xffff; fc.green = fc.blue = 0;  // fg
2304         bc.red = 0xffff; bc.green = 0xffff; bc.blue = 0x0000;     // bg
2305         Cursor cursor = XCreatePixmapCursor(display, img_xpm,msk_xpm, &fc,&bc, iw2,ih2);
2306         XFreePixmap(display, img_xpm);
2307         XFreePixmap(display, msk_xpm);
2308         return cursor;
2309 }
2310
2311 int BC_WindowBase::allocate_color_table()
2312 {
2313         int red, green, blue, color;
2314         XColor col;
2315
2316         for(int i = 0; i < total_colors; i++)
2317         {
2318                 color = color_table[i][0];
2319                 red = (color & 0xFF0000) >> 16;
2320                 green = (color & 0x00FF00) >> 8;
2321                 blue = color & 0xFF;
2322
2323                 col.flags = DoRed | DoGreen | DoBlue;
2324                 col.red   = red<<8   | red;
2325                 col.green = green<<8 | green;
2326                 col.blue  = blue<<8  | blue;
2327
2328                 XAllocColor(display, cmap, &col);
2329                 color_table[i][1] = col.pixel;
2330         }
2331
2332         XInstallColormap(display, cmap);
2333         return 0;
2334 }
2335
2336 int BC_WindowBase::init_window_shape()
2337 {
2338         if(bg_pixmap && bg_pixmap->use_alpha())
2339         {
2340                 XShapeCombineMask(top_level->display,
2341                         this->win, ShapeBounding, 0, 0,
2342                         bg_pixmap->get_alpha(), ShapeSet);
2343         }
2344         return 0;
2345 }
2346
2347
2348 int BC_WindowBase::init_gc()
2349 {
2350         unsigned long gcmask;
2351         gcmask = GCFont | GCGraphicsExposures;
2352
2353         XGCValues gcvalues;
2354         gcvalues.font = mediumfont->fid;        // set the font
2355         gcvalues.graphics_exposures = 0;        // prevent expose events for every redraw
2356         gc = XCreateGC(display, rootwin, gcmask, &gcvalues);
2357
2358 // gcmask = GCCapStyle | GCJoinStyle;
2359 // XGetGCValues(display, gc, gcmask, &gcvalues);
2360 // printf("BC_WindowBase::init_gc %d %d %d\n", __LINE__, gcvalues.cap_style, gcvalues.join_style);
2361         return 0;
2362 }
2363
2364 int BC_WindowBase::init_fonts()
2365 {
2366         if( !(smallfont = XLoadQueryFont(display, _(resources->small_font))) )
2367                 if( !(smallfont = XLoadQueryFont(display, _(resources->small_font2))) )
2368                         smallfont = XLoadQueryFont(display, "fixed");
2369         if( !(mediumfont = XLoadQueryFont(display, _(resources->medium_font))) )
2370                 if( !(mediumfont = XLoadQueryFont(display, _(resources->medium_font2))) )
2371                         mediumfont = XLoadQueryFont(display, "fixed");
2372         if( !(largefont = XLoadQueryFont(display, _(resources->large_font))) )
2373                 if( !(largefont = XLoadQueryFont(display, _(resources->large_font2))) )
2374                         largefont = XLoadQueryFont(display, "fixed");
2375         if( !(bigfont = XLoadQueryFont(display, _(resources->big_font))) )
2376                 if( !(bigfont = XLoadQueryFont(display, _(resources->big_font2))) )
2377                         bigfont = XLoadQueryFont(display, "fixed");
2378
2379         if((clockfont = XLoadQueryFont(display, _(resources->clock_font))) == NULL)
2380                 if((clockfont = XLoadQueryFont(display, _(resources->clock_font2))) == NULL)
2381                         clockfont = XLoadQueryFont(display, "fixed");
2382
2383         init_xft();
2384         if(get_resources()->use_fontset)
2385         {
2386                 char **m, *d;
2387                 int n;
2388
2389 // FIXME: should check the m,d,n values
2390                 smallfontset = XCreateFontSet(display, resources->small_fontset, &m, &n, &d);
2391                 if( !smallfontset )
2392                         smallfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2393                 mediumfontset = XCreateFontSet(display, resources->medium_fontset, &m, &n, &d);
2394                 if( !mediumfontset )
2395                         mediumfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2396                 largefontset = XCreateFontSet(display, resources->large_fontset, &m, &n, &d);
2397                 if( !largefontset )
2398                         largefontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2399                 bigfontset = XCreateFontSet(display, resources->big_fontset, &m, &n, &d);
2400                 if( !bigfontset )
2401                         bigfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2402                 clockfontset = XCreateFontSet(display, resources->clock_fontset, &m, &n, &d);
2403                 if( !clockfontset )
2404                         clockfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2405                 if(clockfontset && bigfontset && largefontset && mediumfontset && smallfontset) {
2406                         curr_fontset = mediumfontset;
2407                         get_resources()->use_fontset = 1;
2408                 }
2409                 else {
2410                         curr_fontset = 0;
2411                         get_resources()->use_fontset = 0;
2412                 }
2413         }
2414
2415         return 0;
2416 }
2417
2418 void BC_WindowBase::init_xft()
2419 {
2420 #ifdef HAVE_XFT
2421         if( !get_resources()->use_xft ) return;
2422 // apparently, xft is not reentrant, more than this is needed
2423 static Mutex xft_init_lock("BC_WindowBase::xft_init_lock", 0);
2424 xft_init_lock.lock("BC_WindowBase::init_xft");
2425         if(!(smallfont_xft =
2426                 (resources->small_font_xft[0] == '-' ?
2427                         xftFontOpenXlfd(display, screen, resources->small_font_xft) :
2428                         xftFontOpenName(display, screen, resources->small_font_xft))) )
2429                 if(!(smallfont_xft =
2430                         xftFontOpenXlfd(display, screen, resources->small_font_xft2)))
2431                         smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2432         if(!(mediumfont_xft =
2433                 (resources->medium_font_xft[0] == '-' ?
2434                         xftFontOpenXlfd(display, screen, resources->medium_font_xft) :
2435                         xftFontOpenName(display, screen, resources->medium_font_xft))) )
2436                 if(!(mediumfont_xft =
2437                         xftFontOpenXlfd(display, screen, resources->medium_font_xft2)))
2438                         mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2439         if(!(largefont_xft =
2440                 (resources->large_font_xft[0] == '-' ?
2441                         xftFontOpenXlfd(display, screen, resources->large_font_xft) :
2442                         xftFontOpenName(display, screen, resources->large_font_xft))) )
2443                 if(!(largefont_xft =
2444                         xftFontOpenXlfd(display, screen, resources->large_font_xft2)))
2445                         largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
2446         if(!(bigfont_xft =
2447                 (resources->big_font_xft[0] == '-' ?
2448                         xftFontOpenXlfd(display, screen, resources->big_font_xft) :
2449                         xftFontOpenName(display, screen, resources->big_font_xft))) )
2450                 if(!(bigfont_xft =
2451                         xftFontOpenXlfd(display, screen, resources->big_font_xft2)))
2452                         bigfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2453         if(!(clockfont_xft =
2454                 (resources->clock_font_xft[0] == '-' ?
2455                         xftFontOpenXlfd(display, screen, resources->clock_font_xft) :
2456                         xftFontOpenName(display, screen, resources->clock_font_xft))) )
2457                 clockfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2458
2459
2460         if(!(bold_smallfont_xft =
2461                 (resources->small_b_font_xft[0] == '-' ?
2462                         xftFontOpenXlfd(display, screen, resources->small_b_font_xft) :
2463                         xftFontOpenName(display, screen, resources->small_b_font_xft))) )
2464                 bold_smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2465         if(!(bold_mediumfont_xft =
2466                 (resources->medium_b_font_xft[0] == '-' ?
2467                         xftFontOpenXlfd(display, screen, resources->medium_b_font_xft) :
2468                         xftFontOpenName(display, screen, resources->medium_b_font_xft))) )
2469                 bold_mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2470         if(!(bold_largefont_xft =
2471                 (resources->large_b_font_xft[0] == '-' ?
2472                         xftFontOpenXlfd(display, screen, resources->large_b_font_xft) :
2473                         xftFontOpenName(display, screen, resources->large_b_font_xft))) )
2474                 bold_largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
2475
2476         if( !smallfont_xft || !mediumfont_xft || !largefont_xft || !bigfont_xft ||
2477             !bold_largefont_xft || !bold_mediumfont_xft || !bold_largefont_xft ||
2478             !clockfont_xft ) {
2479                 printf("BC_WindowBase::init_fonts: no xft fonts found:"
2480                         " %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n",
2481                         resources->small_font_xft, smallfont_xft,
2482                         resources->medium_font_xft, mediumfont_xft,
2483                         resources->large_font_xft, largefont_xft,
2484                         resources->big_font_xft, bigfont_xft,
2485                         resources->clock_font_xft, clockfont_xft,
2486                         resources->small_b_font_xft, bold_smallfont_xft,
2487                         resources->medium_b_font_xft, bold_mediumfont_xft,
2488                         resources->large_b_font_xft, bold_largefont_xft);
2489                 get_resources()->use_xft = 0;
2490                 exit(1);
2491         }
2492 // _XftDisplayInfo needs a lock.
2493         xftDefaultHasRender(display);
2494 xft_init_lock.unlock();
2495 #endif // HAVE_XFT
2496 }
2497
2498 void BC_WindowBase::init_glyphs()
2499 {
2500 // draw all ascii char glyphs
2501 //  There are problems with some/my graphics boards/drivers
2502 //  which cause some glyphs to be munged if draws occur while
2503 //  the font is being loaded.  This code fills the font caches
2504 //  by drawing all the ascii glyphs before the system starts.
2505 //  Not a fix, but much better than nothing.
2506         static int inited = 0;
2507         if( inited ) return;
2508         XGrabServer(display);
2509         XSync(display, 0);
2510         inited = 1;
2511         int cur_font = current_font;
2512 // locale encodings, needed glyphs to be preloaded
2513         const char *text = _( // ascii 0x20...0x7e
2514                 " !\"#$%&'()*+,-./0123456789:;<=>?"
2515                 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
2516                 "`abcdefghijklmnopqrstuvwxyz{|}~");
2517         for( int font=SMALLFONT; font<=LARGEFONT; ++font ) {
2518                 set_font(font);
2519                 draw_text(5,5, text);
2520         }
2521         set_font(cur_font);
2522         XUngrabServer(display);
2523 }
2524
2525 void BC_WindowBase::init_im()
2526 {
2527         XIMStyles *xim_styles;
2528         XIMStyle xim_style;
2529
2530         if(!(input_method = XOpenIM(display, NULL, NULL, NULL)))
2531         {
2532                 printf("BC_WindowBase::init_im: Could not open input method.\n");
2533                 XSetLocaleModifiers("@im=local");
2534                 if(!(input_method = XOpenIM(display, NULL, NULL, NULL))) {
2535                 printf("BC_WindowBase::init_im: Could not open input method local.\n");
2536                 exit(1);
2537                 }
2538         }
2539         if(XGetIMValues(input_method, XNQueryInputStyle, &xim_styles, NULL) ||
2540                         xim_styles == NULL)
2541         {
2542                 printf("BC_WindowBase::init_im: Input method doesn't support any styles.\n");
2543                 XCloseIM(input_method);
2544                 exit(1);
2545         }
2546
2547         xim_style = 0;
2548         for(int z = 0;  z < xim_styles->count_styles;  z++)
2549         {
2550                 if(xim_styles->supported_styles[z] == (XIMPreeditNothing | XIMStatusNothing))
2551                 {
2552                         xim_style = xim_styles->supported_styles[z];
2553                         break;
2554                 }
2555         }
2556         XFree(xim_styles);
2557
2558         if(xim_style == 0)
2559         {
2560                 printf("BC_WindowBase::init_im: Input method doesn't support the style we need.\n");
2561                 XCloseIM(input_method);
2562                 exit(1);
2563         }
2564
2565         input_context = XCreateIC(input_method, XNInputStyle, xim_style,
2566                 XNClientWindow, win, XNFocusWindow, win, NULL);
2567         if(!input_context)
2568         {
2569                 printf("BC_WindowBase::init_im: Failed to create input context.\n");
2570                 XCloseIM(input_method);
2571                 exit(1);
2572         }
2573 }
2574
2575 void BC_WindowBase::finit_im()
2576 {
2577         if( input_context ) {
2578                 XDestroyIC(input_context);
2579                 input_context = 0;
2580         }
2581         if( input_method ) {
2582                 XCloseIM(input_method);
2583                 input_method = 0;
2584         }
2585 }
2586
2587
2588 int BC_WindowBase::get_color(int64_t color)
2589 {
2590 // return pixel of color
2591 // use this only for drawing subwindows not for bitmaps
2592          int i, test, difference;
2593
2594         switch(color_model)
2595         {
2596         case BC_RGB8:
2597                 if(private_color)
2598                         return get_color_rgb8(color);
2599 // test last color looked up
2600                 if(current_color_value == color)
2601                         return current_color_pixel;
2602
2603 // look up in table
2604                 current_color_value = color;
2605                 for(i = 0; i < total_colors; i++)
2606                 {
2607                         if(color_table[i][0] == color)
2608                         {
2609                                 current_color_pixel = color_table[i][1];
2610                                 return current_color_pixel;
2611                         }
2612                 }
2613
2614 // find nearest match
2615                 difference = 0xFFFFFF;
2616
2617                 for(i = 0; i < total_colors; i++)
2618                 {
2619                         test = abs((int)(color_table[i][0] - color));
2620
2621                         if(test < difference)
2622                         {
2623                                 current_color_pixel = color_table[i][1];
2624                                 difference = test;
2625                         }
2626                 }
2627                 return current_color_pixel;
2628
2629         case BC_RGB565:
2630                 return get_color_rgb16(color);
2631
2632         case BC_BGR565:
2633                 return get_color_bgr16(color);
2634
2635         case BC_RGB888:
2636         case BC_BGR888:
2637                 return client_byte_order == server_byte_order ?
2638                         color : get_color_bgr24(color);
2639
2640         default:
2641                 return color;
2642         }
2643         return 0;
2644 }
2645
2646 int BC_WindowBase::get_color_rgb8(int color)
2647 {
2648         int pixel;
2649
2650         pixel = (color & 0xc00000) >> 16;
2651         pixel += (color & 0xe000) >> 10;
2652         pixel += (color & 0xe0) >> 5;
2653         return pixel;
2654 }
2655
2656 int64_t BC_WindowBase::get_color_rgb16(int color)
2657 {
2658         int64_t result;
2659         result = (color & 0xf80000) >> 8;
2660         result += (color & 0xfc00) >> 5;
2661         result += (color & 0xf8) >> 3;
2662
2663         return result;
2664 }
2665
2666 int64_t BC_WindowBase::get_color_bgr16(int color)
2667 {
2668         int64_t result;
2669         result = (color & 0xf80000) >> 19;
2670         result += (color & 0xfc00) >> 5;
2671         result += (color & 0xf8) << 8;
2672
2673         return result;
2674 }
2675
2676 int64_t BC_WindowBase::get_color_bgr24(int color)
2677 {
2678         int64_t result;
2679         result = (color & 0xff) << 16;
2680         result += (color & 0xff00);
2681         result += (color & 0xff0000) >> 16;
2682         return result;
2683 }
2684
2685 void BC_WindowBase::start_video()
2686 {
2687         cursor_timer->update();
2688         video_on = 1;
2689 //      set_color(BLACK);
2690 //      draw_box(0, 0, get_w(), get_h());
2691 //      flash();
2692 }
2693
2694 void BC_WindowBase::stop_video()
2695 {
2696         video_on = 0;
2697         unhide_cursor();
2698 }
2699
2700
2701
2702 int64_t BC_WindowBase::get_color()
2703 {
2704         return top_level->current_color;
2705 }
2706
2707 void BC_WindowBase::set_color(int64_t color)
2708 {
2709         top_level->current_color = color;
2710         XSetForeground(top_level->display,
2711                 top_level->gc,
2712                 top_level->get_color(color));
2713 }
2714
2715 void BC_WindowBase::set_opaque()
2716 {
2717         XSetFunction(top_level->display, top_level->gc, GXcopy);
2718 }
2719
2720 void BC_WindowBase::set_inverse()
2721 {
2722         XSetFunction(top_level->display, top_level->gc, GXxor);
2723 }
2724
2725 void BC_WindowBase::set_line_width(int value)
2726 {
2727         this->line_width = value;
2728         XSetLineAttributes(top_level->display, top_level->gc, value,    /* line_width */
2729                 line_dashes == 0 ? LineSolid : LineOnOffDash,           /* line_style */
2730                 line_dashes == 0 ? CapRound : CapNotLast,               /* cap_style */
2731                 JoinMiter);                                             /* join_style */
2732
2733         if(line_dashes > 0) {
2734                 const char dashes = line_dashes;
2735                 XSetDashes(top_level->display, top_level->gc, 0, &dashes, 1);
2736         }
2737
2738 // XGCValues gcvalues;
2739 // unsigned long gcmask;
2740 // gcmask = GCCapStyle | GCJoinStyle;
2741 // XGetGCValues(top_level->display, top_level->gc, gcmask, &gcvalues);
2742 // printf("BC_WindowBase::set_line_width %d %d %d\n", __LINE__, gcvalues.cap_style, gcvalues.join_style);
2743 }
2744
2745 void BC_WindowBase::set_line_dashes(int value)
2746 {
2747         line_dashes = value;
2748 // call XSetLineAttributes
2749         set_line_width(line_width);
2750 }
2751
2752
2753 Cursor BC_WindowBase::get_cursor_struct(int cursor)
2754 {
2755         switch(cursor)
2756         {
2757                 case ARROW_CURSOR:         return top_level->arrow_cursor;
2758                 case CROSS_CURSOR:         return top_level->cross_cursor;
2759                 case IBEAM_CURSOR:         return top_level->ibeam_cursor;
2760                 case VSEPARATE_CURSOR:     return top_level->vseparate_cursor;
2761                 case HSEPARATE_CURSOR:     return top_level->hseparate_cursor;
2762                 case MOVE_CURSOR:          return top_level->move_cursor;
2763                 case LEFT_CURSOR:          return top_level->left_cursor;
2764                 case RIGHT_CURSOR:         return top_level->right_cursor;
2765                 case UPRIGHT_ARROW_CURSOR: return top_level->upright_arrow_cursor;
2766                 case UPLEFT_RESIZE:        return top_level->upleft_resize_cursor;
2767                 case UPRIGHT_RESIZE:       return top_level->upright_resize_cursor;
2768                 case DOWNLEFT_RESIZE:      return top_level->downleft_resize_cursor;
2769                 case DOWNRIGHT_RESIZE:     return top_level->downright_resize_cursor;
2770                 case HOURGLASS_CURSOR:     return top_level->hourglass_cursor;
2771                 case TRANSPARENT_CURSOR:   return top_level->transparent_cursor;
2772                 case GRABBED_CURSOR:       return top_level->grabbed_cursor;
2773         }
2774         return 0;
2775 }
2776
2777 void BC_WindowBase::set_cursor(int cursor, int override, int flush)
2778 {
2779 // inherit cursor from parent
2780         if(cursor < 0)
2781         {
2782                 XUndefineCursor(top_level->display, win);
2783                 current_cursor = cursor;
2784         }
2785         else
2786 // don't change cursor if overridden
2787         if((!top_level->is_hourglass && !is_transparent) ||
2788                 override)
2789         {
2790                 XDefineCursor(top_level->display, win, get_cursor_struct(cursor));
2791                 if(flush) this->flush();
2792         }
2793
2794         if(!override) current_cursor = cursor;
2795 }
2796
2797 void BC_WindowBase::set_x_cursor(int cursor)
2798 {
2799         temp_cursor = XCreateFontCursor(top_level->display, cursor);
2800         XDefineCursor(top_level->display, win, temp_cursor);
2801         current_cursor = cursor;
2802         flush();
2803 }
2804
2805 int BC_WindowBase::get_cursor()
2806 {
2807         return current_cursor;
2808 }
2809
2810 void BC_WindowBase::start_hourglass()
2811 {
2812         top_level->start_hourglass_recursive();
2813         top_level->flush();
2814 }
2815
2816 void BC_WindowBase::stop_hourglass()
2817 {
2818         top_level->stop_hourglass_recursive();
2819         top_level->flush();
2820 }
2821
2822 void BC_WindowBase::start_hourglass_recursive()
2823 {
2824         if(this == top_level)
2825         {
2826                 hourglass_total++;
2827                 is_hourglass = 1;
2828         }
2829
2830         if(!is_transparent)
2831         {
2832                 set_cursor(HOURGLASS_CURSOR, 1, 0);
2833                 for(int i = 0; i < subwindows->total; i++)
2834                 {
2835                         subwindows->values[i]->start_hourglass_recursive();
2836                 }
2837         }
2838 }
2839
2840 void BC_WindowBase::stop_hourglass_recursive()
2841 {
2842         if(this == top_level)
2843         {
2844                 if(hourglass_total == 0) return;
2845                 top_level->hourglass_total--;
2846         }
2847
2848         if(!top_level->hourglass_total)
2849         {
2850                 top_level->is_hourglass = 0;
2851
2852 // Cause set_cursor to perform change
2853                 if(!is_transparent)
2854                         set_cursor(current_cursor, 1, 0);
2855
2856                 for(int i = 0; i < subwindows->total; i++)
2857                 {
2858                         subwindows->values[i]->stop_hourglass_recursive();
2859                 }
2860         }
2861 }
2862
2863
2864
2865
2866 XFontStruct* BC_WindowBase::get_font_struct(int font)
2867 {
2868 // Clear out unrelated flags
2869         if(font & BOLDFACE) font ^= BOLDFACE;
2870
2871         switch(font) {
2872                 case SMALLFONT:  return top_level->smallfont;  break;
2873                 case MEDIUMFONT: return top_level->mediumfont; break;
2874                 case LARGEFONT:  return top_level->largefont;  break;
2875                 case BIGFONT:    return top_level->bigfont;    break;
2876                 case CLOCKFONT:  return top_level->clockfont;  break;
2877         }
2878         return 0;
2879 }
2880
2881 XFontSet BC_WindowBase::get_fontset(int font)
2882 {
2883         XFontSet fs = 0;
2884
2885         if(get_resources()->use_fontset)
2886         {
2887                 switch(font & 0xff) {
2888                         case SMALLFONT:  fs = top_level->smallfontset; break;
2889                         case MEDIUMFONT: fs = top_level->mediumfontset; break;
2890                         case LARGEFONT:  fs = top_level->largefontset; break;
2891                         case BIGFONT:    fs = top_level->bigfontset;   break;
2892                         case CLOCKFONT: fs = top_level->clockfontset; break;
2893                 }
2894         }
2895
2896         return fs;
2897 }
2898
2899 #ifdef HAVE_XFT
2900 XftFont* BC_WindowBase::get_xft_struct(int font)
2901 {
2902         switch(font) {
2903                 case SMALLFONT:    return (XftFont*)top_level->smallfont_xft;
2904                 case MEDIUMFONT:   return (XftFont*)top_level->mediumfont_xft;
2905                 case LARGEFONT:    return (XftFont*)top_level->largefont_xft;
2906                 case BIGFONT:      return (XftFont*)top_level->bigfont_xft;
2907                 case CLOCKFONT:    return (XftFont*)top_level->clockfont_xft;
2908                 case MEDIUMFONT_3D: return (XftFont*)top_level->bold_mediumfont_xft;
2909                 case SMALLFONT_3D:  return (XftFont*)top_level->bold_smallfont_xft;
2910                 case LARGEFONT_3D:  return (XftFont*)top_level->bold_largefont_xft;
2911         }
2912
2913         return 0;
2914 }
2915 #endif
2916
2917
2918 int BC_WindowBase::get_current_font()
2919 {
2920         return top_level->current_font;
2921 }
2922
2923 void BC_WindowBase::set_font(int font)
2924 {
2925         top_level->current_font = font;
2926
2927 #ifdef HAVE_XFT
2928         if(get_resources()->use_xft) {}
2929         else
2930 #endif
2931         if(get_resources()->use_fontset) {
2932                 set_fontset(font);
2933         }
2934
2935         if(get_font_struct(font))
2936         {
2937                 XSetFont(top_level->display, top_level->gc, get_font_struct(font)->fid);
2938         }
2939
2940         return;
2941 }
2942
2943 void BC_WindowBase::set_fontset(int font)
2944 {
2945         XFontSet fs = 0;
2946
2947         if(get_resources()->use_fontset) {
2948                 switch(font) {
2949                         case SMALLFONT:  fs = top_level->smallfontset; break;
2950                         case MEDIUMFONT: fs = top_level->mediumfontset; break;
2951                         case LARGEFONT:  fs = top_level->largefontset; break;
2952                         case BIGFONT:    fs = top_level->bigfontset;   break;
2953                         case CLOCKFONT:  fs = top_level->clockfontset; break;
2954                 }
2955         }
2956
2957         curr_fontset = fs;
2958 }
2959
2960
2961 XFontSet BC_WindowBase::get_curr_fontset(void)
2962 {
2963         if(get_resources()->use_fontset)
2964                 return curr_fontset;
2965         return 0;
2966 }
2967
2968 int BC_WindowBase::get_single_text_width(int font, const char *text, int length)
2969 {
2970 #ifdef HAVE_XFT
2971         if(get_resources()->use_xft && get_xft_struct(font))
2972         {
2973                 XGlyphInfo extents;
2974 #ifdef X_HAVE_UTF8_STRING
2975                 if(get_resources()->locale_utf8)
2976                 {
2977                         xftTextExtentsUtf8(top_level->display,
2978                                 get_xft_struct(font),
2979                                 (const XftChar8 *)text,
2980                                 length,
2981                                 &extents);
2982                 }
2983                 else
2984 #endif
2985                 {
2986                         xftTextExtents8(top_level->display,
2987                                 get_xft_struct(font),
2988                                 (const XftChar8 *)text,
2989                                 length,
2990                                 &extents);
2991                 }
2992                 return extents.xOff;
2993         }
2994         else
2995 #endif
2996         if(get_resources()->use_fontset && top_level->get_fontset(font))
2997                 return XmbTextEscapement(top_level->get_fontset(font), text, length);
2998         else
2999         if(get_font_struct(font))
3000                 return XTextWidth(get_font_struct(font), text, length);
3001         else
3002         {
3003                 int w = 0;
3004                 switch(font)
3005                 {
3006                         case MEDIUM_7SEGMENT:
3007                                 return get_resources()->medium_7segment[0]->get_w() * length;
3008                                 break;
3009
3010                         default:
3011                                 return 0;
3012                 }
3013                 return w;
3014         }
3015 }
3016
3017 int BC_WindowBase::get_text_width(int font, const char *text, int length)
3018 {
3019         int i, j, w = 0, line_w = 0;
3020         if(length < 0) length = strlen(text);
3021
3022         for(i = 0, j = 0; i <= length; i++)
3023         {
3024                 line_w = 0;
3025                 if(text[i] == '\n')
3026                 {
3027                         line_w = get_single_text_width(font, &text[j], i - j);
3028                         j = i + 1;
3029                 }
3030                 else
3031                 if(text[i] == 0)
3032                 {
3033                         line_w = get_single_text_width(font, &text[j], length - j);
3034                 }
3035                 if(line_w > w) w = line_w;
3036         }
3037
3038         if(i > length && w == 0)
3039         {
3040                 w = get_single_text_width(font, text, length);
3041         }
3042
3043         return w;
3044 }
3045
3046 int BC_WindowBase::get_text_width(int font, const wchr_t *text, int length)
3047 {
3048         int i, j, w = 0;
3049         if( length < 0 ) length = wstrlen(text);
3050
3051         for( i=j=0; i<length && text[i]; ++i ) {
3052                 if( text[i] != '\n' ) continue;
3053                 if( i > j ) {
3054                         int lw = get_single_text_width(font, &text[j], i-j);
3055                         if( w < lw ) w = lw;
3056                 }
3057                 j = i + 1;
3058         }
3059         if( j < length ) {
3060                 int lw = get_single_text_width(font, &text[j], length-j);
3061                 if( w < lw ) w = lw;
3062         }
3063
3064         return w;
3065 }
3066
3067 int BC_WindowBase::get_text_ascent(int font)
3068 {
3069 #ifdef HAVE_XFT
3070         XftFont *fstruct;
3071         if( (fstruct = get_xft_struct(font)) != 0 )
3072                 return fstruct->ascent;
3073 #endif
3074         if(get_resources()->use_fontset && top_level->get_fontset(font))
3075         {
3076                 XFontSetExtents *extents;
3077
3078                 extents = XExtentsOfFontSet(top_level->get_fontset(font));
3079                 return -extents->max_logical_extent.y;
3080         }
3081
3082         if(get_font_struct(font))
3083                 return top_level->get_font_struct(font)->ascent;
3084
3085         switch(font) {
3086                 case MEDIUM_7SEGMENT:
3087                         return get_resources()->medium_7segment[0]->get_h();
3088         }
3089         return 0;
3090 }
3091
3092 int BC_WindowBase::get_text_descent(int font)
3093 {
3094 #ifdef HAVE_XFT
3095         XftFont *fstruct;
3096         if( (fstruct = get_xft_struct(font)) != 0 )
3097                 return fstruct->descent;
3098 #endif
3099         if(get_resources()->use_fontset && top_level->get_fontset(font)) {
3100                 XFontSetExtents *extents;
3101                 extents = XExtentsOfFontSet(top_level->get_fontset(font));
3102                 return (extents->max_logical_extent.height
3103                         + extents->max_logical_extent.y);
3104         }
3105
3106         if(get_font_struct(font))
3107                 return top_level->get_font_struct(font)->descent;
3108
3109         return 0;
3110 }
3111
3112 int BC_WindowBase::get_text_height(int font, const char *text)
3113 {
3114         int rowh;
3115 #ifdef HAVE_XFT
3116         XftFont *fstruct;
3117         if( (fstruct = get_xft_struct(font)) != 0 )
3118                 rowh = fstruct->height;
3119         else
3120 #endif
3121                 rowh = get_text_ascent(font) + get_text_descent(font);
3122
3123         if(!text) return rowh;
3124
3125 // Add height of lines
3126         int h = 0, i, length = strlen(text);
3127         for(i = 0; i <= length; i++)
3128         {
3129                 if(text[i] == '\n')
3130                         h++;
3131                 else
3132                 if(text[i] == 0)
3133                         h++;
3134         }
3135         return h * rowh;
3136 }
3137
3138 // truncate the text with ... & return a new string
3139 char *BC_WindowBase::get_truncated_text(int font, const char *text, int max_w)
3140 {
3141         char *result = cstrdup(text);
3142         int text_w = get_text_width(font, text);
3143         int ci = -1, len = strlen(text);
3144         if( text_w > max_w ) {
3145 // get center of string
3146                 int cx = text_w/2, best = INT_MAX;
3147                 for( int i=1; i<=len; ++i ) {
3148                         int cw = get_text_width(font, text, i);
3149                         if( abs(cw-cx) < abs(best-cx) ) {
3150                                 best = cw;  ci = i;
3151                         }
3152                 }
3153         }
3154         if( ci > 0 && ci < len-1 ) {
3155 // insert ... in the center
3156                 result[ci-1] = result[ci] = result[ci+1] = '.';
3157
3158                 while( ci-2>=0 && ci+2<(int)strlen(result) &&
3159                        get_text_width(font, result) > max_w ) {
3160 // take away a character from the longer side
3161                         int left_w = get_text_width(font, result, ci-2);
3162                         int right_w = get_text_width(font, result + ci+3);
3163                         int i = left_w > right_w ? --ci-1 : ci+2;
3164                         while( (result[i]=result[i+1])!=0 ) ++i;
3165                 }
3166         }
3167
3168         return result;
3169 }
3170
3171 BC_Bitmap* BC_WindowBase::new_bitmap(int w, int h, int color_model)
3172 {
3173         if(color_model < 0) color_model = top_level->get_color_model();
3174         return new BC_Bitmap(top_level, w, h, color_model);
3175 }
3176
3177 void BC_WindowBase::init_wait()
3178 {
3179 #ifndef SINGLE_THREAD
3180         if(window_type != MAIN_WINDOW)
3181                 top_level->init_wait();
3182         init_lock->lock("BC_WindowBase::init_wait");
3183         init_lock->unlock();
3184 #endif
3185 }
3186
3187 int BC_WindowBase::accel_available(int color_model, int lock_it)
3188 {
3189         if( window_type != MAIN_WINDOW )
3190                 return top_level->accel_available(color_model, lock_it);
3191         if( lock_it )
3192                 lock_window("BC_WindowBase::accel_available");
3193
3194         switch(color_model) {
3195         case BC_YUV420P:
3196                 grab_port_id(color_model);
3197                 break;
3198
3199         case BC_YUV422:
3200                 grab_port_id(color_model);
3201                 break;
3202
3203         default:
3204                 break;
3205         }
3206
3207         if( lock_it )
3208                 unlock_window();
3209 //printf("BC_WindowBase::accel_available %d %d\n", color_model, xvideo_port_id);
3210         return xvideo_port_id >= 0 ? 1 : 0;
3211 }
3212
3213
3214 int BC_WindowBase::grab_port_id(int color_model)
3215 {
3216 #ifdef HAVE_XV
3217         if( !get_resources()->use_xvideo ||     // disabled
3218             !get_resources()->use_shm )         // Only local server is fast enough.
3219                 return -1;
3220         if( xvideo_port_id >= 0 )
3221                 return xvideo_port_id;
3222
3223         unsigned int ver, rev, reqBase, eventBase, errorBase;
3224         if( Success != XvQueryExtension(display, // XV extension is available
3225                     &ver, &rev, &reqBase, &eventBase, &errorBase) )
3226                 return -1;
3227
3228 // XV adaptors are available
3229         unsigned int numAdapt = 0;
3230         XvAdaptorInfo *info = 0;
3231         XvQueryAdaptors(display, DefaultRootWindow(display), &numAdapt, &info);
3232         if( !numAdapt )
3233                 return -1;
3234
3235 // Translate from color_model to X color model
3236         int x_color_model = BC_CModels::bc_to_x(color_model);
3237
3238 // Get adaptor with desired color model
3239         for( int i = 0; i < (int)numAdapt && xvideo_port_id == -1; i++) {
3240                 if( !(info[i].type & XvImageMask) || !info[i].num_ports ) continue;
3241 // adaptor supports XvImages
3242                 int numFormats = 0, numPorts = info[i].num_ports;
3243                 XvImageFormatValues *formats =
3244                         XvListImageFormats(display, info[i].base_id, &numFormats);
3245                 if( !formats ) continue;
3246
3247                 for( int j=0; j<numFormats && xvideo_port_id<0; ++j ) {
3248                         if( formats[j].id != x_color_model ) continue;
3249 // this adaptor supports the desired format, grab a port
3250                         for( int k=0; k<numPorts; ++k ) {
3251                                 if( Success == XvGrabPort(top_level->display,
3252                                         info[i].base_id+k, CurrentTime) ) {
3253 //printf("BC_WindowBase::grab_port_id %llx\n", info[i].base_id);
3254                                         xvideo_port_id = info[i].base_id + k;
3255                                         break;
3256                                 }
3257                         }
3258                 }
3259                 XFree(formats);
3260         }
3261
3262         XvFreeAdaptorInfo(info);
3263         return xvideo_port_id;
3264 #else
3265         return -1;
3266 #endif
3267 }
3268
3269
3270 int BC_WindowBase::show_window(int flush)
3271 {
3272         for(int i = 0; i < subwindows->size(); i++)
3273         {
3274                 subwindows->get(i)->show_window(0);
3275         }
3276
3277         XMapWindow(top_level->display, win);
3278         if(flush) XFlush(top_level->display);
3279 //      XSync(top_level->display, 0);
3280         hidden = 0;
3281         return 0;
3282 }
3283
3284 int BC_WindowBase::hide_window(int flush)
3285 {
3286         for(int i = 0; i < subwindows->size(); i++)
3287         {
3288                 subwindows->get(i)->hide_window(0);
3289         }
3290
3291         XUnmapWindow(top_level->display, win);
3292         if(flush) this->flush();
3293         hidden = 1;
3294         return 0;
3295 }
3296
3297 BC_MenuBar* BC_WindowBase::add_menubar(BC_MenuBar *menu_bar)
3298 {
3299         subwindows->append((BC_SubWindow*)menu_bar);
3300
3301         menu_bar->parent_window = this;
3302         menu_bar->top_level = this->top_level;
3303         menu_bar->initialize();
3304         return menu_bar;
3305 }
3306
3307 BC_WindowBase* BC_WindowBase::add_popup(BC_WindowBase *window)
3308 {
3309 //printf("BC_WindowBase::add_popup window=%p win=%p\n", window, window->win);
3310         if(this != top_level) return top_level->add_popup(window);
3311         popups.append(window);
3312         return window;
3313 }
3314
3315 void BC_WindowBase::remove_popup(BC_WindowBase *window)
3316 {
3317 //printf("BC_WindowBase::remove_popup %d size=%d window=%p win=%p\n", __LINE__, popups.size(), window, window->win);
3318         if(this != top_level)
3319                 top_level->remove_popup(window);
3320         else
3321                 popups.remove(window);
3322 //printf("BC_WindowBase::remove_popup %d size=%d window=%p win=%p\n", __LINE__, popups.size(), window, window->win);
3323 }
3324
3325
3326 BC_WindowBase* BC_WindowBase::add_subwindow(BC_WindowBase *subwindow)
3327 {
3328         subwindows->append(subwindow);
3329
3330         if(subwindow->bg_color == -1) subwindow->bg_color = this->bg_color;
3331
3332 // parent window must be set before the subwindow initialization
3333         subwindow->parent_window = this;
3334         subwindow->top_level = this->top_level;
3335
3336 // Execute derived initialization
3337         subwindow->initialize();
3338         return subwindow;
3339 }
3340
3341
3342 BC_WindowBase* BC_WindowBase::add_tool(BC_WindowBase *subwindow)
3343 {
3344         return add_subwindow(subwindow);
3345 }
3346
3347 int BC_WindowBase::flash(int x, int y, int w, int h, int flush)
3348 {
3349         if( !top_level->flash_enabled ) return 0;
3350 //printf("BC_WindowBase::flash %d %d %d %d %d\n", __LINE__, w, h, this->w, this->h);
3351         set_opaque();
3352         XSetWindowBackgroundPixmap(top_level->display, win, pixmap->opaque_pixmap);
3353         if(x >= 0)
3354         {
3355                 XClearArea(top_level->display, win, x, y, w, h, 0);
3356         }
3357         else
3358         {
3359                 XClearWindow(top_level->display, win);
3360         }
3361
3362         if(flush)
3363                 this->flush();
3364         return 0;
3365 }
3366
3367 int BC_WindowBase::flash(int flush)
3368 {
3369         flash(-1, -1, -1, -1, flush);
3370         return 0;
3371 }
3372
3373 void BC_WindowBase::flush()
3374 {
3375         //if(!get_window_lock())
3376         //      printf("BC_WindowBase::flush %s not locked\n", top_level->title);
3377         // X gets hosed if Flush/Sync are not user locked (at libX11-1.1.5 / libxcb-1.1.91)
3378         //   _XReply deadlocks in condition_wait waiting for xlib lock when waiters!=-1
3379         int locked  = get_window_lock();
3380         if( !locked ) lock_window("BC_WindowBase::flush");
3381         XFlush(top_level->display);
3382         if( !locked ) unlock_window();
3383 }
3384
3385 void BC_WindowBase::sync_display()
3386 {
3387         int locked  = get_window_lock();
3388         if( !locked ) lock_window("BC_WindowBase::sync_display");
3389         XSync(top_level->display, False);
3390         if( !locked ) unlock_window();
3391 }
3392
3393 int BC_WindowBase::get_window_lock()
3394 {
3395 #ifdef SINGLE_THREAD
3396         return BC_Display::display_global->get_display_locked();
3397 #else
3398         return top_level->window_lock;
3399 #endif
3400 }
3401
3402 int BC_WindowBase::lock_window(const char *location)
3403 {
3404         if(top_level && top_level != this)
3405         {
3406                 top_level->lock_window(location);
3407         }
3408         else
3409         if(top_level)
3410         {
3411                 SET_LOCK(this, title, location);
3412 #ifdef SINGLE_THREAD
3413                 BC_Display::lock_display(location);
3414 #else
3415                 XLockDisplay(top_level->display);
3416                 top_level->display_lock_owner = pthread_self();
3417 #endif
3418                 SET_LOCK2
3419                 ++top_level->window_lock;
3420         }
3421         else
3422         {
3423                 printf("BC_WindowBase::lock_window top_level NULL\n");
3424         }
3425         return 0;
3426 }
3427
3428 int BC_WindowBase::unlock_window()
3429 {
3430         if(top_level && top_level != this)
3431         {
3432                 top_level->unlock_window();
3433         }
3434         else
3435         if(top_level)
3436         {
3437                 UNSET_LOCK(this);
3438                 if( !top_level->window_lock ) {
3439                         printf("BC_WindowBase::unlock_window %s not locked\n", title);
3440                         booby();
3441                 }
3442                 if( top_level->window_lock > 0 )
3443                         if( --top_level->window_lock == 0 )
3444                                 top_level->display_lock_owner = 0;
3445 #ifdef SINGLE_THREAD
3446                 BC_Display::unlock_display();
3447 #else
3448                 XUnlockDisplay(top_level->display);
3449 #endif
3450         }
3451         else
3452         {
3453                 printf("BC_WindowBase::unlock_window top_level NULL\n");
3454         }
3455         return 0;
3456 }
3457
3458 int BC_WindowBase::break_lock()
3459 {
3460         if( !top_level ) return 0;
3461         if( top_level != this ) return top_level->break_lock();
3462         if( top_level->display_lock_owner != pthread_self() ) return 0;
3463         if( top_level->window_lock != 1 ) return 0;
3464         UNSET_LOCK(this);
3465         window_lock = 0;
3466         display_lock_owner = 0;
3467 #ifdef SINGLE_THREAD
3468         BC_Display::unlock_display();
3469 #else
3470         XUnlockDisplay(display);
3471 #endif
3472         return 1;
3473 }
3474
3475 void BC_WindowBase::set_done(int return_value)
3476 {
3477         if(done_set) return;
3478         done_set = 1;
3479         if(window_type != MAIN_WINDOW)
3480                 top_level->set_done(return_value);
3481         else
3482         {
3483 #ifdef SINGLE_THREAD
3484                 this->return_value = return_value;
3485                 BC_Display::display_global->arm_completion(this);
3486                 completion_lock->unlock();
3487 #else // SINGLE_THREAD
3488                 init_wait();
3489                 if( !event_thread ) return;
3490                 XEvent *event = new_xevent();
3491                 XClientMessageEvent *ptr = (XClientMessageEvent*)event;
3492                 event->type = ClientMessage;
3493                 ptr->message_type = SetDoneXAtom;
3494                 ptr->format = 32;
3495                 this->return_value = return_value;
3496 // May lock up here because XSendEvent doesn't work too well
3497 // asynchronous with XNextEvent.
3498 // This causes BC_WindowEvents to forward a copy of the event to run_window where
3499 // it is deleted.
3500 // Deletion of event_thread is done at the end of BC_WindowBase::run_window() - by calling the destructor
3501                 put_event(event);
3502 #endif
3503         }
3504 }
3505
3506 void BC_WindowBase::close(int return_value)
3507 {
3508         hide_window();  flush();
3509         set_done(return_value);
3510 }
3511
3512 int BC_WindowBase::grab(BC_WindowBase *window)
3513 {
3514         int ret = 0;
3515         BC_WindowBase *grab_window = window->active_grab;
3516         if( grab_window ) {
3517                 int locked = get_window_lock();
3518                 if( locked ) unlock_window();
3519                 grab_window->lock_window("BC_WindowBase::grab(BC_WindowBase");
3520                 grab_window->handle_ungrab();
3521                 grab_window->unlock_window();
3522                 if( locked ) lock_window("BC_WindowBase::grab(BC_WindowBase");
3523         }
3524         window->grab_lock->lock("BC_WindowBase::grab");
3525         if( !window->active_grab ) {
3526                 window->active_grab = this;
3527                 this->grab_active = window;
3528                 ret = 1;
3529         }
3530         window->grab_lock->unlock();
3531         return ret;
3532 }
3533 int BC_WindowBase::ungrab(BC_WindowBase *window)
3534 {
3535         int ret = 0;
3536         window->grab_lock->lock("BC_WindowBase::ungrab");
3537         if( this == window->active_grab ) {
3538                 window->active_grab = 0;
3539                 this->grab_active = 0;
3540                 ret = 1;
3541         }
3542         window->grab_lock->unlock();
3543         return ret;
3544 }
3545 int BC_WindowBase::grab_event_count()
3546 {
3547         int result = 0;
3548 #ifndef SINGLE_THREAD
3549         result = grab_active->get_event_count();
3550 #endif
3551         return result;
3552 }
3553 int BC_WindowBase::grab_buttons()
3554 {
3555         XSync(top_level->display, False);
3556         if( XGrabButton(top_level->display, AnyButton, AnyModifier,
3557                         top_level->rootwin, True, ButtonPressMask | ButtonReleaseMask,
3558                         GrabModeAsync, GrabModeSync, None, None) == GrabSuccess ) {
3559                 set_active_subwindow(this);
3560                 return 0;
3561         }
3562         return 1;
3563 }
3564 void BC_WindowBase::ungrab_buttons()
3565 {
3566         XUngrabButton(top_level->display, AnyButton, AnyModifier, top_level->rootwin);
3567         set_active_subwindow(0);
3568         unhide_cursor();
3569 }
3570 void BC_WindowBase::grab_cursor()
3571 {
3572         Cursor cursor_grab = get_cursor_struct(GRABBED_CURSOR);
3573         XGrabPointer(top_level->display, top_level->rootwin, True,
3574                 PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
3575                 GrabModeAsync, GrabModeAsync, None, cursor_grab, CurrentTime);
3576 }
3577 void BC_WindowBase::ungrab_cursor()
3578 {
3579         XUngrabPointer(top_level->display, CurrentTime);
3580 }
3581
3582 // for get_root_w/h
3583 //   WidthOfScreen/HeightOfScreen of XDefaultScreenOfDisplay
3584 //   this is the bounding box of all the screens
3585
3586 int BC_WindowBase::get_root_w(int lock_display)
3587 {
3588         if(lock_display) lock_window("BC_WindowBase::get_root_w");
3589         Screen *def_screen = XDefaultScreenOfDisplay(top_level->display);
3590         int result = WidthOfScreen(def_screen);
3591         if(lock_display) unlock_window();
3592         return result;
3593 }
3594
3595 int BC_WindowBase::get_root_h(int lock_display)
3596 {
3597         if(lock_display) lock_window("BC_WindowBase::get_root_h");
3598         Screen *def_screen = XDefaultScreenOfDisplay(top_level->display);
3599         int result = HeightOfScreen(def_screen);
3600         if(lock_display) unlock_window();
3601         return result;
3602 }
3603
3604 XineramaScreenInfo *
3605 BC_WindowBase::get_xinerama_info(int screen)
3606 {
3607         if( !xinerama_info || !xinerama_screens ) return 0;
3608         if( screen >= 0 ) {
3609                 for( int i=0; i<xinerama_screens; ++i )
3610                         if( xinerama_info[i].screen_number == screen )
3611                                 return &xinerama_info[i];
3612                 return 0;
3613         }
3614         int top_x = get_x(), top_y = get_y();
3615         if(  BC_DisplayInfo::left_border >= 0 ) top_x +=  BC_DisplayInfo::left_border;
3616         if(  BC_DisplayInfo::top_border >= 0 ) top_y +=  BC_DisplayInfo::top_border;
3617         for( int i=0; i<xinerama_screens; ++i ) {
3618                 int scr_y = top_y - xinerama_info[i].y_org;
3619                 if( scr_y < 0 || scr_y >= xinerama_info[i].height ) continue;
3620                 int scr_x = top_x - xinerama_info[i].x_org;
3621                 if( scr_x >= 0 && scr_x < xinerama_info[i].width )
3622                         return &xinerama_info[i];
3623         }
3624         return 0;
3625 }
3626
3627 void BC_WindowBase::get_fullscreen_geometry(int &wx, int &wy, int &ww, int &wh)
3628 {
3629         XineramaScreenInfo *info = top_level->get_xinerama_info(-1);
3630         if( info ) {
3631                 wx = info->x_org;  wy = info->y_org;
3632                 ww = info->width;  wh = info->height;
3633         }
3634         else {
3635                 wx = get_screen_x(0, -1);
3636                 wy = get_screen_y(0, -1);
3637                 int scr_w0 = get_screen_w(0, 0);
3638                 int root_w = get_root_w(0);
3639                 int root_h = get_root_h(0);
3640                 if( root_w > scr_w0 ) { // multi-headed
3641                         if( wx >= scr_w0 ) {
3642                                 // assumes right side is the big one
3643                                 ww = root_w - scr_w0;
3644                                 wh = root_h;
3645                         }
3646                         else {
3647                                 // use same aspect ratio to compute left height
3648                                 ww = scr_w0;
3649                                 wh = (w*root_h) / (root_w-scr_w0);
3650                         }
3651                 }
3652                 else {
3653                         ww = root_w;
3654                         wh = root_h;
3655                 }
3656         }
3657 }
3658
3659 int BC_WindowBase::get_screen_x(int lock_display, int screen)
3660 {
3661         int result = -1;
3662         if(lock_display) lock_window("BC_WindowBase::get_screen_x");
3663         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3664         if( !info ) {
3665                 result = 0;
3666                 int root_w = get_root_w(0);
3667                 int root_h = get_root_h(0);
3668 // Shift X based on position of current window if dual head
3669                 if( (float)root_w/root_h > 1.8 ) {
3670                         root_w = get_screen_w(0, 0);
3671                         if( top_level->get_x() >= root_w )
3672                                 result = root_w;
3673                 }
3674         }
3675         else
3676                 result = info->x_org;
3677         if(lock_display) unlock_window();
3678         return result;
3679 }
3680
3681 int BC_WindowBase::get_screen_y(int lock_display, int screen)
3682 {
3683         if(lock_display) lock_window("BC_WindowBase::get_screen_y");
3684         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3685         int result = !info ? 0 : info->y_org;
3686         if(lock_display) unlock_window();
3687         return result;
3688 }
3689
3690 int BC_WindowBase::get_screen_w(int lock_display, int screen)
3691 {
3692         int result = -1;
3693         if(lock_display) lock_window("BC_WindowBase::get_screen_w");
3694         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3695         if( !info ) {
3696                 int width = get_root_w(0);
3697                 int height = get_root_h(0);
3698                 if( (float)width/height > 1.8 ) {
3699                         // If dual head, the screen width is > 16x9
3700                         // but we only want to fill one screen
3701                         // this code assumes the "big" screen is on the right
3702                         int scr_w0 = width / 2;
3703                         switch( height ) {
3704                         case 600:  scr_w0 = 800;   break;
3705                         case 720:  scr_w0 = 1280;  break;
3706                         case 1024: scr_w0 = 1280;  break;
3707                         case 1200: scr_w0 = 1600;  break;
3708                         case 1080: scr_w0 = 1920;  break;
3709                         }
3710                         int scr_w1 = width - scr_w0;
3711                         result = screen > 0 ? scr_w1 :
3712                                 screen == 0 ? scr_w0 :
3713                                 top_level->get_x() < scr_w0 ? scr_w0 : scr_w1;
3714                 }
3715                 else
3716                         result = width;
3717         }
3718         else
3719                 result = info->width;
3720         if(lock_display) unlock_window();
3721         return result;
3722 }
3723
3724 int BC_WindowBase::get_screen_h(int lock_display, int screen)
3725 {
3726         if(lock_display) lock_window("BC_WindowBase::get_screen_h");
3727         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3728         int result = info ? info->height : get_root_h(0);
3729         if(lock_display) unlock_window();
3730         return result;
3731 }
3732
3733 // Bottom right corner
3734 int BC_WindowBase::get_x2()
3735 {
3736         return w + x;
3737 }
3738
3739 int BC_WindowBase::get_y2()
3740 {
3741         return y + h;
3742 }
3743
3744 int BC_WindowBase::get_video_on()
3745 {
3746         return video_on;
3747 }
3748
3749 int BC_WindowBase::get_hidden()
3750 {
3751         return top_level->hidden;
3752 }
3753
3754 int BC_WindowBase::cursor_inside()
3755 {
3756         return (top_level->cursor_x >= 0 &&
3757                         top_level->cursor_y >= 0 &&
3758                         top_level->cursor_x < w &&
3759                         top_level->cursor_y < h);
3760 }
3761
3762 BC_WindowBase* BC_WindowBase::get_top_level()
3763 {
3764         return top_level;
3765 }
3766
3767 BC_WindowBase* BC_WindowBase::get_parent()
3768 {
3769         return parent_window;
3770 }
3771
3772 int BC_WindowBase::get_color_model()
3773 {
3774         return top_level->color_model;
3775 }
3776
3777 BC_Resources* BC_WindowBase::get_resources()
3778 {
3779         return BC_WindowBase::resources;
3780 }
3781
3782 BC_Synchronous* BC_WindowBase::get_synchronous()
3783 {
3784         return BC_WindowBase::resources->get_synchronous();
3785 }
3786
3787 int BC_WindowBase::get_bg_color()
3788 {
3789         return bg_color;
3790 }
3791
3792 void BC_WindowBase::set_bg_color(int color)
3793 {
3794         this->bg_color = color;
3795 }
3796
3797 BC_Pixmap* BC_WindowBase::get_bg_pixmap()
3798 {
3799         return bg_pixmap;
3800 }
3801
3802 void BC_WindowBase::set_active_subwindow(BC_WindowBase *subwindow)
3803 {
3804         top_level->active_subwindow = subwindow;
3805 }
3806
3807 int BC_WindowBase::activate()
3808 {
3809         return 0;
3810 }
3811
3812 int BC_WindowBase::deactivate()
3813 {
3814         if(window_type == MAIN_WINDOW)
3815         {
3816                 if( top_level->active_menubar ) {
3817                         top_level->active_menubar->deactivate();
3818                         top_level->active_menubar = 0;
3819                 }
3820                 if( top_level->active_popup_menu ) {
3821                         top_level->active_popup_menu->deactivate();
3822                         top_level->active_popup_menu = 0;
3823                 }
3824                 if( top_level->active_subwindow ) {
3825                         top_level->active_subwindow->deactivate();
3826                         top_level->active_subwindow = 0;
3827                 }
3828                 if( top_level->motion_events && top_level->last_motion_win == this->win )
3829                         top_level->motion_events = 0;
3830
3831         }
3832         return 0;
3833 }
3834
3835 int BC_WindowBase::cycle_textboxes(int amount)
3836 {
3837         int result = 0;
3838         BC_WindowBase *new_textbox = 0;
3839
3840         if(amount > 0)
3841         {
3842                 BC_WindowBase *first_textbox = 0;
3843                 find_next_textbox(&first_textbox, &new_textbox, result);
3844                 if(!new_textbox) new_textbox = first_textbox;
3845
3846         }
3847         else
3848         if(amount < 0)
3849         {
3850                 BC_WindowBase *last_textbox = 0;
3851                 find_prev_textbox(&last_textbox, &new_textbox, result);
3852                 if(!new_textbox) new_textbox = last_textbox;
3853
3854         }
3855
3856         if(new_textbox != active_subwindow)
3857         {
3858                 deactivate();
3859                 new_textbox->activate();
3860         }
3861
3862         return 0;
3863 }
3864
3865 int BC_WindowBase::find_next_textbox(BC_WindowBase **first_textbox, BC_WindowBase **next_textbox, int &result)
3866 {
3867 // Search subwindows for textbox
3868         for(int i = 0; i < subwindows->total && result < 2; i++)
3869         {
3870                 BC_WindowBase *test_subwindow = subwindows->values[i];
3871                 test_subwindow->find_next_textbox(first_textbox, next_textbox, result);
3872         }
3873
3874         if(result < 2)
3875         {
3876                 if(uses_text())
3877                 {
3878                         if(!*first_textbox) *first_textbox = this;
3879
3880                         if(result < 1)
3881                         {
3882                                 if(top_level->active_subwindow == this)
3883                                         result++;
3884                         }
3885                         else
3886                         {
3887                                 result++;
3888                                 *next_textbox = this;
3889                         }
3890                 }
3891         }
3892         return 0;
3893 }
3894
3895 int BC_WindowBase::find_prev_textbox(BC_WindowBase **last_textbox, BC_WindowBase **prev_textbox, int &result)
3896 {
3897         if(result < 2)
3898         {
3899                 if(uses_text())
3900                 {
3901                         if(!*last_textbox) *last_textbox = this;
3902
3903                         if(result < 1)
3904                         {
3905                                 if(top_level->active_subwindow == this)
3906                                         result++;
3907                         }
3908                         else
3909                         {
3910                                 result++;
3911                                 *prev_textbox = this;
3912                         }
3913                 }
3914         }
3915
3916 // Search subwindows for textbox
3917         for(int i = subwindows->total - 1; i >= 0 && result < 2; i--)
3918         {
3919                 BC_WindowBase *test_subwindow = subwindows->values[i];
3920                 test_subwindow->find_prev_textbox(last_textbox, prev_textbox, result);
3921         }
3922         return 0;
3923 }
3924
3925 BC_Clipboard* BC_WindowBase::get_clipboard()
3926 {
3927 #ifdef SINGLE_THREAD
3928         return BC_Display::display_global->clipboard;
3929 #else
3930         return top_level->clipboard;
3931 #endif
3932 }
3933
3934 Atom BC_WindowBase::to_clipboard(const char *data, long len, int clipboard_num)
3935 {
3936         return get_clipboard()->to_clipboard(this, data, len, clipboard_num);
3937 }
3938
3939 long BC_WindowBase::from_clipboard(char *data, long maxlen, int clipboard_num)
3940 {
3941         return get_clipboard()->from_clipboard(data, maxlen, clipboard_num);
3942 }
3943
3944 long BC_WindowBase::clipboard_len(int clipboard_num)
3945 {
3946         return get_clipboard()->clipboard_len(clipboard_num);
3947 }
3948
3949 int BC_WindowBase::do_selection_clear(Window win)
3950 {
3951         top_level->event_win = win;
3952         return dispatch_selection_clear();
3953 }
3954
3955 int BC_WindowBase::dispatch_selection_clear()
3956 {
3957         int result = 0;
3958         for( int i=0; i<subwindows->total && !result; ++i )
3959                 result = subwindows->values[i]->dispatch_selection_clear();
3960         if( !result )
3961                 result = selection_clear_event();
3962         return result;
3963 }
3964
3965
3966 void BC_WindowBase::get_relative_cursor(int &x, int &y, int lock_window)
3967 {
3968         int abs_x, abs_y, win_x, win_y;
3969         unsigned int temp_mask;
3970         Window temp_win;
3971
3972         if(lock_window) this->lock_window("BC_WindowBase::get_relative_cursor");
3973         XQueryPointer(top_level->display, top_level->win,
3974            &temp_win, &temp_win, &abs_x, &abs_y, &win_x, &win_y,
3975            &temp_mask);
3976
3977         XTranslateCoordinates(top_level->display, top_level->rootwin,
3978            win, abs_x, abs_y, &x, &y, &temp_win);
3979         if(lock_window) this->unlock_window();
3980 }
3981 int BC_WindowBase::get_relative_cursor_x(int lock_window)
3982 {
3983         int x, y;
3984         get_relative_cursor(x, y, lock_window);
3985         return x;
3986 }
3987 int BC_WindowBase::get_relative_cursor_y(int lock_window)
3988 {
3989         int x, y;
3990         get_relative_cursor(x, y, lock_window);
3991         return y;
3992 }
3993
3994 void BC_WindowBase::get_abs_cursor(int &abs_x, int &abs_y, int lock_window)
3995 {
3996         int win_x, win_y;
3997         unsigned int temp_mask;
3998         Window temp_win;
3999
4000         if(lock_window) this->lock_window("BC_WindowBase::get_abs_cursor");
4001         XQueryPointer(top_level->display, top_level->win,
4002                 &temp_win, &temp_win, &abs_x, &abs_y, &win_x, &win_y,
4003                 &temp_mask);
4004         if(lock_window) this->unlock_window();
4005 }
4006 int BC_WindowBase::get_abs_cursor_x(int lock_window)
4007 {
4008         int abs_x, abs_y;
4009         get_abs_cursor(abs_x, abs_y, lock_window);
4010         return abs_x;
4011 }
4012 int BC_WindowBase::get_abs_cursor_y(int lock_window)
4013 {
4014         int abs_x, abs_y;
4015         get_abs_cursor(abs_x, abs_y, lock_window);
4016         return abs_y;
4017 }
4018
4019 void BC_WindowBase::get_pop_cursor(int &px, int &py, int lock_window)
4020 {
4021         int xmargin = xS(100), ymargin = yS(100);
4022         get_abs_cursor(px, py, lock_window);
4023         if( px < xmargin ) px = xmargin;
4024         if( py < ymargin ) py = ymargin;
4025         int wd = get_screen_x(lock_window,-1) + get_screen_w(lock_window,-1) - xmargin;
4026         if( px > wd ) px = wd;
4027         int ht = get_screen_y(lock_window,-1) + get_screen_h(lock_window,-1) - ymargin;
4028         if( py > ht ) py = ht;
4029 }
4030 int BC_WindowBase::get_pop_cursor_x(int lock_window)
4031 {
4032         int px, py;
4033         get_pop_cursor(px, py, lock_window);
4034         return px;
4035 }
4036 int BC_WindowBase::get_pop_cursor_y(int lock_window)
4037 {
4038         int px, py;
4039         get_pop_cursor(px, py, lock_window);
4040         return py;
4041 }
4042
4043 int BC_WindowBase::match_window(Window win)
4044 {
4045         if (this->win == win) return 1;
4046         int result = 0;
4047         for(int i = 0; i < subwindows->total; i++) {
4048                 result = subwindows->values[i]->match_window(win);
4049                 if (result) return result;
4050         }
4051         return 0;
4052
4053 }
4054
4055 int BC_WindowBase::get_cursor_over_window()
4056 {
4057         int abs_x, abs_y, win_x, win_y;
4058         unsigned int mask_return;
4059         Window root_return, child_return;
4060
4061         int ret = XQueryPointer(top_level->display, top_level->rootwin,
4062                 &root_return, &child_return, &abs_x, &abs_y,
4063                 &win_x, &win_y, &mask_return);
4064         if( ret && child_return == None ) ret = 0;
4065         if( ret && win != child_return )
4066                 ret = top_level->match_window(child_return);
4067 // query pointer can return a window manager window with this top_level as a child
4068 //  for kde this can be two levels deep
4069         unsigned int nchildren_return = 0;
4070         Window parent_return, *children_return = 0;
4071         Window top_win = top_level->win;
4072         while( !ret && top_win != top_level->rootwin && top_win != root_return &&
4073                 XQueryTree(top_level->display, top_win, &root_return,
4074                         &parent_return, &children_return, &nchildren_return) ) {
4075                 if( children_return ) XFree(children_return);
4076                 if( (top_win=parent_return) == child_return ) ret = 1;
4077         }
4078         return ret;
4079 }
4080
4081 int BC_WindowBase::cursor_above()
4082 {
4083         int rx, ry;
4084         get_relative_cursor(rx, ry);
4085         return rx < 0 || rx >= get_w() ||
4086                 ry < 0 || ry >= get_h() ? 0 : 1;
4087 }
4088
4089 int BC_WindowBase::get_drag_x()
4090 {
4091         return top_level->drag_x;
4092 }
4093
4094 int BC_WindowBase::get_drag_y()
4095 {
4096         return top_level->drag_y;
4097 }
4098
4099 int BC_WindowBase::get_cursor_x()
4100 {
4101         return top_level->cursor_x;
4102 }
4103
4104 int BC_WindowBase::get_cursor_y()
4105 {
4106         return top_level->cursor_y;
4107 }
4108
4109 int BC_WindowBase::dump_windows()
4110 {
4111         printf("\tBC_WindowBase::dump_windows window=%p win=%p '%s', %dx%d+%d+%d %s\n",
4112                 this, (void*)this->win, title, w,h,x,y, typeid(*this).name());
4113         for(int i = 0; i < subwindows->size(); i++)
4114                 subwindows->get(i)->dump_windows();
4115         for(int i = 0; i < popups.size(); i++) {
4116                 BC_WindowBase *p = popups[i];
4117                 printf("\tBC_WindowBase::dump_windows popup=%p win=%p '%s', %dx%d+%d+%d %s\n",
4118                         p, (void*)p->win, p->title, p->w,p->h,p->x,p->y, typeid(*p).name());
4119         }
4120         return 0;
4121 }
4122
4123 int BC_WindowBase::is_event_win()
4124 {
4125         return this->win == top_level->event_win;
4126 }
4127
4128 int BC_WindowBase::is_tooltip_event_win()
4129 {
4130         return this->win == top_level->event_win ||
4131                 tooltip_popup && tooltip_popup->win == top_level->event_win;
4132 }
4133
4134 void BC_WindowBase::set_dragging(int value)
4135 {
4136         is_dragging = value;
4137 }
4138
4139 int BC_WindowBase::get_dragging()
4140 {
4141         return is_dragging;
4142 }
4143
4144 int BC_WindowBase::get_buttonpress()
4145 {
4146         return top_level->button_number;
4147 }
4148
4149 int BC_WindowBase::get_button_down()
4150 {
4151         return top_level->button_down;
4152 }
4153
4154 int BC_WindowBase::alt_down()
4155 {
4156         return top_level->alt_mask;
4157 }
4158
4159 int BC_WindowBase::shift_down()
4160 {
4161         return top_level->shift_mask;
4162 }
4163
4164 int BC_WindowBase::ctrl_down()
4165 {
4166         return top_level->ctrl_mask;
4167 }
4168
4169 wchr_t* BC_WindowBase::get_wkeystring(int *length)
4170 {
4171         if(length)
4172                 *length = top_level->wkey_string_length;
4173         return top_level->wkey_string;
4174 }
4175
4176 #ifdef X_HAVE_UTF8_STRING
4177 char* BC_WindowBase::get_keypress_utf8()
4178 {
4179         return top_level->key_pressed_utf8;
4180 }
4181 #endif
4182
4183
4184 int BC_WindowBase::get_keypress()
4185 {
4186         return top_level->key_pressed;
4187 }
4188
4189 int BC_WindowBase::get_double_click()
4190 {
4191         return top_level->double_click;
4192 }
4193
4194 int BC_WindowBase::get_triple_click()
4195 {
4196         return top_level->triple_click;
4197 }
4198
4199 int BC_WindowBase::get_bgcolor()
4200 {
4201         return bg_color;
4202 }
4203
4204 int BC_WindowBase::resize_window(int w, int h)
4205 {
4206         if(this->w == w && this->h == h) return 0;
4207
4208         if(window_type == MAIN_WINDOW && !allow_resize)
4209         {
4210                 XSizeHints size_hints;
4211                 size_hints.flags = PSize | PMinSize | PMaxSize;
4212                 size_hints.width = w;
4213                 size_hints.height = h;
4214                 size_hints.min_width = w;
4215                 size_hints.max_width = w;
4216                 size_hints.min_height = h;
4217                 size_hints.max_height = h;
4218                 if( this->x > -BC_INFINITY && this->x < BC_INFINITY ) {
4219                         size_hints.flags |= PPosition;
4220                         size_hints.x = this->x;
4221                         size_hints.y = this->y;
4222                 }
4223                 XSetNormalHints(top_level->display, win, &size_hints);
4224         }
4225         XResizeWindow(top_level->display, win, w, h);
4226
4227         this->w = w;
4228         this->h = h;
4229         delete pixmap;
4230         pixmap = new BC_Pixmap(this, w, h);
4231
4232 // Propagate to menubar
4233         for(int i = 0; i < subwindows->total; i++)
4234         {
4235                 subwindows->values[i]->dispatch_resize_event(w, h);
4236         }
4237
4238         draw_background(0, 0, w, h);
4239         if(top_level == this && get_resources()->recursive_resizing)
4240                 resize_history.append(new BC_ResizeCall(w, h));
4241         return 0;
4242 }
4243
4244 // The only way for resize events to be propagated is by updating the internal w and h
4245 int BC_WindowBase::resize_event(int w, int h)
4246 {
4247         if(window_type == MAIN_WINDOW)
4248         {
4249                 this->w = w;
4250                 this->h = h;
4251         }
4252         return 0;
4253 }
4254
4255 int BC_WindowBase::reposition_window(int x, int y)
4256 {
4257         reposition_window(x, y, -1, -1);
4258         return 0;
4259 }
4260
4261
4262 int BC_WindowBase::reposition_window(int x, int y, int w, int h)
4263 {
4264         int resize = 0;
4265
4266 // Some tools set their own dimensions before calling this, causing the
4267 // resize check to skip.
4268         this->x = x;
4269         this->y = y;
4270
4271         if(w > 0 && w != this->w)
4272         {
4273                 resize = 1;
4274                 this->w = w;
4275         }
4276
4277         if(h > 0 && h != this->h)
4278         {
4279                 resize = 1;
4280                 this->h = h;
4281         }
4282
4283 //printf("BC_WindowBase::reposition_window %d %d %d\n", translation_count, x_correction, y_correction);
4284
4285         if(this->w <= 0)
4286                 printf("BC_WindowBase::reposition_window this->w == %d\n", this->w);
4287         if(this->h <= 0)
4288                 printf("BC_WindowBase::reposition_window this->h == %d\n", this->h);
4289
4290         if(translation_count && window_type == MAIN_WINDOW)
4291         {
4292 // KDE shifts window right and down.
4293 // FVWM leaves window alone and adds border around it.
4294                 XMoveResizeWindow(top_level->display, win,
4295                         x - BC_DisplayInfo::auto_reposition_x,
4296                         y - BC_DisplayInfo::auto_reposition_y,
4297                         this->w, this->h);
4298         }
4299         else
4300         {
4301                 XMoveResizeWindow(top_level->display, win, x, y,
4302                         this->w, this->h);
4303         }
4304
4305         if(resize)
4306         {
4307                 delete pixmap;
4308                 pixmap = new BC_Pixmap(this, this->w, this->h);
4309                 clear_box(0,0, this->w, this->h);
4310 // Propagate to menubar
4311                 for(int i = 0; i < subwindows->total; i++)
4312                 {
4313                         subwindows->values[i]->dispatch_resize_event(this->w, this->h);
4314                 }
4315
4316 //              draw_background(0, 0, w, h);
4317         }
4318
4319         return 0;
4320 }
4321
4322 int BC_WindowBase::reposition_window_relative(int dx, int dy, int w, int h)
4323 {
4324         return reposition_window(get_x()+dx, get_y()+dy, w, h);
4325 }
4326
4327 int BC_WindowBase::reposition_window_relative(int dx, int dy)
4328 {
4329         return reposition_window_relative(dx, dy, -1, -1);
4330 }
4331
4332 void BC_WindowBase::set_tooltips(int v)
4333 {
4334         get_resources()->tooltips_enabled = v;
4335 }
4336
4337 void BC_WindowBase::set_force_tooltip(int v)
4338 {
4339         force_tooltip = v;
4340 }
4341
4342 int BC_WindowBase::raise_window(int do_flush)
4343 {
4344         if( hidden ) return 1;
4345         if( wait_viewable(500) ) return 1;
4346         XRaiseWindow(top_level->display, win);
4347         if(do_flush) XFlush(top_level->display);
4348         return 0;
4349 }
4350
4351 int BC_WindowBase::lower_window(int do_flush)
4352 {
4353         XLowerWindow(top_level->display, win);
4354         if(do_flush) XFlush(top_level->display);
4355         return 0;
4356 }
4357
4358 void BC_WindowBase::set_background(VFrame *bitmap)
4359 {
4360         if(bg_pixmap && !shared_bg_pixmap) delete bg_pixmap;
4361
4362         bg_pixmap = new BC_Pixmap(this, bitmap, PIXMAP_OPAQUE);
4363         shared_bg_pixmap = 0;
4364         draw_background(0, 0, w, h);
4365 }
4366
4367 void BC_WindowBase::put_title(const char *text)
4368 {
4369         char *cp = this->title, *ep = cp+sizeof(this->title)-1;
4370         for( const unsigned char *bp = (const unsigned char *)text; *bp && cp<ep; ++bp )
4371                 *cp++ = *bp >= ' ' ? *bp : ' ';
4372         *cp = 0;
4373 }
4374
4375 void BC_WindowBase::set_title(const char *text, int utf8)
4376 {
4377 // utf8>0: wm + net_wm, utf8=0: wm only,  utf<0: net_wm only
4378         put_title(text);
4379         const unsigned char *wm_title = (const unsigned char *)title;
4380         int title_len = strlen((const char *)title);
4381         if( utf8 >= 0 ) {
4382                 Atom xa_wm_name = XA_WM_NAME;
4383                 Atom xa_icon_name = XA_WM_ICON_NAME;
4384                 Atom xa_string = XA_STRING;
4385                 XChangeProperty(display, win, xa_wm_name, xa_string, 8,
4386                                 PropModeReplace, wm_title, title_len);
4387                 XChangeProperty(display, win, xa_icon_name, xa_string, 8,
4388                                 PropModeReplace, wm_title, title_len);
4389         }
4390         if( utf8 != 0 ) {
4391                 Atom xa_net_wm_name = XInternAtom(display, "_NET_WM_NAME", True);
4392                 Atom xa_net_icon_name = XInternAtom(display, "_NET_WM_ICON_NAME", True);
4393                 Atom xa_utf8_string = XInternAtom(display, "UTF8_STRING", True);
4394                 XChangeProperty(display, win, xa_net_wm_name, xa_utf8_string, 8,
4395                                         PropModeReplace, wm_title, title_len);
4396                 XChangeProperty(display, win, xa_net_icon_name, xa_utf8_string, 8,
4397                                         PropModeReplace, wm_title, title_len);
4398         }
4399         flush();
4400 }
4401
4402 // must be RGBA8888
4403 void BC_WindowBase::set_net_icon(VFrame *data)
4404 {
4405         int width = data->get_w(), height = data->get_h();
4406         int size = 2 + width * height;
4407         unsigned long *icon_data = new unsigned long[size];
4408         unsigned long *lp = icon_data;
4409         *lp++ = width;  *lp++ = height;
4410         uint8_t **rows = data->get_rows();
4411         for( int y=0; y<height; ++y ) {
4412                 unsigned *up = (unsigned *)rows[y];
4413                 for( int x=0; x<width; ++x )
4414                         *lp++ = *(unsigned *)up++;
4415         }
4416         Atom NetWMIcon = XInternAtom(display, "_NET_WM_ICON", True);
4417         XChangeProperty(top_level->display, top_level->win, NetWMIcon,
4418                 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)icon_data, size);
4419         delete [] icon_data;
4420 }
4421
4422 const char *BC_WindowBase::get_title()
4423 {
4424         return title;
4425 }
4426
4427 int BC_WindowBase::get_toggle_value()
4428 {
4429         return toggle_value;
4430 }
4431
4432 int BC_WindowBase::get_toggle_drag()
4433 {
4434         return toggle_drag;
4435 }
4436
4437 int BC_WindowBase::set_icon(VFrame *data)
4438 {
4439         if(icon_pixmap) delete icon_pixmap;
4440         icon_pixmap = new BC_Pixmap(top_level, data, PIXMAP_ALPHA, 1);
4441
4442         if(icon_window) delete icon_window;
4443         icon_window = new BC_Popup(this, 0, 0,
4444                 icon_pixmap->get_w(), icon_pixmap->get_h(),
4445                 -1, 1, // All windows are hidden initially
4446                 icon_pixmap);
4447
4448         XWMHints wm_hints;  memset(&wm_hints, 0, sizeof(wm_hints));
4449         wm_hints.flags = IconPixmapHint; // | IconMaskHint | IconWindowHint;
4450         wm_hints.icon_pixmap = icon_pixmap->get_pixmap();
4451         wm_hints.icon_mask = icon_pixmap->get_alpha();
4452         wm_hints.icon_window = icon_window->win;
4453         if( XGroupLeader ) {
4454                 wm_hints.flags |= WindowGroupHint;
4455                 wm_hints.window_group = XGroupLeader;
4456         }
4457
4458 // for(int i = 0; i < 1000; i++)
4459 // printf("02x ", icon_pixmap->get_alpha()->get_row_pointers()[0][i]);
4460 // printf("\n");
4461
4462         XSetWMHints(top_level->display, top_level->win, &wm_hints);
4463
4464         set_net_icon(data);
4465         XSync(top_level->display, 0);
4466         return 0;
4467 }
4468
4469 void BC_WindowBase::init_resources(float scale)
4470 {
4471         if( resources ) return;
4472         XInitThreads();
4473         const char *env = getenv("BC_SCALE");
4474         if( env ) scale = atof(env);
4475         float x_scale = 1, y_scale = 1;
4476         if( scale <= 0 ) {
4477                 BC_DisplayInfo info;
4478                 int wx, wy, ww, wh;
4479                 int cins = info.xinerama_big_screen();
4480                 if( !info.xinerama_geometry(cins, wx, wy, ww, wh) ) {
4481                         int sh = ww * 9 / 16;
4482                         int sw = wh * 16 / 9;
4483                         if( sw < ww ) ww = sw;
4484                         if( sh < wh ) wh = sh;
4485                         if( (x_scale = ww/1920.) < 1 ) x_scale = 1;
4486                         if( (y_scale = wh/1080.) < 1 ) y_scale = 1;
4487                 }
4488         }
4489         else
4490                 x_scale = y_scale = scale;
4491         // constructor sets BC_WindowBase::resources
4492         new BC_Resources(x_scale, y_scale);
4493 }
4494 void BC_WindowBase::finit_resources()
4495 {
4496         delete resources;  resources = 0;
4497 }
4498
4499 int BC_WindowBase::set_w(int w)
4500 {
4501         this->w = w;
4502         return 0;
4503 }
4504
4505 int BC_WindowBase::set_h(int h)
4506 {
4507         this->h = h;
4508         return 0;
4509 }
4510
4511 int BC_WindowBase::load_defaults(BC_Hash *defaults)
4512 {
4513         char string[BCTEXTLEN];
4514         int newest_id = - 1;
4515         for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++)
4516         {
4517                 sprintf(string, "FILEBOX_HISTORY_PATH%d", i);
4518                 resources->filebox_history[i].path[0] = 0;
4519                 defaults->get(string, resources->filebox_history[i].path);
4520                 sprintf(string, "FILEBOX_HISTORY_ID%d", i);
4521                 resources->filebox_history[i].id = defaults->get(string, resources->get_id());
4522                 if(resources->filebox_history[i].id > newest_id)
4523                         newest_id = resources->filebox_history[i].id;
4524         }
4525
4526         resources->filebox_id = newest_id + 1;
4527         resources->filebox_mode = defaults->get("FILEBOX_MODE", get_resources()->filebox_mode);
4528         resources->filebox_w = defaults->get("FILEBOX_W", get_resources()->filebox_w);
4529         resources->filebox_h = defaults->get("FILEBOX_H", get_resources()->filebox_h);
4530         resources->filebox_columntype[0] = defaults->get("FILEBOX_TYPE0", resources->filebox_columntype[0]);
4531         resources->filebox_columntype[1] = defaults->get("FILEBOX_TYPE1", resources->filebox_columntype[1]);
4532         resources->filebox_columntype[2] = defaults->get("FILEBOX_TYPE2", resources->filebox_columntype[2]);
4533         resources->filebox_columntype[3] = defaults->get("FILEBOX_TYPE3", resources->filebox_columntype[3]);
4534         resources->filebox_columnwidth[0] = defaults->get("FILEBOX_WIDTH0", resources->filebox_columnwidth[0]);
4535         resources->filebox_columnwidth[1] = defaults->get("FILEBOX_WIDTH1", resources->filebox_columnwidth[1]);
4536         resources->filebox_columnwidth[2] = defaults->get("FILEBOX_WIDTH2", resources->filebox_columnwidth[2]);
4537         resources->filebox_columnwidth[3] = defaults->get("FILEBOX_WIDTH3", resources->filebox_columnwidth[3]);
4538         resources->filebox_size_format = defaults->get("FILEBOX_SIZE_FORMAT", get_resources()->filebox_size_format);
4539         defaults->get("FILEBOX_FILTER", resources->filebox_filter);
4540         return 0;
4541 }
4542
4543 int BC_WindowBase::save_defaults(BC_Hash *defaults)
4544 {
4545         char string[BCTEXTLEN];
4546         for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++)
4547         {
4548                 sprintf(string, "FILEBOX_HISTORY_PATH%d", i);
4549                 defaults->update(string, resources->filebox_history[i].path);
4550                 sprintf(string, "FILEBOX_HISTORY_ID%d", i);
4551                 defaults->update(string, resources->filebox_history[i].id);
4552         }
4553         defaults->update("FILEBOX_MODE", resources->filebox_mode);
4554         defaults->update("FILEBOX_W", resources->filebox_w);
4555         defaults->update("FILEBOX_H", resources->filebox_h);
4556         defaults->update("FILEBOX_TYPE0", resources->filebox_columntype[0]);
4557         defaults->update("FILEBOX_TYPE1", resources->filebox_columntype[1]);
4558         defaults->update("FILEBOX_TYPE2", resources->filebox_columntype[2]);
4559         defaults->update("FILEBOX_TYPE3", resources->filebox_columntype[3]);
4560         defaults->update("FILEBOX_WIDTH0", resources->filebox_columnwidth[0]);
4561         defaults->update("FILEBOX_WIDTH1", resources->filebox_columnwidth[1]);
4562         defaults->update("FILEBOX_WIDTH2", resources->filebox_columnwidth[2]);
4563         defaults->update("FILEBOX_WIDTH3", resources->filebox_columnwidth[3]);
4564         defaults->update("FILEBOX_FILTER", resources->filebox_filter);
4565         defaults->update("FILEBOX_SIZE_FORMAT", get_resources()->filebox_size_format);
4566         return 0;
4567 }
4568
4569
4570
4571 // For some reason XTranslateCoordinates can take a long time to return.
4572 // We work around this by only calling it when the event windows are different.
4573 void BC_WindowBase::translate_coordinates(Window src_w, Window dest_w,
4574                 int src_x, int src_y, int *dest_x_return, int *dest_y_return)
4575 {
4576         Window tempwin = 0;
4577 //Timer timer;
4578 //timer.update();
4579         if(src_w == dest_w)
4580         {
4581                 *dest_x_return = src_x;
4582                 *dest_y_return = src_y;
4583         }
4584         else
4585         {
4586                 XTranslateCoordinates(top_level->display, src_w, dest_w,
4587                         src_x, src_y, dest_x_return, dest_y_return, &tempwin);
4588 //printf("BC_WindowBase::translate_coordinates 1 %lld\n", timer.get_difference());
4589         }
4590 }
4591
4592 void BC_WindowBase::get_root_coordinates(int x, int y, int *abs_x, int *abs_y)
4593 {
4594         translate_coordinates(win, top_level->rootwin, x, y, abs_x, abs_y);
4595 }
4596
4597 void BC_WindowBase::get_win_coordinates(int abs_x, int abs_y, int *x, int *y)
4598 {
4599         translate_coordinates(top_level->rootwin, win, abs_x, abs_y, x, y);
4600 }
4601
4602
4603 #ifdef HAVE_LIBXXF86VM
4604 void BC_WindowBase::closest_vm(int *vm, int *width, int *height)
4605 {
4606         int foo,bar;
4607         *vm = 0;
4608         if(XF86VidModeQueryExtension(top_level->display,&foo,&bar)) {
4609                 int vm_count,i;
4610                 XF86VidModeModeInfo **vm_modelines;
4611                 XF86VidModeGetAllModeLines(top_level->display,
4612                         XDefaultScreen(top_level->display), &vm_count,&vm_modelines);
4613                 for( i = 0; i < vm_count; i++ ) {
4614                         if( vm_modelines[i]->hdisplay < vm_modelines[*vm]->hdisplay &&
4615                             vm_modelines[i]->hdisplay >= *width )
4616                                 *vm = i;
4617                 }
4618                 display = top_level->display;
4619                 if( vm_modelines[*vm]->hdisplay == *width )
4620                         *vm = -1;
4621                 else {
4622                         *width = vm_modelines[*vm]->hdisplay;
4623                         *height = vm_modelines[*vm]->vdisplay;
4624                 }
4625         }
4626 }
4627
4628 void BC_WindowBase::scale_vm(int vm)
4629 {
4630         int foo,bar,dotclock;
4631         if( XF86VidModeQueryExtension(top_level->display,&foo,&bar) ) {
4632                 int vm_count;
4633                 XF86VidModeModeInfo **vm_modelines;
4634                 XF86VidModeModeLine vml;
4635                 XF86VidModeGetAllModeLines(top_level->display,
4636                         XDefaultScreen(top_level->display), &vm_count,&vm_modelines);
4637                 XF86VidModeGetModeLine(top_level->display,
4638                         XDefaultScreen(top_level->display), &dotclock,&vml);
4639                 orig_modeline.dotclock = dotclock;
4640                 orig_modeline.hdisplay = vml.hdisplay;
4641                 orig_modeline.hsyncstart = vml.hsyncstart;
4642                 orig_modeline.hsyncend = vml.hsyncend;
4643                 orig_modeline.htotal = vml.htotal;
4644                 orig_modeline.vdisplay = vml.vdisplay;
4645                 orig_modeline.vsyncstart = vml.vsyncstart;
4646                 orig_modeline.vsyncend = vml.vsyncend;
4647                 orig_modeline.vtotal = vml.vtotal;
4648                 orig_modeline.flags = vml.flags;
4649                 orig_modeline.privsize = vml.privsize;
4650                 // orig_modeline.private = vml.private;
4651                 XF86VidModeSwitchToMode(top_level->display,XDefaultScreen(top_level->display),vm_modelines[vm]);
4652                 XF86VidModeSetViewPort(top_level->display,XDefaultScreen(top_level->display),0,0);
4653                 XFlush(top_level->display);
4654         }
4655 }
4656
4657 void BC_WindowBase::restore_vm()
4658 {
4659         XF86VidModeSwitchToMode(top_level->display,XDefaultScreen(top_level->display),&orig_modeline);
4660         XFlush(top_level->display);
4661 }
4662 #endif
4663
4664
4665 #ifndef SINGLE_THREAD
4666 int BC_WindowBase::get_event_count()
4667 {
4668         event_lock->lock("BC_WindowBase::get_event_count");
4669         int result = common_events.total;
4670         event_lock->unlock();
4671         return result;
4672 }
4673
4674 XEvent* BC_WindowBase::get_event()
4675 {
4676         XEvent *result = 0;
4677         while(!done && !result)
4678         {
4679                 event_condition->lock("BC_WindowBase::get_event");
4680                 event_lock->lock("BC_WindowBase::get_event");
4681
4682                 if(common_events.total && !done)
4683                 {
4684                         result = common_events.values[0];
4685                         common_events.remove_number(0);
4686                 }
4687
4688                 event_lock->unlock();
4689         }
4690         return result;
4691 }
4692
4693 void BC_WindowBase::put_event(XEvent *event)
4694 {
4695         event_lock->lock("BC_WindowBase::put_event");
4696         common_events.append(event);
4697         event_lock->unlock();
4698         event_condition->unlock();
4699 }
4700
4701 void BC_WindowBase::dequeue_events(Window win)
4702 {
4703         event_lock->lock("BC_WindowBase::dequeue_events");
4704
4705         int out = 0, total = common_events.size();
4706         for( int in=0; in<total; ++in ) {
4707                 if( common_events[in]->xany.window == win ) continue;
4708                 common_events[out++] = common_events[in];
4709         }
4710         common_events.total = out;
4711
4712         event_lock->unlock();
4713 }
4714
4715 int BC_WindowBase::resend_event(BC_WindowBase *window)
4716 {
4717         if( resend_event_window ) return 1;
4718         resend_event_window = window;
4719         return 0;
4720 }
4721
4722 #else
4723
4724 int BC_WindowBase::resend_event(BC_WindowBase *window)
4725 {
4726         return 1;
4727 }
4728
4729 #endif // SINGLE_THREAD
4730
4731 int BC_WindowBase::get_id()
4732 {
4733         return id;
4734 }
4735
4736
4737 BC_Pixmap *BC_WindowBase::create_pixmap(VFrame *vframe)
4738 {
4739         int w = vframe->get_w(), h = vframe->get_h();
4740         BC_Pixmap *icon = new BC_Pixmap(this, w, h);
4741         icon->draw_vframe(vframe, 0,0, w,h, 0,0);
4742         return icon;
4743 }
4744
4745
4746 void BC_WindowBase::flicker(int n, int ms)
4747 {
4748         int color = get_bg_color();
4749         for( int i=2*n; --i>=0; ) {
4750                 set_inverse();          set_bg_color(WHITE);
4751                 clear_box(0,0, w,h);    flash(1);
4752                 sync_display();         Timer::delay(ms);
4753         }
4754         set_bg_color(color);
4755         set_opaque();
4756 }
4757
4758 void BC_WindowBase::focus()
4759 {
4760         XWindowAttributes xwa;
4761         XGetWindowAttributes(top_level->display, top_level->win, &xwa);
4762         if( xwa.map_state == IsViewable )
4763                 XSetInputFocus(top_level->display, top_level->win, RevertToParent, CurrentTime);
4764 }
4765
4766 int BC_WindowBase::wait_viewable(int ms)
4767 {
4768         Timer timer;
4769         XWindowAttributes xwa;
4770         do {
4771                 XGetWindowAttributes(top_level->display, top_level->win, &xwa);
4772                 if( xwa.map_state == IsViewable ) return 0;
4773                 usleep(10000);
4774         } while( timer.get_difference() < ms );
4775         return 1;
4776 }
4777