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