version update
[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 // force setting modifiers state if a modifier key pressed
1177                 switch( keysym ) {
1178                 case XK_Alt_L:
1179                 case XK_Alt_R:
1180                         alt_mask = 1;
1181                         break;
1182                 case XK_Shift_L:
1183                 case XK_Shift_R:
1184                         shift_mask = 1;
1185                         break;
1186                 case XK_Control_L:
1187                 case XK_Control_R:
1188                         ctrl_mask = 1;
1189                         break;
1190                 default:
1191                         break;
1192                 }
1193
1194 // block out control keys
1195                 if(keysym > 0xffe0 && keysym < 0xffff) break;
1196 // block out Alt_GR key
1197                 if(keysym == 0xfe03) break;
1198
1199                 if(test_keypress)
1200                          printf("BC_WindowBase::dispatch_event %x\n", (uint32_t)keysym);
1201
1202 #ifdef X_HAVE_UTF8_STRING
1203 //It's Ascii or UTF8?
1204 //              if (keysym != 0xffff && (keys_return[0] & 0xff) >= 0x7f )
1205 //printf("BC_WindowBase::dispatch_event %d %02x%02x\n", __LINE__, keys_return[0], keys_return[1]);
1206
1207                 if( ((keys_return[1] & 0xff) > 0x80) &&
1208                     ((keys_return[0] & 0xff) > 0xC0) ) {
1209 //printf("BC_WindowBase::dispatch_event %d\n", __LINE__);
1210                         key_pressed = keysym & 0xff;
1211                 }
1212                 else {
1213 #endif
1214 // shuttle speed codes
1215                 if( keysym >= SKEY_MIN && keysym <= SKEY_MAX ) {
1216                         key_pressed = keysym;
1217                 }
1218                 else switch( keysym ) {
1219 // block out extra keys
1220                 case XK_Alt_L:
1221                 case XK_Alt_R:
1222                 case XK_Shift_L:
1223                 case XK_Shift_R:
1224                 case XK_Control_L:
1225                 case XK_Control_R:
1226                         key_pressed = 0;
1227                         break;
1228
1229 // Translate key codes
1230                 case XK_Return:         key_pressed = RETURN;   break;
1231                 case XK_Up:             key_pressed = UP;       break;
1232                 case XK_Down:           key_pressed = DOWN;     break;
1233                 case XK_Left:           key_pressed = LEFT;     break;
1234                 case XK_Right:          key_pressed = RIGHT;    break;
1235                 case XK_Next:           key_pressed = PGDN;     break;
1236                 case XK_Prior:          key_pressed = PGUP;     break;
1237                 case XK_BackSpace:      key_pressed = BACKSPACE; break;
1238                 case XK_Escape:         key_pressed = ESC;      break;
1239                 case XK_Tab:
1240                         if(shift_down())
1241                                 key_pressed = LEFTTAB;
1242                         else
1243                                 key_pressed = TAB;
1244                         break;
1245                 case XK_ISO_Left_Tab:   key_pressed = LEFTTAB;  break;
1246                 case XK_underscore:     key_pressed = '_';      break;
1247                 case XK_asciitilde:     key_pressed = '~';      break;
1248                 case XK_Delete:         key_pressed = DELETE;   break;
1249                 case XK_Home:           key_pressed = HOME;     break;
1250                 case XK_End:            key_pressed = END;      break;
1251
1252 // number pad
1253                 case XK_KP_Enter:       key_pressed = KPENTER;  break;
1254                 case XK_KP_Add:         key_pressed = KPPLUS;   break;
1255                 case XK_KP_Subtract:    key_pressed = KPMINUS;  break;
1256                 case XK_KP_Multiply:    key_pressed = KPSTAR;   break;
1257                 case XK_KP_Divide:      key_pressed = KPSLASH;  break;
1258                 case XK_KP_1:
1259                 case XK_KP_End:         key_pressed = KP1;      break;
1260                 case XK_KP_2:
1261                 case XK_KP_Down:        key_pressed = KP2;      break;
1262                 case XK_KP_3:
1263                 case XK_KP_Page_Down:   key_pressed = KP3;      break;
1264                 case XK_KP_4:
1265                 case XK_KP_Left:        key_pressed = KP4;      break;
1266                 case XK_KP_5:
1267                 case XK_KP_Begin:       key_pressed = KP5;      break;
1268                 case XK_KP_6:
1269                 case XK_KP_Right:       key_pressed = KP6;      break;
1270                 case XK_KP_7:
1271                 case XK_KP_Home:        key_pressed = KP7;      break;
1272                 case XK_KP_8:
1273                 case XK_KP_Up:          key_pressed = KP8;      break;
1274                 case XK_KP_9:
1275                 case XK_KP_Page_Up:     key_pressed = KP9;      break;
1276                 case XK_KP_0:
1277                 case XK_KP_Insert:      key_pressed = KPINS;    break;
1278                 case XK_KP_Decimal:
1279                 case XK_KP_Delete:      key_pressed = KPDEL;    break;
1280
1281                 case XK_F1:             key_pressed = KEY_F1;   break;
1282                 case XK_F2:             key_pressed = KEY_F2;   break;
1283                 case XK_F3:             key_pressed = KEY_F3;   break;
1284                 case XK_F4:             key_pressed = KEY_F4;   break;
1285                 case XK_F5:             key_pressed = KEY_F5;   break;
1286                 case XK_F6:             key_pressed = KEY_F6;   break;
1287                 case XK_F7:             key_pressed = KEY_F7;   break;
1288                 case XK_F8:             key_pressed = KEY_F8;   break;
1289                 case XK_F9:             key_pressed = KEY_F9;   break;
1290                 case XK_F10:            key_pressed = KEY_F10;  break;
1291                 case XK_F11:            key_pressed = KEY_F11;  break;
1292                 case XK_F12:            key_pressed = KEY_F12;  break;
1293 // activates remote
1294                 case XK_Menu:           key_pressed = KPMENU;   break;  /* menu */
1295
1296                 default:
1297                         key_pressed = keysym & 0xff;
1298 #ifdef X_HAVE_UTF8_STRING
1299 //printf("BC_WindowBase::dispatch_event %d\n", __LINE__);
1300                         keys_return[1] = 0;
1301 #endif
1302                         break;
1303                 }
1304 #ifdef X_HAVE_UTF8_STRING
1305                 }
1306                 key_pressed_utf8 = keys_return;
1307 #endif
1308
1309
1310                 result = 0;
1311                 if( top_level == this )
1312                         result = BC_KeyboardHandler::run_listeners(this);
1313
1314 //printf("BC_WindowBase::dispatch_event %d %d %x\n", shift_down(), alt_down(), key_pressed);
1315                 if( !result )
1316                         result = dispatch_keypress_event();
1317 // Handle some default keypresses
1318                 if(!result)
1319                 {
1320                         if(key_pressed == 'w' ||
1321                                 key_pressed == 'W')
1322                         {
1323                                 close_event();
1324                         }
1325                 }
1326                 break;
1327
1328         case KeyRelease:
1329                 XLookupString((XKeyEvent*)event, keys_return, 1, &keysym, 0);
1330                 get_key_masks(event->xkey.state);
1331 // force clearing modifiers state if a modifier key released
1332                 switch( keysym ) {
1333                 case XK_Alt_L:
1334                 case XK_Alt_R:
1335                         alt_mask = 0;
1336                         break;
1337                 case XK_Shift_L:
1338                 case XK_Shift_R:
1339                         shift_mask = 0;
1340                         break;
1341                 case XK_Control_L:
1342                 case XK_Control_R:
1343                         ctrl_mask = 0;
1344                         break;
1345                 default:
1346                         break;
1347                 }
1348                 dispatch_keyrelease_event();
1349 // printf("BC_WindowBase::dispatch_event KeyRelease keysym=%#lx keystate=%04x\n",
1350 // keysym, event->xkey.state);
1351                 break;
1352
1353         case LeaveNotify:
1354                 if( event->xcrossing.mode != NotifyNormal ) break;
1355                 cursor_entered = 0;
1356                 event_win = event->xany.window;
1357                 dispatch_cursor_leave();
1358                 break;
1359
1360         case EnterNotify:
1361                 if( event->xcrossing.mode != NotifyNormal ) break;
1362
1363                 if( !cursor_entered ) {
1364                         for( int i=0; i<popups.size(); ++i ) {  // popups always take focus
1365                                 if( popups[i]->win == event->xcrossing.window )
1366                                 cursor_entered = 1;
1367                         }
1368                         if( !cursor_entered && get_resources()->grab_input_focus &&
1369                             !event->xcrossing.focus && event->xcrossing.window == win ) {
1370                                 cursor_entered = 1;
1371                         }
1372                         if( cursor_entered )
1373                                 focus();
1374                 }
1375                 event_win = event->xany.window;
1376                 cursor_x = event->xcrossing.x;
1377                 cursor_y = event->xcrossing.y;
1378                 dispatch_cursor_enter();
1379                 break;
1380
1381         default:
1382                 break;
1383         }
1384 //printf("100 %s %p %d\n", title, event, event->type);
1385 //if(event->type != ClientMessage) dump();
1386
1387 #ifndef SINGLE_THREAD
1388         unlock_window();
1389         if(event) {
1390                 if( resend_event_window ) {
1391                         resend_event_window->put_event(event);
1392                         resend_event_window = 0;
1393                 }
1394                 else
1395                         delete event;
1396         }
1397 #else
1398 //      if(done) completion_lock->unlock();
1399 #endif
1400
1401 if(debug) printf("BC_WindowBase::dispatch_event this=%p %d\n", this, __LINE__);
1402         return 0;
1403 }
1404
1405 int BC_WindowBase::dispatch_expose_event()
1406 {
1407         int result = 0;
1408         for(int i = 0; i < subwindows->total && !result; i++)
1409         {
1410                 result = subwindows->values[i]->dispatch_expose_event();
1411         }
1412
1413 // Propagate to user
1414         if(!result) expose_event();
1415         return result;
1416 }
1417
1418 int BC_WindowBase::dispatch_resize_event(int w, int h)
1419 {
1420 // Can't store new w and h until the event is handles
1421 // because bcfilebox depends on the old w and h to
1422 // reposition widgets.
1423         if( window_type == MAIN_WINDOW ) {
1424                 flash_enabled = 0;
1425                 resize_events = 0;
1426
1427                 delete pixmap;
1428                 pixmap = new BC_Pixmap(this, w, h);
1429                 clear_box(0, 0, w, h);
1430         }
1431
1432 // Propagate to subwindows
1433         for(int i = 0; i < subwindows->total; i++) {
1434                 subwindows->values[i]->dispatch_resize_event(w, h);
1435         }
1436
1437 // Propagate to user
1438         resize_event(w, h);
1439
1440         if( window_type == MAIN_WINDOW ) {
1441                 this->w = w;
1442                 this->h = h;
1443                 dispatch_flash();
1444                 flush();
1445         }
1446         return 0;
1447 }
1448
1449 int BC_WindowBase::dispatch_flash()
1450 {
1451         flash_enabled = 1;
1452         for(int i = 0; i < subwindows->total; i++)
1453                 subwindows->values[i]->dispatch_flash();
1454         return flash(0);
1455 }
1456
1457 int BC_WindowBase::dispatch_translation_event()
1458 {
1459         translation_events = 0;
1460         if(window_type == MAIN_WINDOW)
1461         {
1462                 prev_x = x;
1463                 prev_y = y;
1464                 x = last_translate_x;
1465                 y = last_translate_y;
1466 // Correct for window manager offsets
1467                 x -= x_correction;
1468                 y -= y_correction;
1469         }
1470
1471         for(int i = 0; i < subwindows->total; i++)
1472         {
1473                 subwindows->values[i]->dispatch_translation_event();
1474         }
1475
1476         translation_event();
1477         return 0;
1478 }
1479
1480 int BC_WindowBase::dispatch_motion_event()
1481 {
1482         int result = 0;
1483         unhide_cursor();
1484
1485         if(top_level == this)
1486         {
1487                 motion_events = 0;
1488                 event_win = last_motion_win;
1489                 get_key_masks(last_motion_state);
1490
1491 // Test for grab
1492                 if(get_button_down() && !active_menubar && !active_popup_menu)
1493                 {
1494                         if(!result)
1495                         {
1496                                 cursor_x = last_motion_x;
1497                                 cursor_y = last_motion_y;
1498                                 result = dispatch_drag_motion();
1499                         }
1500
1501                         if(!result &&
1502                                 (last_motion_x < drag_x1 || last_motion_x >= drag_x2 ||
1503                                 last_motion_y < drag_y1 || last_motion_y >= drag_y2))
1504                         {
1505                                 cursor_x = drag_x;
1506                                 cursor_y = drag_y;
1507
1508                                 result = dispatch_drag_start();
1509                         }
1510                 }
1511
1512                 cursor_x = last_motion_x;
1513                 cursor_y = last_motion_y;
1514
1515 // printf("BC_WindowBase::dispatch_motion_event %d %p %p %p\n",
1516 // __LINE__,
1517 // active_menubar,
1518 // active_popup_menu,
1519 // active_subwindow);
1520
1521                 if(active_menubar && !result) result = active_menubar->dispatch_motion_event();
1522                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_motion_event();
1523                 if(active_subwindow && !result) result = active_subwindow->dispatch_motion_event();
1524         }
1525
1526 // Dispatch in stacking order
1527         for(int i = subwindows->size() - 1; i >= 0 && !result; i--)
1528         {
1529                 result = subwindows->values[i]->dispatch_motion_event();
1530         }
1531
1532         if(!result) result = cursor_motion_event();    // give to user
1533         return result;
1534 }
1535
1536 int BC_WindowBase::dispatch_keypress_event()
1537 {
1538         int result = 0;
1539         if(top_level == this)
1540         {
1541                 if(active_subwindow) result = active_subwindow->dispatch_keypress_event();
1542         }
1543
1544         for(int i = 0; i < subwindows->total && !result; i++)
1545         {
1546                 result = subwindows->values[i]->dispatch_keypress_event();
1547         }
1548
1549         if(!result) result = keypress_event();
1550
1551         return result;
1552 }
1553
1554 int BC_WindowBase::dispatch_keyrelease_event()
1555 {
1556         int result = 0;
1557         if(top_level == this)
1558         {
1559                 if(active_subwindow) result = active_subwindow->dispatch_keyrelease_event();
1560         }
1561
1562         for(int i = 0; i < subwindows->total && !result; i++)
1563         {
1564                 result = subwindows->values[i]->dispatch_keyrelease_event();
1565         }
1566
1567         if(!result) result = keyrelease_event();
1568
1569         return result;
1570 }
1571
1572 int BC_WindowBase::dispatch_focus_in()
1573 {
1574         for(int i = 0; i < subwindows->total; i++)
1575         {
1576                 subwindows->values[i]->dispatch_focus_in();
1577         }
1578
1579         focus_in_event();
1580
1581         return 0;
1582 }
1583
1584 int BC_WindowBase::dispatch_focus_out()
1585 {
1586         for(int i = 0; i < subwindows->total; i++)
1587         {
1588                 subwindows->values[i]->dispatch_focus_out();
1589         }
1590
1591         focus_out_event();
1592
1593         return 0;
1594 }
1595
1596 int BC_WindowBase::get_has_focus()
1597 {
1598         return top_level->has_focus;
1599 }
1600
1601 int BC_WindowBase::get_deleting()
1602 {
1603         if(is_deleting) return 1;
1604         if(parent_window && parent_window->get_deleting()) return 1;
1605         return 0;
1606 }
1607
1608 int BC_WindowBase::dispatch_button_press()
1609 {
1610         int result = 0;
1611
1612
1613         if(top_level == this)
1614         {
1615                 if(active_menubar) result = active_menubar->dispatch_button_press();
1616                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_button_press();
1617                 if(active_subwindow && !result) result = active_subwindow->dispatch_button_press();
1618         }
1619
1620         for(int i = 0; i < subwindows->total && !result; i++)
1621         {
1622                 result = subwindows->values[i]->dispatch_button_press();
1623         }
1624
1625         if(!result) result = button_press_event();
1626
1627
1628         return result;
1629 }
1630
1631 int BC_WindowBase::dispatch_button_release()
1632 {
1633         int result = 0;
1634         if(top_level == this)
1635         {
1636                 if(active_menubar) result = active_menubar->dispatch_button_release();
1637                 if(active_popup_menu && !result) result = active_popup_menu->dispatch_button_release();
1638                 if(active_subwindow && !result) result = active_subwindow->dispatch_button_release();
1639                 if(!result && button_number != 4 && button_number != 5)
1640                         result = dispatch_drag_stop();
1641         }
1642
1643         for(int i = 0; i < subwindows->total && !result; i++)
1644         {
1645                 result = subwindows->values[i]->dispatch_button_release();
1646         }
1647
1648         if(!result)
1649         {
1650                 result = button_release_event();
1651         }
1652
1653         return result;
1654 }
1655
1656
1657 int BC_WindowBase::dispatch_repeat_event(int64_t duration)
1658 {
1659
1660 // all repeat event handlers get called and decide based on activity and duration
1661 // whether to respond
1662         for(int i = 0; i < subwindows->total; i++)
1663         {
1664                 subwindows->values[i]->dispatch_repeat_event(duration);
1665         }
1666
1667
1668         repeat_event(duration);
1669
1670
1671
1672 // Unlock next repeat signal
1673         if(window_type == MAIN_WINDOW)
1674         {
1675 #ifdef SINGLE_THREAD
1676                 BC_Display::display_global->unlock_repeaters(duration);
1677 #else
1678                 for(int i = 0; i < repeaters.total; i++)
1679                 {
1680                         if(repeaters.values[i]->delay == duration)
1681                         {
1682                                 repeaters.values[i]->repeat_lock->unlock();
1683                         }
1684                 }
1685 #endif
1686         }
1687         return 0;
1688 }
1689
1690 void BC_WindowBase::unhide_cursor()
1691 {
1692         if(is_transparent)
1693         {
1694                 is_transparent = 0;
1695                 if(top_level->is_hourglass)
1696                         set_cursor(HOURGLASS_CURSOR, 1, 0);
1697                 else
1698                         set_cursor(current_cursor, 1, 0);
1699         }
1700         cursor_timer->update();
1701 }
1702
1703
1704 void BC_WindowBase::update_video_cursor()
1705 {
1706         if(video_on && !is_transparent)
1707         {
1708                 if(cursor_timer->get_difference() > VIDEO_CURSOR_TIMEOUT && !is_transparent)
1709                 {
1710                         is_transparent = 1;
1711                         set_cursor(TRANSPARENT_CURSOR, 1, 1);
1712                         cursor_timer->update();
1713                 }
1714         }
1715         else
1716         {
1717                 cursor_timer->update();
1718         }
1719 }
1720
1721
1722 int BC_WindowBase::dispatch_cursor_leave()
1723 {
1724         unhide_cursor();
1725
1726         for(int i = 0; i < subwindows->total; i++)
1727         {
1728                 subwindows->values[i]->dispatch_cursor_leave();
1729         }
1730
1731         cursor_leave_event();
1732         return 0;
1733 }
1734
1735 int BC_WindowBase::dispatch_cursor_enter()
1736 {
1737         int result = 0;
1738
1739         unhide_cursor();
1740
1741         if(active_menubar) result = active_menubar->dispatch_cursor_enter();
1742         if(!result && active_popup_menu) result = active_popup_menu->dispatch_cursor_enter();
1743         if(!result && active_subwindow) result = active_subwindow->dispatch_cursor_enter();
1744
1745         for(int i = 0; !result && i < subwindows->total; i++)
1746         {
1747                 result = subwindows->values[i]->dispatch_cursor_enter();
1748         }
1749
1750         if(!result) result = cursor_enter_event();
1751         return result;
1752 }
1753
1754 int BC_WindowBase::cursor_enter_event()
1755 {
1756         return 0;
1757 }
1758
1759 int BC_WindowBase::cursor_leave_event()
1760 {
1761         return 0;
1762 }
1763
1764 int BC_WindowBase::close_event()
1765 {
1766         set_done(1);
1767         return 1;
1768 }
1769
1770 // *** CONTEXT_HELP ***
1771 // This basic implementation serves solely for context help.
1772 // We are handling Alt/H only. Any subclass requiring more sophisticated
1773 // processing of keystrokes has to provide its own overloaded handler.
1774 int BC_WindowBase::keypress_event()
1775 {
1776 //      printf("BC_WindowBase::keypress_event: %d\n", get_keypress());
1777         return context_help_check_and_show();
1778 }
1779
1780 // The stuff up to END_CONTEXT_HELP serves solely for context help
1781 void BC_WindowBase::context_help_set_keyword(const char *keyword)
1782 {
1783         if (keyword) strcpy(context_help_keyword, keyword);
1784 }
1785
1786 const char *BC_WindowBase::context_help_get_keyword()
1787 {
1788 // If we have context_help_keyword defined here, return it.
1789 // Otherwise recursively track widget hierarchy up to top_level
1790 // and return the nearest found nonempty keyword.
1791 // If nothing found, the special keyword "TOC" is returned.
1792         if (context_help_keyword[0] || this == top_level ||
1793             this == parent_window || ! parent_window) {
1794                 if (! context_help_keyword[0] && this == top_level)
1795                         context_help_set_keyword("TOC");
1796                 return context_help_keyword;
1797         }
1798         return parent_window->context_help_get_keyword();
1799 }
1800
1801 void BC_WindowBase::context_help_show(const char *keyword)
1802 {
1803         char cmd[BCTEXTLEN];
1804
1805         if (! keyword) return;
1806
1807         sprintf(cmd, "\"%s/doc/ContextManual.pl\" \"%s\"", getenv("CIN_DAT"),
1808                 keyword);
1809 //      printf("BC_WindowBase::context_help_show(%s):\n%s\n", keyword, cmd);
1810
1811 // ContextManual.pl starts browser in background, so system() should not block
1812         system(cmd);
1813 }
1814
1815 void BC_WindowBase::context_help_show()
1816 {
1817         context_help_show(context_help_get_keyword());
1818 }
1819
1820 int BC_WindowBase::context_help_check_and_show(const char *keyword)
1821 {
1822         if (! keyword)    return 0;
1823         if (! keyword[0]) return 0;
1824
1825 // We are handling Alt/H only
1826         if (get_keypress() != 'h' || ! alt_down()) return 0;
1827
1828 // Restrict cursor location to that widget keystroke occured in
1829         if (! is_tooltip_event_win() || ! cursor_inside()) return 0;
1830
1831         context_help_show(keyword);
1832         return 1;
1833 }
1834
1835 int BC_WindowBase::context_help_check_and_show()
1836 {
1837         const char *keyword;
1838
1839 // We are handling Alt/H only
1840         if (get_keypress() != 'h' || ! alt_down()) return 0;
1841
1842 // Restrict cursor location, so any subwindow can define its own help keyword
1843         if (! is_tooltip_event_win() || ! cursor_inside()) return 0;
1844
1845 // If a widget has not defined its own help keyword, the parent can provide it
1846         keyword = context_help_get_keyword();
1847         if (! keyword[0]) return 0;
1848
1849         context_help_show(keyword);
1850         return 1;
1851 }
1852 // *** END_CONTEXT_HELP ***
1853
1854 int BC_WindowBase::dispatch_drag_start()
1855 {
1856         int result = 0;
1857         if(active_menubar) result = active_menubar->dispatch_drag_start();
1858         if(!result && active_popup_menu) result = active_popup_menu->dispatch_drag_start();
1859         if(!result && active_subwindow) result = active_subwindow->dispatch_drag_start();
1860
1861         for(int i = 0; i < subwindows->total && !result; i++)
1862         {
1863                 result = subwindows->values[i]->dispatch_drag_start();
1864         }
1865
1866         if(!result) result = is_dragging = drag_start_event();
1867         return result;
1868 }
1869
1870 int BC_WindowBase::dispatch_drag_stop()
1871 {
1872         int result = 0;
1873
1874         for(int i = 0; i < subwindows->total && !result; i++)
1875         {
1876                 result = subwindows->values[i]->dispatch_drag_stop();
1877         }
1878
1879         if(is_dragging && !result)
1880         {
1881                 drag_stop_event();
1882                 is_dragging = 0;
1883                 result = 1;
1884         }
1885
1886         return result;
1887 }
1888
1889 int BC_WindowBase::dispatch_drag_motion()
1890 {
1891         int result = 0;
1892         for(int i = 0; i < subwindows->total && !result; i++)
1893         {
1894                 result = subwindows->values[i]->dispatch_drag_motion();
1895         }
1896
1897         if(is_dragging && !result)
1898         {
1899                 drag_motion_event();
1900                 result = 1;
1901         }
1902
1903         return result;
1904 }
1905
1906
1907 int BC_WindowBase::show_tooltip(const char *text, int x, int y, int w, int h)
1908 {
1909 // default text
1910         int forced = !text ? force_tooltip : 1;
1911         if( !text ) text = tooltip_text;
1912         if( !text || (!forced && !get_resources()->tooltips_enabled) ) {
1913                 top_level->hide_tooltip();
1914                 return 1;
1915         }
1916 // default w,h
1917         if(w < 0) w = get_text_width(MEDIUMFONT, text)  + TOOLTIP_MARGIN * 2;
1918         if(h < 0) h = get_text_height(MEDIUMFONT, text) + TOOLTIP_MARGIN * 2;
1919 // default x,y (win relative)
1920         if( x < 0 ) x = get_w();
1921         if( y < 0 ) y = get_h();
1922         int wx, wy;
1923         get_root_coordinates(x, y, &wx, &wy);
1924 // keep the tip inside the window/display
1925         int x0 = top_level->get_x(), x1 = x0 + top_level->get_w();
1926         int x2 = top_level->get_screen_x(0, -1) + top_level->get_screen_w(0, -1);
1927         if( x1 > x2 ) x1 = x2;
1928         if( wx < x0 ) wx = x0;
1929         if( wx >= (x1-=w) ) wx = x1;
1930         int y0 = top_level->get_y(), y1 = y0 + top_level->get_h();
1931         int y2 = top_level->get_root_h(0);
1932         if( y1 > y2 ) y1 = y2;
1933         if( wy < y0 ) wy = y0;
1934         if( wy >= (y1-=h) ) wy = y1;
1935 // avoid tip under cursor (flickers)
1936         int abs_x, abs_y;
1937         get_abs_cursor(abs_x,abs_y, 0);
1938         if( wx < abs_x && abs_x < wx+w && wy < abs_y && abs_y < wy+h ) {
1939                 if( wx-abs_x < wy-abs_y )
1940                         wx = abs_x+1;
1941                 else
1942                         wy = abs_y+1;
1943         }
1944         if( !tooltip_on ) {
1945                 tooltip_on = 1;
1946                 tooltip_popup = new BC_Popup(top_level, wx, wy, w, h,
1947                                 get_resources()->tooltip_bg_color);
1948         }
1949         else
1950                 tooltip_popup->reposition_window(wx, wy, w, h);
1951
1952         draw_tooltip(text);
1953         tooltip_popup->flash();
1954         tooltip_popup->flush();
1955         return 0;
1956 }
1957
1958 int BC_WindowBase::hide_tooltip()
1959 {
1960         if(subwindows)
1961                 for(int i = 0; i < subwindows->total; i++)
1962                 {
1963                         subwindows->values[i]->hide_tooltip();
1964                 }
1965
1966         if(tooltip_on)
1967         {
1968                 tooltip_on = 0;
1969                 delete tooltip_popup;
1970                 tooltip_popup = 0;
1971         }
1972         return 0;
1973 }
1974
1975 const char *BC_WindowBase::get_tooltip()
1976 {
1977         return tooltip_text;
1978 }
1979
1980 int BC_WindowBase::set_tooltip(const char *text)
1981 {
1982         tooltip_text = text;
1983
1984 // Update existing tooltip if it is visible
1985         if(tooltip_on)
1986         {
1987                 draw_tooltip();
1988                 tooltip_popup->flash();
1989         }
1990         return 0;
1991 }
1992 // signal the event handler to repeat
1993 int BC_WindowBase::set_repeat(int64_t duration)
1994 {
1995         if(duration <= 0)
1996         {
1997                 printf("BC_WindowBase::set_repeat duration=%jd\n", duration);
1998                 return 0;
1999         }
2000         if(window_type != MAIN_WINDOW) return top_level->set_repeat(duration);
2001
2002 #ifdef SINGLE_THREAD
2003         BC_Display::display_global->set_repeat(this, duration);
2004 #else
2005 // test repeater database for duplicates
2006         for(int i = 0; i < repeaters.total; i++)
2007         {
2008 // Already exists
2009                 if(repeaters.values[i]->delay == duration)
2010                 {
2011                         repeaters.values[i]->start_repeating(this);
2012                         return 0;
2013                 }
2014         }
2015
2016         BC_Repeater *repeater = new BC_Repeater(this, duration);
2017         repeater->initialize();
2018         repeaters.append(repeater);
2019         repeater->start_repeating();
2020 #endif
2021         return 0;
2022 }
2023
2024 int BC_WindowBase::unset_repeat(int64_t duration)
2025 {
2026         if(window_type != MAIN_WINDOW) return top_level->unset_repeat(duration);
2027
2028 #ifdef SINGLE_THREAD
2029         BC_Display::display_global->unset_repeat(this, duration);
2030 #else
2031         for(int i = 0; i < repeaters.total; i++)
2032         {
2033                 if(repeaters.values[i]->delay == duration)
2034                 {
2035                         repeaters.values[i]->stop_repeating();
2036                 }
2037         }
2038 #endif
2039         return 0;
2040 }
2041
2042
2043 int BC_WindowBase::unset_all_repeaters()
2044 {
2045 #ifdef SINGLE_THREAD
2046         BC_Display::display_global->unset_all_repeaters(this);
2047 #else
2048         for(int i = 0; i < repeaters.total; i++)
2049         {
2050                 repeaters.values[i]->stop_repeating();
2051         }
2052         repeaters.remove_all_objects();
2053 #endif
2054         return 0;
2055 }
2056
2057 // long BC_WindowBase::get_repeat_id()
2058 // {
2059 //      return top_level->next_repeat_id++;
2060 // }
2061
2062 XEvent *BC_WindowBase::new_xevent()
2063 {
2064         XEvent *event = new XEvent;
2065         memset(event, 0, sizeof(*event));
2066         return event;
2067 }
2068
2069 #ifndef SINGLE_THREAD
2070 int BC_WindowBase::arm_repeat(int64_t duration)
2071 {
2072         XEvent *event = new_xevent();
2073         XClientMessageEvent *ptr = (XClientMessageEvent*)event;
2074         ptr->type = ClientMessage;
2075         ptr->message_type = RepeaterXAtom;
2076         ptr->format = 32;
2077         ptr->data.l[0] = duration;
2078
2079 // Couldn't use XSendEvent since it locked up randomly.
2080         put_event(event);
2081         return 0;
2082 }
2083 #endif
2084
2085 int BC_WindowBase::receive_custom_xatoms(xatom_event *event)
2086 {
2087         return 0;
2088 }
2089
2090 int BC_WindowBase::send_custom_xatom(xatom_event *event)
2091 {
2092 #ifndef SINGLE_THREAD
2093         XEvent *myevent = new_xevent();
2094         XClientMessageEvent *ptr = (XClientMessageEvent*)myevent;
2095         ptr->type = ClientMessage;
2096         ptr->message_type = event->message_type;
2097         ptr->format = event->format;
2098         ptr->data.l[0] = event->data.l[0];
2099         ptr->data.l[1] = event->data.l[1];
2100         ptr->data.l[2] = event->data.l[2];
2101         ptr->data.l[3] = event->data.l[3];
2102         ptr->data.l[4] = event->data.l[4];
2103
2104         put_event(myevent);
2105 #endif
2106         return 0;
2107 }
2108
2109
2110
2111 Atom BC_WindowBase::create_xatom(const char *atom_name)
2112 {
2113         return XInternAtom(display, atom_name, False);
2114 }
2115
2116 int BC_WindowBase::get_atoms()
2117 {
2118         SetDoneXAtom =  XInternAtom(display, "BC_REPEAT_EVENT", False);
2119         RepeaterXAtom = XInternAtom(display, "BC_CLOSE_EVENT", False);
2120         DestroyAtom =   XInternAtom(display, "BC_DESTROY_WINDOW", False);
2121         DelWinXAtom =   XInternAtom(display, "WM_DELETE_WINDOW", False);
2122         if( (ProtoXAtom = XInternAtom(display, "WM_PROTOCOLS", False)) != 0 )
2123                 XChangeProperty(display, win, ProtoXAtom, XA_ATOM, 32,
2124                         PropModeReplace, (unsigned char *)&DelWinXAtom, True);
2125         return 0;
2126
2127 }
2128
2129
2130 void BC_WindowBase::init_cursors()
2131 {
2132         arrow_cursor = XCreateFontCursor(display, XC_top_left_arrow);
2133         cross_cursor = XCreateFontCursor(display, XC_crosshair);
2134         ibeam_cursor = XCreateFontCursor(display, XC_xterm);
2135         vseparate_cursor = XCreateFontCursor(display, XC_sb_v_double_arrow);
2136         hseparate_cursor = XCreateFontCursor(display, XC_sb_h_double_arrow);
2137         move_cursor = XCreateFontCursor(display, XC_fleur);
2138         left_cursor = XCreateFontCursor(display, XC_sb_left_arrow);
2139         right_cursor = XCreateFontCursor(display, XC_sb_right_arrow);
2140         upright_arrow_cursor = XCreateFontCursor(display, XC_arrow);
2141         upleft_resize_cursor = XCreateFontCursor(display, XC_top_left_corner);
2142         upright_resize_cursor = XCreateFontCursor(display, XC_top_right_corner);
2143         downleft_resize_cursor = XCreateFontCursor(display, XC_bottom_left_corner);
2144         downright_resize_cursor = XCreateFontCursor(display, XC_bottom_right_corner);
2145         hourglass_cursor = XCreateFontCursor(display, XC_watch);
2146         grabbed_cursor = create_grab_cursor();
2147
2148         static char cursor_data[] = { 0,0,0,0, 0,0,0,0 };
2149         Colormap colormap = DefaultColormap(display, screen);
2150         Pixmap pixmap_bottom = XCreateBitmapFromData(display,
2151                 rootwin, cursor_data, 8, 8);
2152         XColor black, dummy;
2153         XAllocNamedColor(display, colormap, "black", &black, &dummy);
2154         transparent_cursor = XCreatePixmapCursor(display,
2155                 pixmap_bottom, pixmap_bottom, &black, &black, 0, 0);
2156 //      XDefineCursor(display, win, transparent_cursor);
2157         XFreePixmap(display, pixmap_bottom);
2158 }
2159
2160 int BC_WindowBase::evaluate_color_model(int client_byte_order, int server_byte_order, int depth)
2161 {
2162         int color_model = BC_TRANSPARENCY;
2163         switch(depth)
2164         {
2165                 case 8:
2166                         color_model = BC_RGB8;
2167                         break;
2168                 case 16:
2169                         color_model = (server_byte_order == client_byte_order) ? BC_RGB565 : BC_BGR565;
2170                         break;
2171                 case 24:
2172                         color_model = server_byte_order ? BC_BGR888 : BC_RGB888;
2173                         break;
2174                 case 32:
2175                         color_model = server_byte_order ? BC_BGR8888 : BC_ARGB8888;
2176                         break;
2177         }
2178         return color_model;
2179 }
2180
2181 int BC_WindowBase::init_colors()
2182 {
2183         total_colors = 0;
2184         current_color_value = current_color_pixel = 0;
2185
2186 // Get the real depth
2187         char *data = 0;
2188         XImage *ximage;
2189         ximage = XCreateImage(top_level->display,
2190                         top_level->vis, top_level->default_depth,
2191                         ZPixmap, 0, data, 16, 16, 8, 0);
2192         bits_per_pixel = ximage->bits_per_pixel;
2193         XDestroyImage(ximage);
2194
2195         color_model = evaluate_color_model(client_byte_order,
2196                 server_byte_order,
2197                 bits_per_pixel);
2198 // Get the color model
2199         switch(color_model)
2200         {
2201                 case BC_RGB8:
2202                         if(private_color) {
2203                                 cmap = XCreateColormap(display, rootwin, vis, AllocNone);
2204                                 create_private_colors();
2205                         }
2206                         else {
2207                                 cmap = DefaultColormap(display, screen);
2208                                 create_shared_colors();
2209                         }
2210
2211                         allocate_color_table();
2212                         break;
2213
2214                 default:
2215                         //cmap = DefaultColormap(display, screen);
2216                         cmap = XCreateColormap(display, rootwin, vis, AllocNone );
2217                         break;
2218         }
2219         return 0;
2220 }
2221
2222 int BC_WindowBase::create_private_colors()
2223 {
2224         int color;
2225         total_colors = 256;
2226
2227         for(int i = 0; i < 255; i++)
2228         {
2229                 color = (i & 0xc0) << 16;
2230                 color += (i & 0x38) << 10;
2231                 color += (i & 0x7) << 5;
2232                 color_table[i][0] = color;
2233         }
2234         create_shared_colors();        // overwrite the necessary colors on the table
2235         return 0;
2236 }
2237
2238
2239 int BC_WindowBase::create_color(int color)
2240 {
2241         if(total_colors == 256)
2242         {
2243 // replace the closest match with an exact match
2244                 color_table[get_color_rgb8(color)][0] = color;
2245         }
2246         else
2247         {
2248 // add the color to the table
2249                 color_table[total_colors][0] = color;
2250                 total_colors++;
2251         }
2252         return 0;
2253 }
2254
2255 int BC_WindowBase::create_shared_colors()
2256 {
2257         create_color(BLACK);
2258         create_color(WHITE);
2259
2260         create_color(LTGREY);
2261         create_color(MEGREY);
2262         create_color(MDGREY);
2263         create_color(DKGREY);
2264
2265         create_color(LTCYAN);
2266         create_color(MECYAN);
2267         create_color(MDCYAN);
2268         create_color(DKCYAN);
2269
2270         create_color(LTGREEN);
2271         create_color(GREEN);
2272         create_color(DKGREEN);
2273
2274         create_color(LTPINK);
2275         create_color(PINK);
2276         create_color(RED);
2277
2278         create_color(LTBLUE);
2279         create_color(BLUE);
2280         create_color(DKBLUE);
2281
2282         create_color(LTYELLOW);
2283         create_color(MEYELLOW);
2284         create_color(MDYELLOW);
2285         create_color(DKYELLOW);
2286
2287         create_color(LTPURPLE);
2288         create_color(MEPURPLE);
2289         create_color(MDPURPLE);
2290         create_color(DKPURPLE);
2291
2292         create_color(FGGREY);
2293         create_color(MNBLUE);
2294         create_color(ORANGE);
2295         create_color(FTGREY);
2296
2297         return 0;
2298 }
2299
2300 Cursor BC_WindowBase::create_grab_cursor()
2301 {
2302         int iw = 23, iw1 = iw-1, iw2 = iw/2;
2303         int ih = 23, ih1 = ih-1, ih2 = ih/2;
2304         VFrame grab(iw,ih,BC_RGB888);
2305         grab.clear_frame();
2306         grab.set_pixel_color(RED);   // fg
2307         grab.draw_smooth(iw2,0,   iw1,0,   iw1,ih2);
2308         grab.draw_smooth(iw1,ih2, iw1,ih1, iw2,ih1);
2309         grab.draw_smooth(iw2,ih1, 0,ih1,   0,ih2);
2310         grab.draw_smooth(0,ih2,   0,0,     iw2,0);
2311         grab.set_pixel_color(WHITE); // bg
2312         grab.draw_line(0,ih2,     iw2-2,ih2);
2313         grab.draw_line(iw2+2,ih2, iw1,ih2);
2314         grab.draw_line(iw2,0,     iw2,ih2-2);
2315         grab.draw_line(iw2,ih2+2, iw2,ih1);
2316
2317         int bpl = (iw+7)/8, isz = bpl * ih;
2318         char img[isz];  memset(img, 0, isz);
2319         char msk[isz];  memset(msk, 0, isz);
2320         unsigned char **rows = grab.get_rows();
2321         for( int iy=0; iy<ih; ++iy ) {
2322                 char *op = img + iy*bpl;
2323                 char *mp = msk + iy*bpl;
2324                 unsigned char *ip = rows[iy];
2325                 for( int ix=0; ix<iw; ++ix,ip+=3 ) {
2326                         if( ip[0] ) mp[ix>>3] |= (1<<(ix&7));
2327                         if( !ip[1] ) op[ix>>3] |= (1<<(ix&7));
2328                 }
2329         }
2330         unsigned long white_pix = WhitePixel(display, screen);
2331         unsigned long black_pix = BlackPixel(display, screen);
2332         Pixmap img_xpm = XCreatePixmapFromBitmapData(display, rootwin,
2333                 img, iw,ih, white_pix,black_pix, 1);
2334         Pixmap msk_xpm = XCreatePixmapFromBitmapData(display, rootwin,
2335                 msk, iw,ih, white_pix,black_pix, 1);
2336
2337         XColor fc, bc;
2338         fc.flags = bc.flags = DoRed | DoGreen | DoBlue;
2339         fc.red = 0xffff; fc.green = fc.blue = 0;  // fg
2340         bc.red = 0xffff; bc.green = 0xffff; bc.blue = 0x0000;     // bg
2341         Cursor cursor = XCreatePixmapCursor(display, img_xpm,msk_xpm, &fc,&bc, iw2,ih2);
2342         XFreePixmap(display, img_xpm);
2343         XFreePixmap(display, msk_xpm);
2344         return cursor;
2345 }
2346
2347 int BC_WindowBase::allocate_color_table()
2348 {
2349         int red, green, blue, color;
2350         XColor col;
2351
2352         for(int i = 0; i < total_colors; i++)
2353         {
2354                 color = color_table[i][0];
2355                 red = (color & 0xFF0000) >> 16;
2356                 green = (color & 0x00FF00) >> 8;
2357                 blue = color & 0xFF;
2358
2359                 col.flags = DoRed | DoGreen | DoBlue;
2360                 col.red   = red<<8   | red;
2361                 col.green = green<<8 | green;
2362                 col.blue  = blue<<8  | blue;
2363
2364                 XAllocColor(display, cmap, &col);
2365                 color_table[i][1] = col.pixel;
2366         }
2367
2368         XInstallColormap(display, cmap);
2369         return 0;
2370 }
2371
2372 int BC_WindowBase::init_window_shape()
2373 {
2374         if(bg_pixmap && bg_pixmap->use_alpha())
2375         {
2376                 XShapeCombineMask(top_level->display,
2377                         this->win, ShapeBounding, 0, 0,
2378                         bg_pixmap->get_alpha(), ShapeSet);
2379         }
2380         return 0;
2381 }
2382
2383
2384 int BC_WindowBase::init_gc()
2385 {
2386         unsigned long gcmask;
2387         gcmask = GCFont | GCGraphicsExposures;
2388
2389         XGCValues gcvalues;
2390         gcvalues.font = mediumfont->fid;        // set the font
2391         gcvalues.graphics_exposures = 0;        // prevent expose events for every redraw
2392         gc = XCreateGC(display, rootwin, gcmask, &gcvalues);
2393
2394 // gcmask = GCCapStyle | GCJoinStyle;
2395 // XGetGCValues(display, gc, gcmask, &gcvalues);
2396 // printf("BC_WindowBase::init_gc %d %d %d\n", __LINE__, gcvalues.cap_style, gcvalues.join_style);
2397         return 0;
2398 }
2399
2400 int BC_WindowBase::init_fonts()
2401 {
2402         if( !(smallfont = XLoadQueryFont(display, _(resources->small_font))) )
2403                 if( !(smallfont = XLoadQueryFont(display, _(resources->small_font2))) )
2404                         smallfont = XLoadQueryFont(display, "fixed");
2405         if( !(mediumfont = XLoadQueryFont(display, _(resources->medium_font))) )
2406                 if( !(mediumfont = XLoadQueryFont(display, _(resources->medium_font2))) )
2407                         mediumfont = XLoadQueryFont(display, "fixed");
2408         if( !(largefont = XLoadQueryFont(display, _(resources->large_font))) )
2409                 if( !(largefont = XLoadQueryFont(display, _(resources->large_font2))) )
2410                         largefont = XLoadQueryFont(display, "fixed");
2411         if( !(bigfont = XLoadQueryFont(display, _(resources->big_font))) )
2412                 if( !(bigfont = XLoadQueryFont(display, _(resources->big_font2))) )
2413                         bigfont = XLoadQueryFont(display, "fixed");
2414
2415         if((clockfont = XLoadQueryFont(display, _(resources->clock_font))) == NULL)
2416                 if((clockfont = XLoadQueryFont(display, _(resources->clock_font2))) == NULL)
2417                         clockfont = XLoadQueryFont(display, "fixed");
2418
2419         init_xft();
2420         if(get_resources()->use_fontset)
2421         {
2422                 char **m, *d;
2423                 int n;
2424
2425 // FIXME: should check the m,d,n values
2426                 smallfontset = XCreateFontSet(display, resources->small_fontset, &m, &n, &d);
2427                 if( !smallfontset )
2428                         smallfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2429                 mediumfontset = XCreateFontSet(display, resources->medium_fontset, &m, &n, &d);
2430                 if( !mediumfontset )
2431                         mediumfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2432                 largefontset = XCreateFontSet(display, resources->large_fontset, &m, &n, &d);
2433                 if( !largefontset )
2434                         largefontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2435                 bigfontset = XCreateFontSet(display, resources->big_fontset, &m, &n, &d);
2436                 if( !bigfontset )
2437                         bigfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2438                 clockfontset = XCreateFontSet(display, resources->clock_fontset, &m, &n, &d);
2439                 if( !clockfontset )
2440                         clockfontset = XCreateFontSet(display, "fixed,*", &m, &n, &d);
2441                 if(clockfontset && bigfontset && largefontset && mediumfontset && smallfontset) {
2442                         curr_fontset = mediumfontset;
2443                         get_resources()->use_fontset = 1;
2444                 }
2445                 else {
2446                         curr_fontset = 0;
2447                         get_resources()->use_fontset = 0;
2448                 }
2449         }
2450
2451         return 0;
2452 }
2453
2454 void BC_WindowBase::init_xft()
2455 {
2456 #ifdef HAVE_XFT
2457         if( !get_resources()->use_xft ) return;
2458 // apparently, xft is not reentrant, more than this is needed
2459 static Mutex xft_init_lock("BC_WindowBase::xft_init_lock", 0);
2460 xft_init_lock.lock("BC_WindowBase::init_xft");
2461         if(!(smallfont_xft =
2462                 (resources->small_font_xft[0] == '-' ?
2463                         xftFontOpenXlfd(display, screen, resources->small_font_xft) :
2464                         xftFontOpenName(display, screen, resources->small_font_xft))) )
2465                 if(!(smallfont_xft =
2466                         xftFontOpenXlfd(display, screen, resources->small_font_xft2)))
2467                         smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2468         if(!(mediumfont_xft =
2469                 (resources->medium_font_xft[0] == '-' ?
2470                         xftFontOpenXlfd(display, screen, resources->medium_font_xft) :
2471                         xftFontOpenName(display, screen, resources->medium_font_xft))) )
2472                 if(!(mediumfont_xft =
2473                         xftFontOpenXlfd(display, screen, resources->medium_font_xft2)))
2474                         mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2475         if(!(largefont_xft =
2476                 (resources->large_font_xft[0] == '-' ?
2477                         xftFontOpenXlfd(display, screen, resources->large_font_xft) :
2478                         xftFontOpenName(display, screen, resources->large_font_xft))) )
2479                 if(!(largefont_xft =
2480                         xftFontOpenXlfd(display, screen, resources->large_font_xft2)))
2481                         largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
2482         if(!(bigfont_xft =
2483                 (resources->big_font_xft[0] == '-' ?
2484                         xftFontOpenXlfd(display, screen, resources->big_font_xft) :
2485                         xftFontOpenName(display, screen, resources->big_font_xft))) )
2486                 if(!(bigfont_xft =
2487                         xftFontOpenXlfd(display, screen, resources->big_font_xft2)))
2488                         bigfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2489         if(!(clockfont_xft =
2490                 (resources->clock_font_xft[0] == '-' ?
2491                         xftFontOpenXlfd(display, screen, resources->clock_font_xft) :
2492                         xftFontOpenName(display, screen, resources->clock_font_xft))) )
2493                 clockfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2494
2495
2496         if(!(bold_smallfont_xft =
2497                 (resources->small_b_font_xft[0] == '-' ?
2498                         xftFontOpenXlfd(display, screen, resources->small_b_font_xft) :
2499                         xftFontOpenName(display, screen, resources->small_b_font_xft))) )
2500                 bold_smallfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2501         if(!(bold_mediumfont_xft =
2502                 (resources->medium_b_font_xft[0] == '-' ?
2503                         xftFontOpenXlfd(display, screen, resources->medium_b_font_xft) :
2504                         xftFontOpenName(display, screen, resources->medium_b_font_xft))) )
2505                 bold_mediumfont_xft = xftFontOpenXlfd(display, screen, "fixed");
2506         if(!(bold_largefont_xft =
2507                 (resources->large_b_font_xft[0] == '-' ?
2508                         xftFontOpenXlfd(display, screen, resources->large_b_font_xft) :
2509                         xftFontOpenName(display, screen, resources->large_b_font_xft))) )
2510                 bold_largefont_xft = xftFontOpenXlfd(display, screen, "fixed");
2511
2512         if( !smallfont_xft || !mediumfont_xft || !largefont_xft || !bigfont_xft ||
2513             !bold_largefont_xft || !bold_mediumfont_xft || !bold_largefont_xft ||
2514             !clockfont_xft ) {
2515                 printf("BC_WindowBase::init_fonts: no xft fonts found:"
2516                         " %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n %s=%p\n",
2517                         resources->small_font_xft, smallfont_xft,
2518                         resources->medium_font_xft, mediumfont_xft,
2519                         resources->large_font_xft, largefont_xft,
2520                         resources->big_font_xft, bigfont_xft,
2521                         resources->clock_font_xft, clockfont_xft,
2522                         resources->small_b_font_xft, bold_smallfont_xft,
2523                         resources->medium_b_font_xft, bold_mediumfont_xft,
2524                         resources->large_b_font_xft, bold_largefont_xft);
2525                 get_resources()->use_xft = 0;
2526                 exit(1);
2527         }
2528 // _XftDisplayInfo needs a lock.
2529         xftDefaultHasRender(display);
2530 xft_init_lock.unlock();
2531 #endif // HAVE_XFT
2532 }
2533
2534 void BC_WindowBase::init_glyphs()
2535 {
2536 // draw all ascii char glyphs
2537 //  There are problems with some/my graphics boards/drivers
2538 //  which cause some glyphs to be munged if draws occur while
2539 //  the font is being loaded.  This code fills the font caches
2540 //  by drawing all the ascii glyphs before the system starts.
2541 //  Not a fix, but much better than nothing.
2542         static int inited = 0;
2543         if( inited ) return;
2544         XGrabServer(display);
2545         XSync(display, 0);
2546         inited = 1;
2547         int cur_font = current_font;
2548 // locale encodings, needed glyphs to be preloaded
2549         const char *text = _( // ascii 0x20...0x7e
2550                 " !\"#$%&'()*+,-./0123456789:;<=>?"
2551                 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
2552                 "`abcdefghijklmnopqrstuvwxyz{|}~");
2553         for( int font=SMALLFONT; font<=LARGEFONT; ++font ) {
2554                 set_font(font);
2555                 draw_text(5,5, text);
2556         }
2557         set_font(cur_font);
2558         XUngrabServer(display);
2559 }
2560
2561 void BC_WindowBase::init_im()
2562 {
2563         XIMStyles *xim_styles;
2564         XIMStyle xim_style;
2565
2566         if(!(input_method = XOpenIM(display, NULL, NULL, NULL)))
2567         {
2568                 printf("BC_WindowBase::init_im: Could not open input method.\n");
2569                 XSetLocaleModifiers("@im=local");
2570                 if(!(input_method = XOpenIM(display, NULL, NULL, NULL))) {
2571                 printf("BC_WindowBase::init_im: Could not open input method local.\n");
2572                 exit(1);
2573                 }
2574         }
2575         if(XGetIMValues(input_method, XNQueryInputStyle, &xim_styles, NULL) ||
2576                         xim_styles == NULL)
2577         {
2578                 printf("BC_WindowBase::init_im: Input method doesn't support any styles.\n");
2579                 XCloseIM(input_method);
2580                 exit(1);
2581         }
2582
2583         xim_style = 0;
2584         for(int z = 0;  z < xim_styles->count_styles;  z++)
2585         {
2586                 if(xim_styles->supported_styles[z] == (XIMPreeditNothing | XIMStatusNothing))
2587                 {
2588                         xim_style = xim_styles->supported_styles[z];
2589                         break;
2590                 }
2591         }
2592         XFree(xim_styles);
2593
2594         if(xim_style == 0)
2595         {
2596                 printf("BC_WindowBase::init_im: Input method doesn't support the style we need.\n");
2597                 XCloseIM(input_method);
2598                 exit(1);
2599         }
2600
2601         input_context = XCreateIC(input_method, XNInputStyle, xim_style,
2602                 XNClientWindow, win, XNFocusWindow, win, NULL);
2603         if(!input_context)
2604         {
2605                 printf("BC_WindowBase::init_im: Failed to create input context.\n");
2606                 XCloseIM(input_method);
2607                 exit(1);
2608         }
2609 }
2610
2611 void BC_WindowBase::finit_im()
2612 {
2613         if( input_context ) {
2614                 XDestroyIC(input_context);
2615                 input_context = 0;
2616         }
2617         if( input_method ) {
2618                 XCloseIM(input_method);
2619                 input_method = 0;
2620         }
2621 }
2622
2623
2624 int BC_WindowBase::get_color(int64_t color)
2625 {
2626 // return pixel of color
2627 // use this only for drawing subwindows not for bitmaps
2628          int i, test, difference;
2629
2630         switch(color_model)
2631         {
2632         case BC_RGB8:
2633                 if(private_color)
2634                         return get_color_rgb8(color);
2635 // test last color looked up
2636                 if(current_color_value == color)
2637                         return current_color_pixel;
2638
2639 // look up in table
2640                 current_color_value = color;
2641                 for(i = 0; i < total_colors; i++)
2642                 {
2643                         if(color_table[i][0] == color)
2644                         {
2645                                 current_color_pixel = color_table[i][1];
2646                                 return current_color_pixel;
2647                         }
2648                 }
2649
2650 // find nearest match
2651                 difference = 0xFFFFFF;
2652
2653                 for(i = 0; i < total_colors; i++)
2654                 {
2655                         test = abs((int)(color_table[i][0] - color));
2656
2657                         if(test < difference)
2658                         {
2659                                 current_color_pixel = color_table[i][1];
2660                                 difference = test;
2661                         }
2662                 }
2663                 return current_color_pixel;
2664
2665         case BC_RGB565:
2666                 return get_color_rgb16(color);
2667
2668         case BC_BGR565:
2669                 return get_color_bgr16(color);
2670
2671         case BC_RGB888:
2672         case BC_BGR888:
2673                 return client_byte_order == server_byte_order ?
2674                         color : get_color_bgr24(color);
2675
2676         default:
2677                 return color;
2678         }
2679         return 0;
2680 }
2681
2682 int BC_WindowBase::get_color_rgb8(int color)
2683 {
2684         int pixel;
2685
2686         pixel = (color & 0xc00000) >> 16;
2687         pixel += (color & 0xe000) >> 10;
2688         pixel += (color & 0xe0) >> 5;
2689         return pixel;
2690 }
2691
2692 int64_t BC_WindowBase::get_color_rgb16(int color)
2693 {
2694         int64_t result;
2695         result = (color & 0xf80000) >> 8;
2696         result += (color & 0xfc00) >> 5;
2697         result += (color & 0xf8) >> 3;
2698
2699         return result;
2700 }
2701
2702 int64_t BC_WindowBase::get_color_bgr16(int color)
2703 {
2704         int64_t result;
2705         result = (color & 0xf80000) >> 19;
2706         result += (color & 0xfc00) >> 5;
2707         result += (color & 0xf8) << 8;
2708
2709         return result;
2710 }
2711
2712 int64_t BC_WindowBase::get_color_bgr24(int color)
2713 {
2714         int64_t result;
2715         result = (color & 0xff) << 16;
2716         result += (color & 0xff00);
2717         result += (color & 0xff0000) >> 16;
2718         return result;
2719 }
2720
2721 void BC_WindowBase::start_video()
2722 {
2723         cursor_timer->update();
2724         video_on = 1;
2725 //      set_color(BLACK);
2726 //      draw_box(0, 0, get_w(), get_h());
2727 //      flash();
2728 }
2729
2730 void BC_WindowBase::stop_video()
2731 {
2732         video_on = 0;
2733         unhide_cursor();
2734 }
2735
2736
2737
2738 int64_t BC_WindowBase::get_color()
2739 {
2740         return top_level->current_color;
2741 }
2742
2743 void BC_WindowBase::set_color(int64_t color)
2744 {
2745         top_level->current_color = color;
2746         XSetForeground(top_level->display,
2747                 top_level->gc,
2748                 top_level->get_color(color));
2749 }
2750
2751 void BC_WindowBase::set_opaque()
2752 {
2753         XSetFunction(top_level->display, top_level->gc, GXcopy);
2754 }
2755
2756 void BC_WindowBase::set_inverse()
2757 {
2758         XSetFunction(top_level->display, top_level->gc, GXxor);
2759 }
2760
2761 void BC_WindowBase::set_line_width(int value)
2762 {
2763         this->line_width = value;
2764         XSetLineAttributes(top_level->display, top_level->gc, value,    /* line_width */
2765                 line_dashes == 0 ? LineSolid : LineOnOffDash,           /* line_style */
2766                 line_dashes == 0 ? CapRound : CapNotLast,               /* cap_style */
2767                 JoinMiter);                                             /* join_style */
2768
2769         if(line_dashes > 0) {
2770                 const char dashes = line_dashes;
2771                 XSetDashes(top_level->display, top_level->gc, 0, &dashes, 1);
2772         }
2773
2774 // XGCValues gcvalues;
2775 // unsigned long gcmask;
2776 // gcmask = GCCapStyle | GCJoinStyle;
2777 // XGetGCValues(top_level->display, top_level->gc, gcmask, &gcvalues);
2778 // printf("BC_WindowBase::set_line_width %d %d %d\n", __LINE__, gcvalues.cap_style, gcvalues.join_style);
2779 }
2780
2781 void BC_WindowBase::set_line_dashes(int value)
2782 {
2783         line_dashes = value;
2784 // call XSetLineAttributes
2785         set_line_width(line_width);
2786 }
2787
2788
2789 Cursor BC_WindowBase::get_cursor_struct(int cursor)
2790 {
2791         switch(cursor)
2792         {
2793                 case ARROW_CURSOR:         return top_level->arrow_cursor;
2794                 case CROSS_CURSOR:         return top_level->cross_cursor;
2795                 case IBEAM_CURSOR:         return top_level->ibeam_cursor;
2796                 case VSEPARATE_CURSOR:     return top_level->vseparate_cursor;
2797                 case HSEPARATE_CURSOR:     return top_level->hseparate_cursor;
2798                 case MOVE_CURSOR:          return top_level->move_cursor;
2799                 case LEFT_CURSOR:          return top_level->left_cursor;
2800                 case RIGHT_CURSOR:         return top_level->right_cursor;
2801                 case UPRIGHT_ARROW_CURSOR: return top_level->upright_arrow_cursor;
2802                 case UPLEFT_RESIZE:        return top_level->upleft_resize_cursor;
2803                 case UPRIGHT_RESIZE:       return top_level->upright_resize_cursor;
2804                 case DOWNLEFT_RESIZE:      return top_level->downleft_resize_cursor;
2805                 case DOWNRIGHT_RESIZE:     return top_level->downright_resize_cursor;
2806                 case HOURGLASS_CURSOR:     return top_level->hourglass_cursor;
2807                 case TRANSPARENT_CURSOR:   return top_level->transparent_cursor;
2808                 case GRABBED_CURSOR:       return top_level->grabbed_cursor;
2809         }
2810         return 0;
2811 }
2812
2813 void BC_WindowBase::set_cursor(int cursor, int override, int flush)
2814 {
2815 // inherit cursor from parent
2816         if(cursor < 0)
2817         {
2818                 XUndefineCursor(top_level->display, win);
2819                 current_cursor = cursor;
2820         }
2821         else
2822 // don't change cursor if overridden
2823         if((!top_level->is_hourglass && !is_transparent) ||
2824                 override)
2825         {
2826                 XDefineCursor(top_level->display, win, get_cursor_struct(cursor));
2827                 if(flush) this->flush();
2828         }
2829
2830         if(!override) current_cursor = cursor;
2831 }
2832
2833 void BC_WindowBase::set_x_cursor(int cursor)
2834 {
2835         temp_cursor = XCreateFontCursor(top_level->display, cursor);
2836         XDefineCursor(top_level->display, win, temp_cursor);
2837         current_cursor = cursor;
2838         flush();
2839 }
2840
2841 int BC_WindowBase::get_cursor()
2842 {
2843         return current_cursor;
2844 }
2845
2846 void BC_WindowBase::start_hourglass()
2847 {
2848         top_level->start_hourglass_recursive();
2849         top_level->flush();
2850 }
2851
2852 void BC_WindowBase::stop_hourglass()
2853 {
2854         top_level->stop_hourglass_recursive();
2855         top_level->flush();
2856 }
2857
2858 void BC_WindowBase::start_hourglass_recursive()
2859 {
2860         if(this == top_level)
2861         {
2862                 hourglass_total++;
2863                 is_hourglass = 1;
2864         }
2865
2866         if(!is_transparent)
2867         {
2868                 set_cursor(HOURGLASS_CURSOR, 1, 0);
2869                 for(int i = 0; i < subwindows->total; i++)
2870                 {
2871                         subwindows->values[i]->start_hourglass_recursive();
2872                 }
2873         }
2874 }
2875
2876 void BC_WindowBase::stop_hourglass_recursive()
2877 {
2878         if(this == top_level)
2879         {
2880                 if(hourglass_total == 0) return;
2881                 top_level->hourglass_total--;
2882         }
2883
2884         if(!top_level->hourglass_total)
2885         {
2886                 top_level->is_hourglass = 0;
2887
2888 // Cause set_cursor to perform change
2889                 if(!is_transparent)
2890                         set_cursor(current_cursor, 1, 0);
2891
2892                 for(int i = 0; i < subwindows->total; i++)
2893                 {
2894                         subwindows->values[i]->stop_hourglass_recursive();
2895                 }
2896         }
2897 }
2898
2899
2900
2901
2902 XFontStruct* BC_WindowBase::get_font_struct(int font)
2903 {
2904 // Clear out unrelated flags
2905         if(font & BOLDFACE) font ^= BOLDFACE;
2906
2907         switch(font) {
2908                 case SMALLFONT:  return top_level->smallfont;  break;
2909                 case MEDIUMFONT: return top_level->mediumfont; break;
2910                 case LARGEFONT:  return top_level->largefont;  break;
2911                 case BIGFONT:    return top_level->bigfont;    break;
2912                 case CLOCKFONT:  return top_level->clockfont;  break;
2913         }
2914         return 0;
2915 }
2916
2917 XFontSet BC_WindowBase::get_fontset(int font)
2918 {
2919         XFontSet fs = 0;
2920
2921         if(get_resources()->use_fontset)
2922         {
2923                 switch(font & 0xff) {
2924                         case SMALLFONT:  fs = top_level->smallfontset; break;
2925                         case MEDIUMFONT: fs = top_level->mediumfontset; break;
2926                         case LARGEFONT:  fs = top_level->largefontset; break;
2927                         case BIGFONT:    fs = top_level->bigfontset;   break;
2928                         case CLOCKFONT: fs = top_level->clockfontset; break;
2929                 }
2930         }
2931
2932         return fs;
2933 }
2934
2935 #ifdef HAVE_XFT
2936 XftFont* BC_WindowBase::get_xft_struct(int font)
2937 {
2938         switch(font) {
2939                 case SMALLFONT:    return (XftFont*)top_level->smallfont_xft;
2940                 case MEDIUMFONT:   return (XftFont*)top_level->mediumfont_xft;
2941                 case LARGEFONT:    return (XftFont*)top_level->largefont_xft;
2942                 case BIGFONT:      return (XftFont*)top_level->bigfont_xft;
2943                 case CLOCKFONT:    return (XftFont*)top_level->clockfont_xft;
2944                 case MEDIUMFONT_3D: return (XftFont*)top_level->bold_mediumfont_xft;
2945                 case SMALLFONT_3D:  return (XftFont*)top_level->bold_smallfont_xft;
2946                 case LARGEFONT_3D:  return (XftFont*)top_level->bold_largefont_xft;
2947         }
2948
2949         return 0;
2950 }
2951 #endif
2952
2953
2954 int BC_WindowBase::get_current_font()
2955 {
2956         return top_level->current_font;
2957 }
2958
2959 void BC_WindowBase::set_font(int font)
2960 {
2961         top_level->current_font = font;
2962
2963 #ifdef HAVE_XFT
2964         if(get_resources()->use_xft) {}
2965         else
2966 #endif
2967         if(get_resources()->use_fontset) {
2968                 set_fontset(font);
2969         }
2970
2971         if(get_font_struct(font))
2972         {
2973                 XSetFont(top_level->display, top_level->gc, get_font_struct(font)->fid);
2974         }
2975
2976         return;
2977 }
2978
2979 void BC_WindowBase::set_fontset(int font)
2980 {
2981         XFontSet fs = 0;
2982
2983         if(get_resources()->use_fontset) {
2984                 switch(font) {
2985                         case SMALLFONT:  fs = top_level->smallfontset; break;
2986                         case MEDIUMFONT: fs = top_level->mediumfontset; break;
2987                         case LARGEFONT:  fs = top_level->largefontset; break;
2988                         case BIGFONT:    fs = top_level->bigfontset;   break;
2989                         case CLOCKFONT:  fs = top_level->clockfontset; break;
2990                 }
2991         }
2992
2993         curr_fontset = fs;
2994 }
2995
2996
2997 XFontSet BC_WindowBase::get_curr_fontset(void)
2998 {
2999         if(get_resources()->use_fontset)
3000                 return curr_fontset;
3001         return 0;
3002 }
3003
3004 int BC_WindowBase::get_single_text_width(int font, const char *text, int length)
3005 {
3006 #ifdef HAVE_XFT
3007         if(get_resources()->use_xft && get_xft_struct(font))
3008         {
3009                 XGlyphInfo extents;
3010 #ifdef X_HAVE_UTF8_STRING
3011                 if(get_resources()->locale_utf8)
3012                 {
3013                         xftTextExtentsUtf8(top_level->display,
3014                                 get_xft_struct(font),
3015                                 (const XftChar8 *)text,
3016                                 length,
3017                                 &extents);
3018                 }
3019                 else
3020 #endif
3021                 {
3022                         xftTextExtents8(top_level->display,
3023                                 get_xft_struct(font),
3024                                 (const XftChar8 *)text,
3025                                 length,
3026                                 &extents);
3027                 }
3028                 return extents.xOff;
3029         }
3030         else
3031 #endif
3032         if(get_resources()->use_fontset && top_level->get_fontset(font))
3033                 return XmbTextEscapement(top_level->get_fontset(font), text, length);
3034         else
3035         if(get_font_struct(font))
3036                 return XTextWidth(get_font_struct(font), text, length);
3037         else
3038         {
3039                 int w = 0;
3040                 switch(font)
3041                 {
3042                         case MEDIUM_7SEGMENT:
3043                                 return get_resources()->medium_7segment[0]->get_w() * length;
3044                                 break;
3045
3046                         default:
3047                                 return 0;
3048                 }
3049                 return w;
3050         }
3051 }
3052
3053 int BC_WindowBase::get_text_width(int font, const char *text, int length)
3054 {
3055         int i, j, w = 0, line_w = 0;
3056         if(length < 0) length = strlen(text);
3057
3058         for(i = 0, j = 0; i <= length; i++)
3059         {
3060                 line_w = 0;
3061                 if(text[i] == '\n')
3062                 {
3063                         line_w = get_single_text_width(font, &text[j], i - j);
3064                         j = i + 1;
3065                 }
3066                 else
3067                 if(text[i] == 0)
3068                 {
3069                         line_w = get_single_text_width(font, &text[j], length - j);
3070                 }
3071                 if(line_w > w) w = line_w;
3072         }
3073
3074         if(i > length && w == 0)
3075         {
3076                 w = get_single_text_width(font, text, length);
3077         }
3078
3079         return w;
3080 }
3081
3082 int BC_WindowBase::get_text_width(int font, const wchr_t *text, int length)
3083 {
3084         int i, j, w = 0;
3085         if( length < 0 ) length = wstrlen(text);
3086
3087         for( i=j=0; i<length && text[i]; ++i ) {
3088                 if( text[i] != '\n' ) continue;
3089                 if( i > j ) {
3090                         int lw = get_single_text_width(font, &text[j], i-j);
3091                         if( w < lw ) w = lw;
3092                 }
3093                 j = i + 1;
3094         }
3095         if( j < length ) {
3096                 int lw = get_single_text_width(font, &text[j], length-j);
3097                 if( w < lw ) w = lw;
3098         }
3099
3100         return w;
3101 }
3102
3103 int BC_WindowBase::get_text_ascent(int font)
3104 {
3105 #ifdef HAVE_XFT
3106         XftFont *fstruct;
3107         if( (fstruct = get_xft_struct(font)) != 0 )
3108                 return fstruct->ascent;
3109 #endif
3110         if(get_resources()->use_fontset && top_level->get_fontset(font))
3111         {
3112                 XFontSetExtents *extents;
3113
3114                 extents = XExtentsOfFontSet(top_level->get_fontset(font));
3115                 return -extents->max_logical_extent.y;
3116         }
3117
3118         if(get_font_struct(font))
3119                 return top_level->get_font_struct(font)->ascent;
3120
3121         switch(font) {
3122                 case MEDIUM_7SEGMENT:
3123                         return get_resources()->medium_7segment[0]->get_h();
3124         }
3125         return 0;
3126 }
3127
3128 int BC_WindowBase::get_text_descent(int font)
3129 {
3130 #ifdef HAVE_XFT
3131         XftFont *fstruct;
3132         if( (fstruct = get_xft_struct(font)) != 0 )
3133                 return fstruct->descent;
3134 #endif
3135         if(get_resources()->use_fontset && top_level->get_fontset(font)) {
3136                 XFontSetExtents *extents;
3137                 extents = XExtentsOfFontSet(top_level->get_fontset(font));
3138                 return (extents->max_logical_extent.height
3139                         + extents->max_logical_extent.y);
3140         }
3141
3142         if(get_font_struct(font))
3143                 return top_level->get_font_struct(font)->descent;
3144
3145         return 0;
3146 }
3147
3148 int BC_WindowBase::get_text_height(int font, const char *text)
3149 {
3150         int rowh;
3151 #ifdef HAVE_XFT
3152         XftFont *fstruct;
3153         if( (fstruct = get_xft_struct(font)) != 0 )
3154                 rowh = fstruct->height;
3155         else
3156 #endif
3157                 rowh = get_text_ascent(font) + get_text_descent(font);
3158
3159         if(!text) return rowh;
3160
3161 // Add height of lines
3162         int h = 0, i, length = strlen(text);
3163         for(i = 0; i <= length; i++)
3164         {
3165                 if(text[i] == '\n')
3166                         h++;
3167                 else
3168                 if(text[i] == 0)
3169                         h++;
3170         }
3171         return h * rowh;
3172 }
3173
3174 // truncate the text with ... & return a new string
3175 char *BC_WindowBase::get_truncated_text(int font, const char *text, int max_w)
3176 {
3177         char *result = cstrdup(text);
3178         int text_w = get_text_width(font, text);
3179         int ci = -1, len = strlen(text);
3180         if( text_w > max_w ) {
3181 // get center of string
3182                 int cx = text_w/2, best = INT_MAX;
3183                 for( int i=1; i<=len; ++i ) {
3184                         int cw = get_text_width(font, text, i);
3185                         if( abs(cw-cx) < abs(best-cx) ) {
3186                                 best = cw;  ci = i;
3187                         }
3188                 }
3189         }
3190         if( ci > 0 && ci < len-1 ) {
3191 // insert ... in the center
3192                 result[ci-1] = result[ci] = result[ci+1] = '.';
3193
3194                 while( ci-2>=0 && ci+2<(int)strlen(result) &&
3195                        get_text_width(font, result) > max_w ) {
3196 // take away a character from the longer side
3197                         int left_w = get_text_width(font, result, ci-2);
3198                         int right_w = get_text_width(font, result + ci+3);
3199                         int i = left_w > right_w ? --ci-1 : ci+2;
3200                         while( (result[i]=result[i+1])!=0 ) ++i;
3201                 }
3202         }
3203
3204         return result;
3205 }
3206
3207 BC_Bitmap* BC_WindowBase::new_bitmap(int w, int h, int color_model)
3208 {
3209         if(color_model < 0) color_model = top_level->get_color_model();
3210         return new BC_Bitmap(top_level, w, h, color_model);
3211 }
3212
3213 void BC_WindowBase::init_wait()
3214 {
3215 #ifndef SINGLE_THREAD
3216         if(window_type != MAIN_WINDOW)
3217                 top_level->init_wait();
3218         init_lock->lock("BC_WindowBase::init_wait");
3219         init_lock->unlock();
3220 #endif
3221 }
3222
3223 int BC_WindowBase::accel_available(int color_model, int lock_it)
3224 {
3225         if( window_type != MAIN_WINDOW )
3226                 return top_level->accel_available(color_model, lock_it);
3227         if( lock_it )
3228                 lock_window("BC_WindowBase::accel_available");
3229
3230         switch(color_model) {
3231         case BC_YUV420P:
3232                 grab_port_id(color_model);
3233                 break;
3234
3235         case BC_YUV422:
3236                 grab_port_id(color_model);
3237                 break;
3238
3239         default:
3240                 break;
3241         }
3242
3243         if( lock_it )
3244                 unlock_window();
3245 //printf("BC_WindowBase::accel_available %d %d\n", color_model, xvideo_port_id);
3246         return xvideo_port_id >= 0 ? 1 : 0;
3247 }
3248
3249
3250 int BC_WindowBase::grab_port_id(int color_model)
3251 {
3252 #ifdef HAVE_XV
3253         if( !get_resources()->use_xvideo ||     // disabled
3254             !get_resources()->use_shm )         // Only local server is fast enough.
3255                 return -1;
3256         if( xvideo_port_id >= 0 )
3257                 return xvideo_port_id;
3258
3259         unsigned int ver, rev, reqBase, eventBase, errorBase;
3260         if( Success != XvQueryExtension(display, // XV extension is available
3261                     &ver, &rev, &reqBase, &eventBase, &errorBase) )
3262                 return -1;
3263
3264 // XV adaptors are available
3265         unsigned int numAdapt = 0;
3266         XvAdaptorInfo *info = 0;
3267         XvQueryAdaptors(display, DefaultRootWindow(display), &numAdapt, &info);
3268         if( !numAdapt )
3269                 return -1;
3270
3271 // Translate from color_model to X color model
3272         int x_color_model = BC_CModels::bc_to_x(color_model);
3273
3274 // Get adaptor with desired color model
3275         for( int i = 0; i < (int)numAdapt && xvideo_port_id == -1; i++) {
3276                 if( !(info[i].type & XvImageMask) || !info[i].num_ports ) continue;
3277 // adaptor supports XvImages
3278                 int numFormats = 0, numPorts = info[i].num_ports;
3279                 XvImageFormatValues *formats =
3280                         XvListImageFormats(display, info[i].base_id, &numFormats);
3281                 if( !formats ) continue;
3282
3283                 for( int j=0; j<numFormats && xvideo_port_id<0; ++j ) {
3284                         if( formats[j].id != x_color_model ) continue;
3285 // this adaptor supports the desired format, grab a port
3286                         for( int k=0; k<numPorts; ++k ) {
3287                                 if( Success == XvGrabPort(top_level->display,
3288                                         info[i].base_id+k, CurrentTime) ) {
3289 //printf("BC_WindowBase::grab_port_id %llx\n", info[i].base_id);
3290                                         xvideo_port_id = info[i].base_id + k;
3291                                         break;
3292                                 }
3293                         }
3294                 }
3295                 XFree(formats);
3296         }
3297
3298         XvFreeAdaptorInfo(info);
3299         return xvideo_port_id;
3300 #else
3301         return -1;
3302 #endif
3303 }
3304
3305
3306 int BC_WindowBase::show_window(int flush)
3307 {
3308         for(int i = 0; i < subwindows->size(); i++)
3309         {
3310                 subwindows->get(i)->show_window(0);
3311         }
3312
3313         XMapWindow(top_level->display, win);
3314         if(flush) XFlush(top_level->display);
3315 //      XSync(top_level->display, 0);
3316         hidden = 0;
3317         return 0;
3318 }
3319
3320 int BC_WindowBase::hide_window(int flush)
3321 {
3322         for(int i = 0; i < subwindows->size(); i++)
3323         {
3324                 subwindows->get(i)->hide_window(0);
3325         }
3326
3327         XUnmapWindow(top_level->display, win);
3328         if(flush) this->flush();
3329         hidden = 1;
3330         return 0;
3331 }
3332
3333 BC_MenuBar* BC_WindowBase::add_menubar(BC_MenuBar *menu_bar)
3334 {
3335         subwindows->append((BC_SubWindow*)menu_bar);
3336
3337         menu_bar->parent_window = this;
3338         menu_bar->top_level = this->top_level;
3339         menu_bar->initialize();
3340         return menu_bar;
3341 }
3342
3343 BC_WindowBase* BC_WindowBase::add_popup(BC_WindowBase *window)
3344 {
3345 //printf("BC_WindowBase::add_popup window=%p win=%p\n", window, window->win);
3346         if(this != top_level) return top_level->add_popup(window);
3347         popups.append(window);
3348         return window;
3349 }
3350
3351 void BC_WindowBase::remove_popup(BC_WindowBase *window)
3352 {
3353 //printf("BC_WindowBase::remove_popup %d size=%d window=%p win=%p\n", __LINE__, popups.size(), window, window->win);
3354         if(this != top_level)
3355                 top_level->remove_popup(window);
3356         else
3357                 popups.remove(window);
3358 //printf("BC_WindowBase::remove_popup %d size=%d window=%p win=%p\n", __LINE__, popups.size(), window, window->win);
3359 }
3360
3361
3362 BC_WindowBase* BC_WindowBase::add_subwindow(BC_WindowBase *subwindow)
3363 {
3364         subwindows->append(subwindow);
3365
3366         if(subwindow->bg_color == -1) subwindow->bg_color = this->bg_color;
3367
3368 // parent window must be set before the subwindow initialization
3369         subwindow->parent_window = this;
3370         subwindow->top_level = this->top_level;
3371
3372 // Execute derived initialization
3373         subwindow->initialize();
3374         return subwindow;
3375 }
3376
3377
3378 BC_WindowBase* BC_WindowBase::add_tool(BC_WindowBase *subwindow)
3379 {
3380         return add_subwindow(subwindow);
3381 }
3382
3383 int BC_WindowBase::flash(int x, int y, int w, int h, int flush)
3384 {
3385         if( !top_level->flash_enabled ) return 0;
3386 //printf("BC_WindowBase::flash %d %d %d %d %d\n", __LINE__, w, h, this->w, this->h);
3387         set_opaque();
3388         XSetWindowBackgroundPixmap(top_level->display, win, pixmap->opaque_pixmap);
3389         if(x >= 0)
3390         {
3391                 XClearArea(top_level->display, win, x, y, w, h, 0);
3392         }
3393         else
3394         {
3395                 XClearWindow(top_level->display, win);
3396         }
3397
3398         if(flush)
3399                 this->flush();
3400         return 0;
3401 }
3402
3403 int BC_WindowBase::flash(int flush)
3404 {
3405         flash(-1, -1, -1, -1, flush);
3406         return 0;
3407 }
3408
3409 void BC_WindowBase::flush()
3410 {
3411         //if(!get_window_lock())
3412         //      printf("BC_WindowBase::flush %s not locked\n", top_level->title);
3413         // X gets hosed if Flush/Sync are not user locked (at libX11-1.1.5 / libxcb-1.1.91)
3414         //   _XReply deadlocks in condition_wait waiting for xlib lock when waiters!=-1
3415         int locked  = get_window_lock();
3416         if( !locked ) lock_window("BC_WindowBase::flush");
3417         XFlush(top_level->display);
3418         if( !locked ) unlock_window();
3419 }
3420
3421 void BC_WindowBase::sync_display()
3422 {
3423         int locked  = get_window_lock();
3424         if( !locked ) lock_window("BC_WindowBase::sync_display");
3425         XSync(top_level->display, False);
3426         if( !locked ) unlock_window();
3427 }
3428
3429 int BC_WindowBase::get_window_lock()
3430 {
3431 #ifdef SINGLE_THREAD
3432         return BC_Display::display_global->get_display_locked();
3433 #else
3434         return top_level->window_lock;
3435 #endif
3436 }
3437
3438 int BC_WindowBase::lock_window(const char *location)
3439 {
3440         if(top_level && top_level != this)
3441         {
3442                 top_level->lock_window(location);
3443         }
3444         else
3445         if(top_level)
3446         {
3447                 SET_LOCK(this, title, location);
3448 #ifdef SINGLE_THREAD
3449                 BC_Display::lock_display(location);
3450 #else
3451                 XLockDisplay(top_level->display);
3452                 top_level->display_lock_owner = pthread_self();
3453 #endif
3454                 SET_LOCK2
3455                 ++top_level->window_lock;
3456         }
3457         else
3458         {
3459                 printf("BC_WindowBase::lock_window top_level NULL\n");
3460         }
3461         return 0;
3462 }
3463
3464 int BC_WindowBase::unlock_window()
3465 {
3466         if(top_level && top_level != this)
3467         {
3468                 top_level->unlock_window();
3469         }
3470         else
3471         if(top_level)
3472         {
3473                 UNSET_LOCK(this);
3474                 if( !top_level->window_lock ) {
3475                         printf("BC_WindowBase::unlock_window %s not locked\n", title);
3476                         booby();
3477                 }
3478                 if( top_level->window_lock > 0 )
3479                         if( --top_level->window_lock == 0 )
3480                                 top_level->display_lock_owner = 0;
3481 #ifdef SINGLE_THREAD
3482                 BC_Display::unlock_display();
3483 #else
3484                 XUnlockDisplay(top_level->display);
3485 #endif
3486         }
3487         else
3488         {
3489                 printf("BC_WindowBase::unlock_window top_level NULL\n");
3490         }
3491         return 0;
3492 }
3493
3494 int BC_WindowBase::break_lock()
3495 {
3496         if( !top_level ) return 0;
3497         if( top_level != this ) return top_level->break_lock();
3498         if( top_level->display_lock_owner != pthread_self() ) return 0;
3499         if( top_level->window_lock != 1 ) return 0;
3500         UNSET_LOCK(this);
3501         window_lock = 0;
3502         display_lock_owner = 0;
3503 #ifdef SINGLE_THREAD
3504         BC_Display::unlock_display();
3505 #else
3506         XUnlockDisplay(display);
3507 #endif
3508         return 1;
3509 }
3510
3511 void BC_WindowBase::set_done(int return_value)
3512 {
3513         if(done_set) return;
3514         done_set = 1;
3515         if(window_type != MAIN_WINDOW)
3516                 top_level->set_done(return_value);
3517         else
3518         {
3519 #ifdef SINGLE_THREAD
3520                 this->return_value = return_value;
3521                 BC_Display::display_global->arm_completion(this);
3522                 completion_lock->unlock();
3523 #else // SINGLE_THREAD
3524                 init_wait();
3525                 if( !event_thread ) return;
3526                 XEvent *event = new_xevent();
3527                 XClientMessageEvent *ptr = (XClientMessageEvent*)event;
3528                 event->type = ClientMessage;
3529                 ptr->message_type = SetDoneXAtom;
3530                 ptr->format = 32;
3531                 this->return_value = return_value;
3532 // May lock up here because XSendEvent doesn't work too well
3533 // asynchronous with XNextEvent.
3534 // This causes BC_WindowEvents to forward a copy of the event to run_window where
3535 // it is deleted.
3536 // Deletion of event_thread is done at the end of BC_WindowBase::run_window() - by calling the destructor
3537                 put_event(event);
3538 #endif
3539         }
3540 }
3541
3542 void BC_WindowBase::close(int return_value)
3543 {
3544         hide_window();  flush();
3545         set_done(return_value);
3546 }
3547
3548 int BC_WindowBase::grab(BC_WindowBase *window)
3549 {
3550         int ret = 0;
3551         BC_WindowBase *grab_window = window->active_grab;
3552         if( grab_window ) {
3553                 int locked = get_window_lock();
3554                 if( locked ) unlock_window();
3555                 grab_window->lock_window("BC_WindowBase::grab(BC_WindowBase");
3556                 grab_window->handle_ungrab();
3557                 grab_window->unlock_window();
3558                 if( locked ) lock_window("BC_WindowBase::grab(BC_WindowBase");
3559         }
3560         window->grab_lock->lock("BC_WindowBase::grab");
3561         if( !window->active_grab ) {
3562                 window->active_grab = this;
3563                 this->grab_active = window;
3564                 ret = 1;
3565         }
3566         window->grab_lock->unlock();
3567         return ret;
3568 }
3569 int BC_WindowBase::ungrab(BC_WindowBase *window)
3570 {
3571         int ret = 0;
3572         window->grab_lock->lock("BC_WindowBase::ungrab");
3573         if( this == window->active_grab ) {
3574                 window->active_grab = 0;
3575                 this->grab_active = 0;
3576                 ret = 1;
3577         }
3578         window->grab_lock->unlock();
3579         return ret;
3580 }
3581 int BC_WindowBase::grab_event_count()
3582 {
3583         int result = 0;
3584 #ifndef SINGLE_THREAD
3585         result = grab_active->get_event_count();
3586 #endif
3587         return result;
3588 }
3589 int BC_WindowBase::grab_buttons()
3590 {
3591         XSync(top_level->display, False);
3592         if( XGrabButton(top_level->display, AnyButton, AnyModifier,
3593                         top_level->rootwin, True, ButtonPressMask | ButtonReleaseMask,
3594                         GrabModeAsync, GrabModeSync, None, None) == GrabSuccess ) {
3595                 set_active_subwindow(this);
3596                 return 0;
3597         }
3598         return 1;
3599 }
3600 void BC_WindowBase::ungrab_buttons()
3601 {
3602         XUngrabButton(top_level->display, AnyButton, AnyModifier, top_level->rootwin);
3603         set_active_subwindow(0);
3604         unhide_cursor();
3605 }
3606 void BC_WindowBase::grab_cursor()
3607 {
3608         Cursor cursor_grab = get_cursor_struct(GRABBED_CURSOR);
3609         XGrabPointer(top_level->display, top_level->rootwin, True,
3610                 PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
3611                 GrabModeAsync, GrabModeAsync, None, cursor_grab, CurrentTime);
3612 }
3613 void BC_WindowBase::ungrab_cursor()
3614 {
3615         XUngrabPointer(top_level->display, CurrentTime);
3616 }
3617
3618 // for get_root_w/h
3619 //   WidthOfScreen/HeightOfScreen of XDefaultScreenOfDisplay
3620 //   this is the bounding box of all the screens
3621
3622 int BC_WindowBase::get_root_w(int lock_display)
3623 {
3624         if(lock_display) lock_window("BC_WindowBase::get_root_w");
3625         Screen *def_screen = XDefaultScreenOfDisplay(top_level->display);
3626         int result = WidthOfScreen(def_screen);
3627         if(lock_display) unlock_window();
3628         return result;
3629 }
3630
3631 int BC_WindowBase::get_root_h(int lock_display)
3632 {
3633         if(lock_display) lock_window("BC_WindowBase::get_root_h");
3634         Screen *def_screen = XDefaultScreenOfDisplay(top_level->display);
3635         int result = HeightOfScreen(def_screen);
3636         if(lock_display) unlock_window();
3637         return result;
3638 }
3639
3640 XineramaScreenInfo *
3641 BC_WindowBase::get_xinerama_info(int screen)
3642 {
3643         if( !xinerama_info || !xinerama_screens ) return 0;
3644         if( screen >= 0 ) {
3645                 for( int i=0; i<xinerama_screens; ++i )
3646                         if( xinerama_info[i].screen_number == screen )
3647                                 return &xinerama_info[i];
3648                 return 0;
3649         }
3650         int top_x = get_x(), top_y = get_y();
3651         if(  BC_DisplayInfo::left_border >= 0 ) top_x +=  BC_DisplayInfo::left_border;
3652         if(  BC_DisplayInfo::top_border >= 0 ) top_y +=  BC_DisplayInfo::top_border;
3653         for( int i=0; i<xinerama_screens; ++i ) {
3654                 int scr_y = top_y - xinerama_info[i].y_org;
3655                 if( scr_y < 0 || scr_y >= xinerama_info[i].height ) continue;
3656                 int scr_x = top_x - xinerama_info[i].x_org;
3657                 if( scr_x >= 0 && scr_x < xinerama_info[i].width )
3658                         return &xinerama_info[i];
3659         }
3660         return 0;
3661 }
3662
3663 void BC_WindowBase::get_fullscreen_geometry(int &wx, int &wy, int &ww, int &wh)
3664 {
3665         XineramaScreenInfo *info = top_level->get_xinerama_info(-1);
3666         if( info ) {
3667                 wx = info->x_org;  wy = info->y_org;
3668                 ww = info->width;  wh = info->height;
3669         }
3670         else {
3671                 wx = get_screen_x(0, -1);
3672                 wy = get_screen_y(0, -1);
3673                 int scr_w0 = get_screen_w(0, 0);
3674                 int root_w = get_root_w(0);
3675                 int root_h = get_root_h(0);
3676                 if( root_w > scr_w0 ) { // multi-headed
3677                         if( wx >= scr_w0 ) {
3678                                 // assumes right side is the big one
3679                                 ww = root_w - scr_w0;
3680                                 wh = root_h;
3681                         }
3682                         else {
3683                                 // use same aspect ratio to compute left height
3684                                 ww = scr_w0;
3685                                 wh = (w*root_h) / (root_w-scr_w0);
3686                         }
3687                 }
3688                 else {
3689                         ww = root_w;
3690                         wh = root_h;
3691                 }
3692         }
3693 }
3694
3695 int BC_WindowBase::get_screen_x(int lock_display, int screen)
3696 {
3697         int result = -1;
3698         if(lock_display) lock_window("BC_WindowBase::get_screen_x");
3699         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3700         if( !info ) {
3701                 result = 0;
3702                 int root_w = get_root_w(0);
3703                 int root_h = get_root_h(0);
3704 // Shift X based on position of current window if dual head
3705                 if( (float)root_w/root_h > 1.8 ) {
3706                         root_w = get_screen_w(0, 0);
3707                         if( top_level->get_x() >= root_w )
3708                                 result = root_w;
3709                 }
3710         }
3711         else
3712                 result = info->x_org;
3713         if(lock_display) unlock_window();
3714         return result;
3715 }
3716
3717 int BC_WindowBase::get_screen_y(int lock_display, int screen)
3718 {
3719         if(lock_display) lock_window("BC_WindowBase::get_screen_y");
3720         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3721         int result = !info ? 0 : info->y_org;
3722         if(lock_display) unlock_window();
3723         return result;
3724 }
3725
3726 int BC_WindowBase::get_screen_w(int lock_display, int screen)
3727 {
3728         int result = -1;
3729         if(lock_display) lock_window("BC_WindowBase::get_screen_w");
3730         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3731         if( !info ) {
3732                 int width = get_root_w(0);
3733                 int height = get_root_h(0);
3734                 if( (float)width/height > 1.8 ) {
3735                         // If dual head, the screen width is > 16x9
3736                         // but we only want to fill one screen
3737                         // this code assumes the "big" screen is on the right
3738                         int scr_w0 = width / 2;
3739                         switch( height ) {
3740                         case 600:  scr_w0 = 800;   break;
3741                         case 720:  scr_w0 = 1280;  break;
3742                         case 1024: scr_w0 = 1280;  break;
3743                         case 1200: scr_w0 = 1600;  break;
3744                         case 1080: scr_w0 = 1920;  break;
3745                         }
3746                         int scr_w1 = width - scr_w0;
3747                         result = screen > 0 ? scr_w1 :
3748                                 screen == 0 ? scr_w0 :
3749                                 top_level->get_x() < scr_w0 ? scr_w0 : scr_w1;
3750                 }
3751                 else
3752                         result = width;
3753         }
3754         else
3755                 result = info->width;
3756         if(lock_display) unlock_window();
3757         return result;
3758 }
3759
3760 int BC_WindowBase::get_screen_h(int lock_display, int screen)
3761 {
3762         if(lock_display) lock_window("BC_WindowBase::get_screen_h");
3763         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3764         int result = info ? info->height : get_root_h(0);
3765         if(lock_display) unlock_window();
3766         return result;
3767 }
3768
3769 // Bottom right corner
3770 int BC_WindowBase::get_x2()
3771 {
3772         return w + x;
3773 }
3774
3775 int BC_WindowBase::get_y2()
3776 {
3777         return y + h;
3778 }
3779
3780 int BC_WindowBase::get_video_on()
3781 {
3782         return video_on;
3783 }
3784
3785 int BC_WindowBase::get_hidden()
3786 {
3787         return top_level->hidden;
3788 }
3789
3790 int BC_WindowBase::cursor_inside()
3791 {
3792         return (top_level->cursor_x >= 0 &&
3793                         top_level->cursor_y >= 0 &&
3794                         top_level->cursor_x < w &&
3795                         top_level->cursor_y < h);
3796 }
3797
3798 BC_WindowBase* BC_WindowBase::get_top_level()
3799 {
3800         return top_level;
3801 }
3802
3803 BC_WindowBase* BC_WindowBase::get_parent()
3804 {
3805         return parent_window;
3806 }
3807
3808 int BC_WindowBase::get_color_model()
3809 {
3810         return top_level->color_model;
3811 }
3812
3813 BC_Resources* BC_WindowBase::get_resources()
3814 {
3815         return BC_WindowBase::resources;
3816 }
3817
3818 BC_Synchronous* BC_WindowBase::get_synchronous()
3819 {
3820         return BC_WindowBase::resources->get_synchronous();
3821 }
3822
3823 int BC_WindowBase::get_bg_color()
3824 {
3825         return bg_color;
3826 }
3827
3828 void BC_WindowBase::set_bg_color(int color)
3829 {
3830         this->bg_color = color;
3831 }
3832
3833 BC_Pixmap* BC_WindowBase::get_bg_pixmap()
3834 {
3835         return bg_pixmap;
3836 }
3837
3838 void BC_WindowBase::set_active_subwindow(BC_WindowBase *subwindow)
3839 {
3840         top_level->active_subwindow = subwindow;
3841 }
3842
3843 int BC_WindowBase::activate()
3844 {
3845         return 0;
3846 }
3847
3848 int BC_WindowBase::deactivate()
3849 {
3850         if(window_type == MAIN_WINDOW)
3851         {
3852                 if( top_level->active_menubar ) {
3853                         top_level->active_menubar->deactivate();
3854                         top_level->active_menubar = 0;
3855                 }
3856                 if( top_level->active_popup_menu ) {
3857                         top_level->active_popup_menu->deactivate();
3858                         top_level->active_popup_menu = 0;
3859                 }
3860                 if( top_level->active_subwindow ) {
3861                         top_level->active_subwindow->deactivate();
3862                         top_level->active_subwindow = 0;
3863                 }
3864                 if( top_level->motion_events && top_level->last_motion_win == this->win )
3865                         top_level->motion_events = 0;
3866
3867         }
3868         return 0;
3869 }
3870
3871 int BC_WindowBase::cycle_textboxes(int amount)
3872 {
3873         int result = 0;
3874         BC_WindowBase *new_textbox = 0;
3875
3876         if(amount > 0)
3877         {
3878                 BC_WindowBase *first_textbox = 0;
3879                 find_next_textbox(&first_textbox, &new_textbox, result);
3880                 if(!new_textbox) new_textbox = first_textbox;
3881
3882         }
3883         else
3884         if(amount < 0)
3885         {
3886                 BC_WindowBase *last_textbox = 0;
3887                 find_prev_textbox(&last_textbox, &new_textbox, result);
3888                 if(!new_textbox) new_textbox = last_textbox;
3889
3890         }
3891
3892         if(new_textbox != active_subwindow)
3893         {
3894                 deactivate();
3895                 new_textbox->activate();
3896         }
3897
3898         return 0;
3899 }
3900
3901 int BC_WindowBase::find_next_textbox(BC_WindowBase **first_textbox, BC_WindowBase **next_textbox, int &result)
3902 {
3903 // Search subwindows for textbox
3904         for(int i = 0; i < subwindows->total && result < 2; i++)
3905         {
3906                 BC_WindowBase *test_subwindow = subwindows->values[i];
3907                 test_subwindow->find_next_textbox(first_textbox, next_textbox, result);
3908         }
3909
3910         if(result < 2)
3911         {
3912                 if(uses_text())
3913                 {
3914                         if(!*first_textbox) *first_textbox = this;
3915
3916                         if(result < 1)
3917                         {
3918                                 if(top_level->active_subwindow == this)
3919                                         result++;
3920                         }
3921                         else
3922                         {
3923                                 result++;
3924                                 *next_textbox = this;
3925                         }
3926                 }
3927         }
3928         return 0;
3929 }
3930
3931 int BC_WindowBase::find_prev_textbox(BC_WindowBase **last_textbox, BC_WindowBase **prev_textbox, int &result)
3932 {
3933         if(result < 2)
3934         {
3935                 if(uses_text())
3936                 {
3937                         if(!*last_textbox) *last_textbox = this;
3938
3939                         if(result < 1)
3940                         {
3941                                 if(top_level->active_subwindow == this)
3942                                         result++;
3943                         }
3944                         else
3945                         {
3946                                 result++;
3947                                 *prev_textbox = this;
3948                         }
3949                 }
3950         }
3951
3952 // Search subwindows for textbox
3953         for(int i = subwindows->total - 1; i >= 0 && result < 2; i--)
3954         {
3955                 BC_WindowBase *test_subwindow = subwindows->values[i];
3956                 test_subwindow->find_prev_textbox(last_textbox, prev_textbox, result);
3957         }
3958         return 0;
3959 }
3960
3961 BC_Clipboard* BC_WindowBase::get_clipboard()
3962 {
3963 #ifdef SINGLE_THREAD
3964         return BC_Display::display_global->clipboard;
3965 #else
3966         return top_level->clipboard;
3967 #endif
3968 }
3969
3970 Atom BC_WindowBase::to_clipboard(const char *data, long len, int clipboard_num)
3971 {
3972         return get_clipboard()->to_clipboard(this, data, len, clipboard_num);
3973 }
3974
3975 long BC_WindowBase::from_clipboard(char *data, long maxlen, int clipboard_num)
3976 {
3977         return get_clipboard()->from_clipboard(data, maxlen, clipboard_num);
3978 }
3979
3980 long BC_WindowBase::clipboard_len(int clipboard_num)
3981 {
3982         return get_clipboard()->clipboard_len(clipboard_num);
3983 }
3984
3985 int BC_WindowBase::do_selection_clear(Window win)
3986 {
3987         top_level->event_win = win;
3988         return dispatch_selection_clear();
3989 }
3990
3991 int BC_WindowBase::dispatch_selection_clear()
3992 {
3993         int result = 0;
3994         for( int i=0; i<subwindows->total && !result; ++i )
3995                 result = subwindows->values[i]->dispatch_selection_clear();
3996         if( !result )
3997                 result = selection_clear_event();
3998         return result;
3999 }
4000
4001
4002 void BC_WindowBase::get_relative_cursor(int &x, int &y, int lock_window)
4003 {
4004         int abs_x, abs_y, win_x, win_y;
4005         unsigned int temp_mask;
4006         Window temp_win;
4007
4008         if(lock_window) this->lock_window("BC_WindowBase::get_relative_cursor");
4009         XQueryPointer(top_level->display, top_level->win,
4010            &temp_win, &temp_win, &abs_x, &abs_y, &win_x, &win_y,
4011            &temp_mask);
4012
4013         XTranslateCoordinates(top_level->display, top_level->rootwin,
4014            win, abs_x, abs_y, &x, &y, &temp_win);
4015         if(lock_window) this->unlock_window();
4016 }
4017 int BC_WindowBase::get_relative_cursor_x(int lock_window)
4018 {
4019         int x, y;
4020         get_relative_cursor(x, y, lock_window);
4021         return x;
4022 }
4023 int BC_WindowBase::get_relative_cursor_y(int lock_window)
4024 {
4025         int x, y;
4026         get_relative_cursor(x, y, lock_window);
4027         return y;
4028 }
4029
4030 void BC_WindowBase::get_abs_cursor(int &abs_x, int &abs_y, int lock_window)
4031 {
4032         int win_x, win_y;
4033         unsigned int temp_mask;
4034         Window temp_win;
4035
4036         if(lock_window) this->lock_window("BC_WindowBase::get_abs_cursor");
4037         XQueryPointer(top_level->display, top_level->win,
4038                 &temp_win, &temp_win, &abs_x, &abs_y, &win_x, &win_y,
4039                 &temp_mask);
4040         if(lock_window) this->unlock_window();
4041 }
4042 int BC_WindowBase::get_abs_cursor_x(int lock_window)
4043 {
4044         int abs_x, abs_y;
4045         get_abs_cursor(abs_x, abs_y, lock_window);
4046         return abs_x;
4047 }
4048 int BC_WindowBase::get_abs_cursor_y(int lock_window)
4049 {
4050         int abs_x, abs_y;
4051         get_abs_cursor(abs_x, abs_y, lock_window);
4052         return abs_y;
4053 }
4054
4055 void BC_WindowBase::get_pop_cursor(int &px, int &py, int lock_window)
4056 {
4057         int xmargin = xS(100), ymargin = yS(100);
4058         get_abs_cursor(px, py, lock_window);
4059         if( px < xmargin ) px = xmargin;
4060         if( py < ymargin ) py = ymargin;
4061         int wd = get_screen_x(lock_window,-1) + get_screen_w(lock_window,-1) - xmargin;
4062         if( px > wd ) px = wd;
4063         int ht = get_screen_y(lock_window,-1) + get_screen_h(lock_window,-1) - ymargin;
4064         if( py > ht ) py = ht;
4065 }
4066 int BC_WindowBase::get_pop_cursor_x(int lock_window)
4067 {
4068         int px, py;
4069         get_pop_cursor(px, py, lock_window);
4070         return px;
4071 }
4072 int BC_WindowBase::get_pop_cursor_y(int lock_window)
4073 {
4074         int px, py;
4075         get_pop_cursor(px, py, lock_window);
4076         return py;
4077 }
4078
4079 int BC_WindowBase::match_window(Window win)
4080 {
4081         if (this->win == win) return 1;
4082         int result = 0;
4083         for(int i = 0; i < subwindows->total; i++) {
4084                 result = subwindows->values[i]->match_window(win);
4085                 if (result) return result;
4086         }
4087         return 0;
4088
4089 }
4090
4091 int BC_WindowBase::get_cursor_over_window()
4092 {
4093         int abs_x, abs_y, win_x, win_y;
4094         unsigned int mask_return;
4095         Window root_return, child_return;
4096
4097         int ret = XQueryPointer(top_level->display, top_level->rootwin,
4098                 &root_return, &child_return, &abs_x, &abs_y,
4099                 &win_x, &win_y, &mask_return);
4100         if( ret && child_return == None ) ret = 0;
4101         if( ret && win != child_return )
4102                 ret = top_level->match_window(child_return);
4103 // query pointer can return a window manager window with this top_level as a child
4104 //  for kde this can be two levels deep
4105         unsigned int nchildren_return = 0;
4106         Window parent_return, *children_return = 0;
4107         Window top_win = top_level->win;
4108         while( !ret && top_win != top_level->rootwin && top_win != root_return &&
4109                 XQueryTree(top_level->display, top_win, &root_return,
4110                         &parent_return, &children_return, &nchildren_return) ) {
4111                 if( children_return ) XFree(children_return);
4112                 if( (top_win=parent_return) == child_return ) ret = 1;
4113         }
4114         return ret;
4115 }
4116
4117 int BC_WindowBase::cursor_above()
4118 {
4119         int rx, ry;
4120         get_relative_cursor(rx, ry);
4121         return rx < 0 || rx >= get_w() ||
4122                 ry < 0 || ry >= get_h() ? 0 : 1;
4123 }
4124
4125 int BC_WindowBase::get_drag_x()
4126 {
4127         return top_level->drag_x;
4128 }
4129
4130 int BC_WindowBase::get_drag_y()
4131 {
4132         return top_level->drag_y;
4133 }
4134
4135 int BC_WindowBase::get_cursor_x()
4136 {
4137         return top_level->cursor_x;
4138 }
4139
4140 int BC_WindowBase::get_cursor_y()
4141 {
4142         return top_level->cursor_y;
4143 }
4144
4145 int BC_WindowBase::dump_windows()
4146 {
4147         printf("\tBC_WindowBase::dump_windows window=%p win=%p '%s', %dx%d+%d+%d %s\n",
4148                 this, (void*)this->win, title, w,h,x,y, typeid(*this).name());
4149         for(int i = 0; i < subwindows->size(); i++)
4150                 subwindows->get(i)->dump_windows();
4151         for(int i = 0; i < popups.size(); i++) {
4152                 BC_WindowBase *p = popups[i];
4153                 printf("\tBC_WindowBase::dump_windows popup=%p win=%p '%s', %dx%d+%d+%d %s\n",
4154                         p, (void*)p->win, p->title, p->w,p->h,p->x,p->y, typeid(*p).name());
4155         }
4156         return 0;
4157 }
4158
4159 int BC_WindowBase::is_event_win()
4160 {
4161         return this->win == top_level->event_win;
4162 }
4163
4164 int BC_WindowBase::is_tooltip_event_win()
4165 {
4166         return this->win == top_level->event_win ||
4167                 tooltip_popup && tooltip_popup->win == top_level->event_win;
4168 }
4169
4170 void BC_WindowBase::set_dragging(int value)
4171 {
4172         is_dragging = value;
4173 }
4174
4175 int BC_WindowBase::get_dragging()
4176 {
4177         return is_dragging;
4178 }
4179
4180 int BC_WindowBase::get_buttonpress()
4181 {
4182         return top_level->button_number;
4183 }
4184
4185 int BC_WindowBase::get_button_down()
4186 {
4187         return top_level->button_down;
4188 }
4189
4190 int BC_WindowBase::alt_down()
4191 {
4192         return top_level->alt_mask;
4193 }
4194
4195 int BC_WindowBase::shift_down()
4196 {
4197         return top_level->shift_mask;
4198 }
4199
4200 int BC_WindowBase::ctrl_down()
4201 {
4202         return top_level->ctrl_mask;
4203 }
4204
4205 wchr_t* BC_WindowBase::get_wkeystring(int *length)
4206 {
4207         if(length)
4208                 *length = top_level->wkey_string_length;
4209         return top_level->wkey_string;
4210 }
4211
4212 #ifdef X_HAVE_UTF8_STRING
4213 char* BC_WindowBase::get_keypress_utf8()
4214 {
4215         return top_level->key_pressed_utf8;
4216 }
4217 #endif
4218
4219
4220 int BC_WindowBase::get_keypress()
4221 {
4222         return top_level->key_pressed;
4223 }
4224
4225 int BC_WindowBase::get_double_click()
4226 {
4227         return top_level->double_click;
4228 }
4229
4230 int BC_WindowBase::get_triple_click()
4231 {
4232         return top_level->triple_click;
4233 }
4234
4235 int BC_WindowBase::get_bgcolor()
4236 {
4237         return bg_color;
4238 }
4239
4240 int BC_WindowBase::resize_window(int w, int h)
4241 {
4242         if(this->w == w && this->h == h) return 0;
4243
4244         if(window_type == MAIN_WINDOW && !allow_resize)
4245         {
4246                 XSizeHints size_hints;
4247                 size_hints.flags = PSize | PMinSize | PMaxSize;
4248                 size_hints.width = w;
4249                 size_hints.height = h;
4250                 size_hints.min_width = w;
4251                 size_hints.max_width = w;
4252                 size_hints.min_height = h;
4253                 size_hints.max_height = h;
4254                 if( this->x > -BC_INFINITY && this->x < BC_INFINITY ) {
4255                         size_hints.flags |= PPosition;
4256                         size_hints.x = this->x;
4257                         size_hints.y = this->y;
4258                 }
4259                 XSetNormalHints(top_level->display, win, &size_hints);
4260         }
4261         XResizeWindow(top_level->display, win, w, h);
4262
4263         this->w = w;
4264         this->h = h;
4265         delete pixmap;
4266         pixmap = new BC_Pixmap(this, w, h);
4267
4268 // Propagate to menubar
4269         for(int i = 0; i < subwindows->total; i++)
4270         {
4271                 subwindows->values[i]->dispatch_resize_event(w, h);
4272         }
4273
4274         draw_background(0, 0, w, h);
4275         if(top_level == this && get_resources()->recursive_resizing)
4276                 resize_history.append(new BC_ResizeCall(w, h));
4277         return 0;
4278 }
4279
4280 // The only way for resize events to be propagated is by updating the internal w and h
4281 int BC_WindowBase::resize_event(int w, int h)
4282 {
4283         if(window_type == MAIN_WINDOW)
4284         {
4285                 this->w = w;
4286                 this->h = h;
4287         }
4288         return 0;
4289 }
4290
4291 int BC_WindowBase::reposition_window(int x, int y)
4292 {
4293         reposition_window(x, y, -1, -1);
4294         return 0;
4295 }
4296
4297
4298 int BC_WindowBase::reposition_window(int x, int y, int w, int h)
4299 {
4300         int resize = 0;
4301
4302 // Some tools set their own dimensions before calling this, causing the
4303 // resize check to skip.
4304         this->x = x;
4305         this->y = y;
4306
4307         if(w > 0 && w != this->w)
4308         {
4309                 resize = 1;
4310                 this->w = w;
4311         }
4312
4313         if(h > 0 && h != this->h)
4314         {
4315                 resize = 1;
4316                 this->h = h;
4317         }
4318
4319 //printf("BC_WindowBase::reposition_window %d %d %d\n", translation_count, x_correction, y_correction);
4320
4321         if(this->w <= 0)
4322                 printf("BC_WindowBase::reposition_window this->w == %d\n", this->w);
4323         if(this->h <= 0)
4324                 printf("BC_WindowBase::reposition_window this->h == %d\n", this->h);
4325
4326         if(translation_count && window_type == MAIN_WINDOW)
4327         {
4328 // KDE shifts window right and down.
4329 // FVWM leaves window alone and adds border around it.
4330                 XMoveResizeWindow(top_level->display, win,
4331                         x - BC_DisplayInfo::auto_reposition_x,
4332                         y - BC_DisplayInfo::auto_reposition_y,
4333                         this->w, this->h);
4334         }
4335         else
4336         {
4337                 XMoveResizeWindow(top_level->display, win, x, y,
4338                         this->w, this->h);
4339         }
4340
4341         if(resize)
4342         {
4343                 delete pixmap;
4344                 pixmap = new BC_Pixmap(this, this->w, this->h);
4345                 clear_box(0,0, this->w, this->h);
4346 // Propagate to menubar
4347                 for(int i = 0; i < subwindows->total; i++)
4348                 {
4349                         subwindows->values[i]->dispatch_resize_event(this->w, this->h);
4350                 }
4351
4352 //              draw_background(0, 0, w, h);
4353         }
4354
4355         return 0;
4356 }
4357
4358 int BC_WindowBase::reposition_window_relative(int dx, int dy, int w, int h)
4359 {
4360         return reposition_window(get_x()+dx, get_y()+dy, w, h);
4361 }
4362
4363 int BC_WindowBase::reposition_window_relative(int dx, int dy)
4364 {
4365         return reposition_window_relative(dx, dy, -1, -1);
4366 }
4367
4368 void BC_WindowBase::set_tooltips(int v)
4369 {
4370         get_resources()->tooltips_enabled = v;
4371 }
4372
4373 void BC_WindowBase::set_force_tooltip(int v)
4374 {
4375         force_tooltip = v;
4376 }
4377
4378 int BC_WindowBase::raise_window(int do_flush)
4379 {
4380         if( hidden ) return 1;
4381         if( wait_viewable(500) ) return 1;
4382         XRaiseWindow(top_level->display, win);
4383         if(do_flush) XFlush(top_level->display);
4384         return 0;
4385 }
4386
4387 int BC_WindowBase::lower_window(int do_flush)
4388 {
4389         XLowerWindow(top_level->display, win);
4390         if(do_flush) XFlush(top_level->display);
4391         return 0;
4392 }
4393
4394 void BC_WindowBase::set_background(VFrame *bitmap)
4395 {
4396         if(bg_pixmap && !shared_bg_pixmap) delete bg_pixmap;
4397
4398         bg_pixmap = new BC_Pixmap(this, bitmap, PIXMAP_OPAQUE);
4399         shared_bg_pixmap = 0;
4400         draw_background(0, 0, w, h);
4401 }
4402
4403 void BC_WindowBase::put_title(const char *text)
4404 {
4405         char *cp = this->title, *ep = cp+sizeof(this->title)-1;
4406         for( const unsigned char *bp = (const unsigned char *)text; *bp && cp<ep; ++bp )
4407                 *cp++ = *bp >= ' ' ? *bp : ' ';
4408         *cp = 0;
4409 }
4410
4411 void BC_WindowBase::set_title(const char *text, int utf8)
4412 {
4413 // utf8>0: wm + net_wm, utf8=0: wm only,  utf<0: net_wm only
4414         put_title(text);
4415         const unsigned char *wm_title = (const unsigned char *)title;
4416         int title_len = strlen((const char *)title);
4417         if( utf8 >= 0 ) {
4418                 Atom xa_wm_name = XA_WM_NAME;
4419                 Atom xa_icon_name = XA_WM_ICON_NAME;
4420                 Atom xa_string = XA_STRING;
4421                 XChangeProperty(display, win, xa_wm_name, xa_string, 8,
4422                                 PropModeReplace, wm_title, title_len);
4423                 XChangeProperty(display, win, xa_icon_name, xa_string, 8,
4424                                 PropModeReplace, wm_title, title_len);
4425         }
4426         if( utf8 != 0 ) {
4427                 Atom xa_net_wm_name = XInternAtom(display, "_NET_WM_NAME", True);
4428                 Atom xa_net_icon_name = XInternAtom(display, "_NET_WM_ICON_NAME", True);
4429                 Atom xa_utf8_string = XInternAtom(display, "UTF8_STRING", True);
4430                 XChangeProperty(display, win, xa_net_wm_name, xa_utf8_string, 8,
4431                                         PropModeReplace, wm_title, title_len);
4432                 XChangeProperty(display, win, xa_net_icon_name, xa_utf8_string, 8,
4433                                         PropModeReplace, wm_title, title_len);
4434         }
4435         flush();
4436 }
4437
4438 // must be RGBA8888
4439 void BC_WindowBase::set_net_icon(VFrame *data)
4440 {
4441         int width = data->get_w(), height = data->get_h();
4442         int size = 2 + width * height;
4443         unsigned long *icon_data = new unsigned long[size];
4444         unsigned long *lp = icon_data;
4445         *lp++ = width;  *lp++ = height;
4446         uint8_t **rows = data->get_rows();
4447         for( int y=0; y<height; ++y ) {
4448                 unsigned *up = (unsigned *)rows[y];
4449                 for( int x=0; x<width; ++x )
4450                         *lp++ = *(unsigned *)up++;
4451         }
4452         Atom NetWMIcon = XInternAtom(display, "_NET_WM_ICON", True);
4453         XChangeProperty(top_level->display, top_level->win, NetWMIcon,
4454                 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)icon_data, size);
4455         delete [] icon_data;
4456 }
4457
4458 const char *BC_WindowBase::get_title()
4459 {
4460         return title;
4461 }
4462
4463 int BC_WindowBase::get_toggle_value()
4464 {
4465         return toggle_value;
4466 }
4467
4468 int BC_WindowBase::get_toggle_drag()
4469 {
4470         return toggle_drag;
4471 }
4472
4473 int BC_WindowBase::set_icon(VFrame *data)
4474 {
4475         if(icon_pixmap) delete icon_pixmap;
4476         icon_pixmap = new BC_Pixmap(top_level, data, PIXMAP_ALPHA, 1);
4477
4478         if(icon_window) delete icon_window;
4479         icon_window = new BC_Popup(this, 0, 0,
4480                 icon_pixmap->get_w(), icon_pixmap->get_h(),
4481                 -1, 1, // All windows are hidden initially
4482                 icon_pixmap);
4483
4484         XWMHints wm_hints;  memset(&wm_hints, 0, sizeof(wm_hints));
4485         wm_hints.flags = IconPixmapHint; // | IconMaskHint | IconWindowHint;
4486         wm_hints.icon_pixmap = icon_pixmap->get_pixmap();
4487         wm_hints.icon_mask = icon_pixmap->get_alpha();
4488         wm_hints.icon_window = icon_window->win;
4489         if( XGroupLeader ) {
4490                 wm_hints.flags |= WindowGroupHint;
4491                 wm_hints.window_group = XGroupLeader;
4492         }
4493
4494 // for(int i = 0; i < 1000; i++)
4495 // printf("02x ", icon_pixmap->get_alpha()->get_row_pointers()[0][i]);
4496 // printf("\n");
4497
4498         XSetWMHints(top_level->display, top_level->win, &wm_hints);
4499
4500         set_net_icon(data);
4501         XSync(top_level->display, 0);
4502         return 0;
4503 }
4504
4505 void BC_WindowBase::init_resources(float scale)
4506 {
4507         if( resources ) return;
4508         XInitThreads();
4509         const char *env = getenv("BC_SCALE");
4510         if( env ) scale = atof(env);
4511         float x_scale = 1, y_scale = 1;
4512         if( scale <= 0 ) {
4513                 BC_DisplayInfo info;
4514                 int wx, wy, ww, wh;
4515                 int cins = info.xinerama_big_screen();
4516                 if( !info.xinerama_geometry(cins, wx, wy, ww, wh) ) {
4517                         int sh = ww * 9 / 16;
4518                         int sw = wh * 16 / 9;
4519                         if( sw < ww ) ww = sw;
4520                         if( sh < wh ) wh = sh;
4521                         if( (x_scale = ww/1920.) < 1 ) x_scale = 1;
4522                         if( (y_scale = wh/1080.) < 1 ) y_scale = 1;
4523                 }
4524         }
4525         else
4526                 x_scale = y_scale = scale;
4527         // constructor sets BC_WindowBase::resources
4528         new BC_Resources(x_scale, y_scale);
4529 }
4530 void BC_WindowBase::finit_resources()
4531 {
4532         delete resources;  resources = 0;
4533 }
4534
4535 int BC_WindowBase::set_w(int w)
4536 {
4537         this->w = w;
4538         return 0;
4539 }
4540
4541 int BC_WindowBase::set_h(int h)
4542 {
4543         this->h = h;
4544         return 0;
4545 }
4546
4547 int BC_WindowBase::load_defaults(BC_Hash *defaults)
4548 {
4549         char string[BCTEXTLEN];
4550         int newest_id = - 1;
4551         for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++)
4552         {
4553                 sprintf(string, "FILEBOX_HISTORY_PATH%d", i);
4554                 resources->filebox_history[i].path[0] = 0;
4555                 defaults->get(string, resources->filebox_history[i].path);
4556                 sprintf(string, "FILEBOX_HISTORY_ID%d", i);
4557                 resources->filebox_history[i].id = defaults->get(string, resources->get_id());
4558                 if(resources->filebox_history[i].id > newest_id)
4559                         newest_id = resources->filebox_history[i].id;
4560         }
4561
4562         resources->filebox_id = newest_id + 1;
4563         resources->filebox_mode = defaults->get("FILEBOX_MODE", get_resources()->filebox_mode);
4564         resources->filebox_w = defaults->get("FILEBOX_W", get_resources()->filebox_w);
4565         resources->filebox_h = defaults->get("FILEBOX_H", get_resources()->filebox_h);
4566         resources->filebox_columntype[0] = defaults->get("FILEBOX_TYPE0", resources->filebox_columntype[0]);
4567         resources->filebox_columntype[1] = defaults->get("FILEBOX_TYPE1", resources->filebox_columntype[1]);
4568         resources->filebox_columntype[2] = defaults->get("FILEBOX_TYPE2", resources->filebox_columntype[2]);
4569         resources->filebox_columntype[3] = defaults->get("FILEBOX_TYPE3", resources->filebox_columntype[3]);
4570         resources->filebox_columnwidth[0] = defaults->get("FILEBOX_WIDTH0", resources->filebox_columnwidth[0]);
4571         resources->filebox_columnwidth[1] = defaults->get("FILEBOX_WIDTH1", resources->filebox_columnwidth[1]);
4572         resources->filebox_columnwidth[2] = defaults->get("FILEBOX_WIDTH2", resources->filebox_columnwidth[2]);
4573         resources->filebox_columnwidth[3] = defaults->get("FILEBOX_WIDTH3", resources->filebox_columnwidth[3]);
4574         resources->filebox_size_format = defaults->get("FILEBOX_SIZE_FORMAT", get_resources()->filebox_size_format);
4575         defaults->get("FILEBOX_FILTER", resources->filebox_filter);
4576         return 0;
4577 }
4578
4579 int BC_WindowBase::save_defaults(BC_Hash *defaults)
4580 {
4581         char string[BCTEXTLEN];
4582         for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++)
4583         {
4584                 sprintf(string, "FILEBOX_HISTORY_PATH%d", i);
4585                 defaults->update(string, resources->filebox_history[i].path);
4586                 sprintf(string, "FILEBOX_HISTORY_ID%d", i);
4587                 defaults->update(string, resources->filebox_history[i].id);
4588         }
4589         defaults->update("FILEBOX_MODE", resources->filebox_mode);
4590         defaults->update("FILEBOX_W", resources->filebox_w);
4591         defaults->update("FILEBOX_H", resources->filebox_h);
4592         defaults->update("FILEBOX_TYPE0", resources->filebox_columntype[0]);
4593         defaults->update("FILEBOX_TYPE1", resources->filebox_columntype[1]);
4594         defaults->update("FILEBOX_TYPE2", resources->filebox_columntype[2]);
4595         defaults->update("FILEBOX_TYPE3", resources->filebox_columntype[3]);
4596         defaults->update("FILEBOX_WIDTH0", resources->filebox_columnwidth[0]);
4597         defaults->update("FILEBOX_WIDTH1", resources->filebox_columnwidth[1]);
4598         defaults->update("FILEBOX_WIDTH2", resources->filebox_columnwidth[2]);
4599         defaults->update("FILEBOX_WIDTH3", resources->filebox_columnwidth[3]);
4600         defaults->update("FILEBOX_FILTER", resources->filebox_filter);
4601         defaults->update("FILEBOX_SIZE_FORMAT", get_resources()->filebox_size_format);
4602         return 0;
4603 }
4604
4605
4606
4607 // For some reason XTranslateCoordinates can take a long time to return.
4608 // We work around this by only calling it when the event windows are different.
4609 void BC_WindowBase::translate_coordinates(Window src_w, Window dest_w,
4610                 int src_x, int src_y, int *dest_x_return, int *dest_y_return)
4611 {
4612         Window tempwin = 0;
4613 //Timer timer;
4614 //timer.update();
4615         if(src_w == dest_w)
4616         {
4617                 *dest_x_return = src_x;
4618                 *dest_y_return = src_y;
4619         }
4620         else
4621         {
4622                 XTranslateCoordinates(top_level->display, src_w, dest_w,
4623                         src_x, src_y, dest_x_return, dest_y_return, &tempwin);
4624 //printf("BC_WindowBase::translate_coordinates 1 %lld\n", timer.get_difference());
4625         }
4626 }
4627
4628 void BC_WindowBase::get_root_coordinates(int x, int y, int *abs_x, int *abs_y)
4629 {
4630         translate_coordinates(win, top_level->rootwin, x, y, abs_x, abs_y);
4631 }
4632
4633 void BC_WindowBase::get_win_coordinates(int abs_x, int abs_y, int *x, int *y)
4634 {
4635         translate_coordinates(top_level->rootwin, win, abs_x, abs_y, x, y);
4636 }
4637
4638
4639 #ifdef HAVE_LIBXXF86VM
4640 void BC_WindowBase::closest_vm(int *vm, int *width, int *height)
4641 {
4642         int foo,bar;
4643         *vm = 0;
4644         if(XF86VidModeQueryExtension(top_level->display,&foo,&bar)) {
4645                 int vm_count,i;
4646                 XF86VidModeModeInfo **vm_modelines;
4647                 XF86VidModeGetAllModeLines(top_level->display,
4648                         XDefaultScreen(top_level->display), &vm_count,&vm_modelines);
4649                 for( i = 0; i < vm_count; i++ ) {
4650                         if( vm_modelines[i]->hdisplay < vm_modelines[*vm]->hdisplay &&
4651                             vm_modelines[i]->hdisplay >= *width )
4652                                 *vm = i;
4653                 }
4654                 display = top_level->display;
4655                 if( vm_modelines[*vm]->hdisplay == *width )
4656                         *vm = -1;
4657                 else {
4658                         *width = vm_modelines[*vm]->hdisplay;
4659                         *height = vm_modelines[*vm]->vdisplay;
4660                 }
4661         }
4662 }
4663
4664 void BC_WindowBase::scale_vm(int vm)
4665 {
4666         int foo,bar,dotclock;
4667         if( XF86VidModeQueryExtension(top_level->display,&foo,&bar) ) {
4668                 int vm_count;
4669                 XF86VidModeModeInfo **vm_modelines;
4670                 XF86VidModeModeLine vml;
4671                 XF86VidModeGetAllModeLines(top_level->display,
4672                         XDefaultScreen(top_level->display), &vm_count,&vm_modelines);
4673                 XF86VidModeGetModeLine(top_level->display,
4674                         XDefaultScreen(top_level->display), &dotclock,&vml);
4675                 orig_modeline.dotclock = dotclock;
4676                 orig_modeline.hdisplay = vml.hdisplay;
4677                 orig_modeline.hsyncstart = vml.hsyncstart;
4678                 orig_modeline.hsyncend = vml.hsyncend;
4679                 orig_modeline.htotal = vml.htotal;
4680                 orig_modeline.vdisplay = vml.vdisplay;
4681                 orig_modeline.vsyncstart = vml.vsyncstart;
4682                 orig_modeline.vsyncend = vml.vsyncend;
4683                 orig_modeline.vtotal = vml.vtotal;
4684                 orig_modeline.flags = vml.flags;
4685                 orig_modeline.privsize = vml.privsize;
4686                 // orig_modeline.private = vml.private;
4687                 XF86VidModeSwitchToMode(top_level->display,XDefaultScreen(top_level->display),vm_modelines[vm]);
4688                 XF86VidModeSetViewPort(top_level->display,XDefaultScreen(top_level->display),0,0);
4689                 XFlush(top_level->display);
4690         }
4691 }
4692
4693 void BC_WindowBase::restore_vm()
4694 {
4695         XF86VidModeSwitchToMode(top_level->display,XDefaultScreen(top_level->display),&orig_modeline);
4696         XFlush(top_level->display);
4697 }
4698 #endif
4699
4700
4701 #ifndef SINGLE_THREAD
4702 int BC_WindowBase::get_event_count()
4703 {
4704         event_lock->lock("BC_WindowBase::get_event_count");
4705         int result = common_events.total;
4706         event_lock->unlock();
4707         return result;
4708 }
4709
4710 XEvent* BC_WindowBase::get_event()
4711 {
4712         XEvent *result = 0;
4713         while(!done && !result)
4714         {
4715                 event_condition->lock("BC_WindowBase::get_event");
4716                 event_lock->lock("BC_WindowBase::get_event");
4717
4718                 if(common_events.total && !done)
4719                 {
4720                         result = common_events.values[0];
4721                         common_events.remove_number(0);
4722                 }
4723
4724                 event_lock->unlock();
4725         }
4726         return result;
4727 }
4728
4729 void BC_WindowBase::put_event(XEvent *event)
4730 {
4731         event_lock->lock("BC_WindowBase::put_event");
4732         common_events.append(event);
4733         event_lock->unlock();
4734         event_condition->unlock();
4735 }
4736
4737 void BC_WindowBase::dequeue_events(Window win)
4738 {
4739         event_lock->lock("BC_WindowBase::dequeue_events");
4740
4741         int out = 0, total = common_events.size();
4742         for( int in=0; in<total; ++in ) {
4743                 if( common_events[in]->xany.window == win ) continue;
4744                 common_events[out++] = common_events[in];
4745         }
4746         common_events.total = out;
4747
4748         event_lock->unlock();
4749 }
4750
4751 int BC_WindowBase::resend_event(BC_WindowBase *window)
4752 {
4753         if( resend_event_window ) return 1;
4754         resend_event_window = window;
4755         return 0;
4756 }
4757
4758 #else
4759
4760 int BC_WindowBase::resend_event(BC_WindowBase *window)
4761 {
4762         return 1;
4763 }
4764
4765 #endif // SINGLE_THREAD
4766
4767 int BC_WindowBase::get_id()
4768 {
4769         return id;
4770 }
4771
4772
4773 BC_Pixmap *BC_WindowBase::create_pixmap(VFrame *vframe)
4774 {
4775         int w = vframe->get_w(), h = vframe->get_h();
4776         BC_Pixmap *icon = new BC_Pixmap(this, w, h);
4777         icon->draw_vframe(vframe, 0,0, w,h, 0,0);
4778         return icon;
4779 }
4780
4781
4782 void BC_WindowBase::flicker(int n, int ms)
4783 {
4784         int color = get_bg_color();
4785         for( int i=2*n; --i>=0; ) {
4786                 set_inverse();          set_bg_color(WHITE);
4787                 clear_box(0,0, w,h);    flash(1);
4788                 sync_display();         Timer::delay(ms);
4789         }
4790         set_bg_color(color);
4791         set_opaque();
4792 }
4793
4794 void BC_WindowBase::focus()
4795 {
4796         XWindowAttributes xwa;
4797         XGetWindowAttributes(top_level->display, top_level->win, &xwa);
4798         if( xwa.map_state == IsViewable )
4799                 XSetInputFocus(top_level->display, top_level->win, RevertToParent, CurrentTime);
4800 }
4801
4802 int BC_WindowBase::wait_viewable(int ms)
4803 {
4804         Timer timer;
4805         XWindowAttributes xwa;
4806         do {
4807                 XGetWindowAttributes(top_level->display, top_level->win, &xwa);
4808                 if( xwa.map_state == IsViewable ) return 0;
4809                 usleep(10000);
4810         } while( timer.get_difference() < ms );
4811         return 1;
4812 }
4813