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