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