a2cccd2cf4da8abac6f37276c0af1ce4516aeabf
[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         if( window->active_grab && this != window->active_grab ) return 0;
3419         window->active_grab = this;
3420         this->grab_active = window;
3421         return 1;
3422 }
3423 int BC_WindowBase::ungrab(BC_WindowBase *window)
3424 {
3425         if( window->active_grab && this != window->active_grab ) return 0;
3426         window->active_grab = 0;
3427         this->grab_active = 0;
3428         return 1;
3429 }
3430 int BC_WindowBase::grab_event_count()
3431 {
3432         int result = 0;
3433 #ifndef SINGLE_THREAD
3434         result = grab_active->get_event_count();
3435 #endif
3436         return result;
3437 }
3438 int BC_WindowBase::grab_buttons()
3439 {
3440         XSync(top_level->display, False);
3441         if( XGrabButton(top_level->display, AnyButton, AnyModifier,
3442                         top_level->rootwin, True, ButtonPressMask | ButtonReleaseMask,
3443                         GrabModeAsync, GrabModeSync, None, None) == GrabSuccess ) {
3444                 set_active_subwindow(this);
3445                 return 0;
3446         }
3447         return 1;
3448 }
3449 void BC_WindowBase::ungrab_buttons()
3450 {
3451         XUngrabButton(top_level->display, AnyButton, AnyModifier, top_level->rootwin);
3452         set_active_subwindow(0);
3453         unhide_cursor();
3454 }
3455 void BC_WindowBase::grab_cursor()
3456 {
3457         Cursor cursor_grab = get_cursor_struct(GRABBED_CURSOR);
3458         XGrabPointer(top_level->display, top_level->rootwin, True,
3459                 PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
3460                 GrabModeAsync, GrabModeAsync, None, cursor_grab, CurrentTime);
3461 }
3462 void BC_WindowBase::ungrab_cursor()
3463 {
3464         XUngrabPointer(top_level->display, CurrentTime);
3465 }
3466
3467 // for get_root_w/h
3468 //   WidthOfScreen/HeightOfScreen of XDefaultScreenOfDisplay
3469 //   this is the bounding box of all the screens
3470
3471 int BC_WindowBase::get_root_w(int lock_display)
3472 {
3473         if(lock_display) lock_window("BC_WindowBase::get_root_w");
3474         Screen *def_screen = XDefaultScreenOfDisplay(top_level->display);
3475         int result = WidthOfScreen(def_screen);
3476         if(lock_display) unlock_window();
3477         return result;
3478 }
3479
3480 int BC_WindowBase::get_root_h(int lock_display)
3481 {
3482         if(lock_display) lock_window("BC_WindowBase::get_root_h");
3483         Screen *def_screen = XDefaultScreenOfDisplay(top_level->display);
3484         int result = HeightOfScreen(def_screen);
3485         if(lock_display) unlock_window();
3486         return result;
3487 }
3488
3489 XineramaScreenInfo *
3490 BC_WindowBase::get_xinerama_info(int screen)
3491 {
3492         if( !xinerama_info || !xinerama_screens ) return 0;
3493         if( screen >= 0 ) {
3494                 for( int i=0; i<xinerama_screens; ++i )
3495                         if( xinerama_info[i].screen_number == screen )
3496                                 return &xinerama_info[i];
3497                 return 0;
3498         }
3499         int top_x = get_x(), top_y = get_y();
3500         if(  BC_DisplayInfo::left_border >= 0 ) top_x +=  BC_DisplayInfo::left_border;
3501         if(  BC_DisplayInfo::top_border >= 0 ) top_y +=  BC_DisplayInfo::top_border;
3502         for( int i=0; i<xinerama_screens; ++i ) {
3503                 int scr_y = top_y - xinerama_info[i].y_org;
3504                 if( scr_y < 0 || scr_y >= xinerama_info[i].height ) continue;
3505                 int scr_x = top_x - xinerama_info[i].x_org;
3506                 if( scr_x >= 0 && scr_x < xinerama_info[i].width )
3507                         return &xinerama_info[i];
3508         }
3509         return 0;
3510 }
3511
3512 void BC_WindowBase::get_fullscreen_geometry(int &wx, int &wy, int &ww, int &wh)
3513 {
3514         XineramaScreenInfo *info = top_level->get_xinerama_info(-1);
3515         if( info ) {
3516                 wx = info->x_org;  wy = info->y_org;
3517                 ww = info->width;  wh = info->height;
3518         }
3519         else {
3520                 wx = get_screen_x(0, -1);
3521                 wy = get_screen_y(0, -1);
3522                 int scr_w0 = get_screen_w(0, 0);
3523                 int root_w = get_root_w(0);
3524                 int root_h = get_root_h(0);
3525                 if( root_w > scr_w0 ) { // multi-headed
3526                         if( wx >= scr_w0 ) {
3527                                 // assumes right side is the big one
3528                                 ww = root_w - scr_w0;
3529                                 wh = root_h;
3530                         }
3531                         else {
3532                                 // use same aspect ratio to compute left height
3533                                 ww = scr_w0;
3534                                 wh = (w*root_h) / (root_w-scr_w0);
3535                         }
3536                 }
3537                 else {
3538                         ww = root_w;
3539                         wh = root_h;
3540                 }
3541         }
3542 }
3543
3544 int BC_WindowBase::get_screen_x(int lock_display, int screen)
3545 {
3546         int result = -1;
3547         if(lock_display) lock_window("BC_WindowBase::get_screen_x");
3548         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3549         if( !info ) {
3550                 result = 0;
3551                 int root_w = get_root_w(0);
3552                 int root_h = get_root_h(0);
3553 // Shift X based on position of current window if dual head
3554                 if( (float)root_w/root_h > 1.8 ) {
3555                         root_w = get_screen_w(0, 0);
3556                         if( top_level->get_x() >= root_w )
3557                                 result = root_w;
3558                 }
3559         }
3560         else
3561                 result = info->x_org;
3562         if(lock_display) unlock_window();
3563         return result;
3564 }
3565
3566 int BC_WindowBase::get_screen_y(int lock_display, int screen)
3567 {
3568         if(lock_display) lock_window("BC_WindowBase::get_screen_y");
3569         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3570         int result = !info ? 0 : info->y_org;
3571         if(lock_display) unlock_window();
3572         return result;
3573 }
3574
3575 int BC_WindowBase::get_screen_w(int lock_display, int screen)
3576 {
3577         int result = -1;
3578         if(lock_display) lock_window("BC_WindowBase::get_screen_w");
3579         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3580         if( !info ) {
3581                 int width = get_root_w(0);
3582                 int height = get_root_h(0);
3583                 if( (float)width/height > 1.8 ) {
3584                         // If dual head, the screen width is > 16x9
3585                         // but we only want to fill one screen
3586                         // this code assumes the "big" screen is on the right
3587                         int scr_w0 = width / 2;
3588                         switch( height ) {
3589                         case 600:  scr_w0 = 800;   break;
3590                         case 720:  scr_w0 = 1280;  break;
3591                         case 1024: scr_w0 = 1280;  break;
3592                         case 1200: scr_w0 = 1600;  break;
3593                         case 1080: scr_w0 = 1920;  break;
3594                         }
3595                         int scr_w1 = width - scr_w0;
3596                         result = screen > 0 ? scr_w1 :
3597                                 screen == 0 ? scr_w0 :
3598                                 top_level->get_x() < scr_w0 ? scr_w0 : scr_w1;
3599                 }
3600                 else
3601                         result = width;
3602         }
3603         else
3604                 result = info->width;
3605         if(lock_display) unlock_window();
3606         return result;
3607 }
3608
3609 int BC_WindowBase::get_screen_h(int lock_display, int screen)
3610 {
3611         if(lock_display) lock_window("BC_WindowBase::get_screen_h");
3612         XineramaScreenInfo *info = top_level->get_xinerama_info(screen);
3613         int result = info ? info->height : get_root_h(0);
3614         if(lock_display) unlock_window();
3615         return result;
3616 }
3617
3618 // Bottom right corner
3619 int BC_WindowBase::get_x2()
3620 {
3621         return w + x;
3622 }
3623
3624 int BC_WindowBase::get_y2()
3625 {
3626         return y + h;
3627 }
3628
3629 int BC_WindowBase::get_video_on()
3630 {
3631         return video_on;
3632 }
3633
3634 int BC_WindowBase::get_hidden()
3635 {
3636         return top_level->hidden;
3637 }
3638
3639 int BC_WindowBase::cursor_inside()
3640 {
3641         return (top_level->cursor_x >= 0 &&
3642                         top_level->cursor_y >= 0 &&
3643                         top_level->cursor_x < w &&
3644                         top_level->cursor_y < h);
3645 }
3646
3647 BC_WindowBase* BC_WindowBase::get_top_level()
3648 {
3649         return top_level;
3650 }
3651
3652 BC_WindowBase* BC_WindowBase::get_parent()
3653 {
3654         return parent_window;
3655 }
3656
3657 int BC_WindowBase::get_color_model()
3658 {
3659         return top_level->color_model;
3660 }
3661
3662 BC_Resources* BC_WindowBase::get_resources()
3663 {
3664         return BC_WindowBase::resources;
3665 }
3666
3667 BC_Synchronous* BC_WindowBase::get_synchronous()
3668 {
3669         return BC_WindowBase::resources->get_synchronous();
3670 }
3671
3672 int BC_WindowBase::get_bg_color()
3673 {
3674         return bg_color;
3675 }
3676
3677 void BC_WindowBase::set_bg_color(int color)
3678 {
3679         this->bg_color = color;
3680 }
3681
3682 BC_Pixmap* BC_WindowBase::get_bg_pixmap()
3683 {
3684         return bg_pixmap;
3685 }
3686
3687 void BC_WindowBase::set_active_subwindow(BC_WindowBase *subwindow)
3688 {
3689         top_level->active_subwindow = subwindow;
3690 }
3691
3692 int BC_WindowBase::activate()
3693 {
3694         return 0;
3695 }
3696
3697 int BC_WindowBase::deactivate()
3698 {
3699         if(window_type == MAIN_WINDOW)
3700         {
3701                 if( top_level->active_menubar ) {
3702                         top_level->active_menubar->deactivate();
3703                         top_level->active_menubar = 0;
3704                 }
3705                 if( top_level->active_popup_menu ) {
3706                         top_level->active_popup_menu->deactivate();
3707                         top_level->active_popup_menu = 0;
3708                 }
3709                 if( top_level->active_subwindow ) {
3710                         top_level->active_subwindow->deactivate();
3711                         top_level->active_subwindow = 0;
3712                 }
3713                 if( top_level->motion_events && top_level->last_motion_win == this->win )
3714                         top_level->motion_events = 0;
3715
3716         }
3717         return 0;
3718 }
3719
3720 int BC_WindowBase::cycle_textboxes(int amount)
3721 {
3722         int result = 0;
3723         BC_WindowBase *new_textbox = 0;
3724
3725         if(amount > 0)
3726         {
3727                 BC_WindowBase *first_textbox = 0;
3728                 find_next_textbox(&first_textbox, &new_textbox, result);
3729                 if(!new_textbox) new_textbox = first_textbox;
3730
3731         }
3732         else
3733         if(amount < 0)
3734         {
3735                 BC_WindowBase *last_textbox = 0;
3736                 find_prev_textbox(&last_textbox, &new_textbox, result);
3737                 if(!new_textbox) new_textbox = last_textbox;
3738
3739         }
3740
3741         if(new_textbox != active_subwindow)
3742         {
3743                 deactivate();
3744                 new_textbox->activate();
3745         }
3746
3747         return 0;
3748 }
3749
3750 int BC_WindowBase::find_next_textbox(BC_WindowBase **first_textbox, BC_WindowBase **next_textbox, int &result)
3751 {
3752 // Search subwindows for textbox
3753         for(int i = 0; i < subwindows->total && result < 2; i++)
3754         {
3755                 BC_WindowBase *test_subwindow = subwindows->values[i];
3756                 test_subwindow->find_next_textbox(first_textbox, next_textbox, result);
3757         }
3758
3759         if(result < 2)
3760         {
3761                 if(uses_text())
3762                 {
3763                         if(!*first_textbox) *first_textbox = this;
3764
3765                         if(result < 1)
3766                         {
3767                                 if(top_level->active_subwindow == this)
3768                                         result++;
3769                         }
3770                         else
3771                         {
3772                                 result++;
3773                                 *next_textbox = this;
3774                         }
3775                 }
3776         }
3777         return 0;
3778 }
3779
3780 int BC_WindowBase::find_prev_textbox(BC_WindowBase **last_textbox, BC_WindowBase **prev_textbox, int &result)
3781 {
3782         if(result < 2)
3783         {
3784                 if(uses_text())
3785                 {
3786                         if(!*last_textbox) *last_textbox = this;
3787
3788                         if(result < 1)
3789                         {
3790                                 if(top_level->active_subwindow == this)
3791                                         result++;
3792                         }
3793                         else
3794                         {
3795                                 result++;
3796                                 *prev_textbox = this;
3797                         }
3798                 }
3799         }
3800
3801 // Search subwindows for textbox
3802         for(int i = subwindows->total - 1; i >= 0 && result < 2; i--)
3803         {
3804                 BC_WindowBase *test_subwindow = subwindows->values[i];
3805                 test_subwindow->find_prev_textbox(last_textbox, prev_textbox, result);
3806         }
3807         return 0;
3808 }
3809
3810 BC_Clipboard* BC_WindowBase::get_clipboard()
3811 {
3812 #ifdef SINGLE_THREAD
3813         return BC_Display::display_global->clipboard;
3814 #else
3815         return top_level->clipboard;
3816 #endif
3817 }
3818
3819 Atom BC_WindowBase::to_clipboard(const char *data, long len, int clipboard_num)
3820 {
3821         return get_clipboard()->to_clipboard(this, data, len, clipboard_num);
3822 }
3823
3824 long BC_WindowBase::from_clipboard(char *data, long maxlen, int clipboard_num)
3825 {
3826         return get_clipboard()->from_clipboard(data, maxlen, clipboard_num);
3827 }
3828
3829 long BC_WindowBase::clipboard_len(int clipboard_num)
3830 {
3831         return get_clipboard()->clipboard_len(clipboard_num);
3832 }
3833
3834 int BC_WindowBase::do_selection_clear(Window win)
3835 {
3836         top_level->event_win = win;
3837         return dispatch_selection_clear();
3838 }
3839
3840 int BC_WindowBase::dispatch_selection_clear()
3841 {
3842         int result = 0;
3843         for( int i=0; i<subwindows->total && !result; ++i )
3844                 result = subwindows->values[i]->dispatch_selection_clear();
3845         if( !result )
3846                 result = selection_clear_event();
3847         return result;
3848 }
3849
3850
3851 void BC_WindowBase::get_relative_cursor(int &x, int &y, int lock_window)
3852 {
3853         int abs_x, abs_y, win_x, win_y;
3854         unsigned int temp_mask;
3855         Window temp_win;
3856
3857         if(lock_window) this->lock_window("BC_WindowBase::get_relative_cursor");
3858         XQueryPointer(top_level->display, top_level->win,
3859            &temp_win, &temp_win, &abs_x, &abs_y, &win_x, &win_y,
3860            &temp_mask);
3861
3862         XTranslateCoordinates(top_level->display, top_level->rootwin,
3863            win, abs_x, abs_y, &x, &y, &temp_win);
3864         if(lock_window) this->unlock_window();
3865 }
3866 int BC_WindowBase::get_relative_cursor_x(int lock_window)
3867 {
3868         int x, y;
3869         get_relative_cursor(x, y, lock_window);
3870         return x;
3871 }
3872 int BC_WindowBase::get_relative_cursor_y(int lock_window)
3873 {
3874         int x, y;
3875         get_relative_cursor(x, y, lock_window);
3876         return y;
3877 }
3878
3879 void BC_WindowBase::get_abs_cursor(int &abs_x, int &abs_y, int lock_window)
3880 {
3881         int win_x, win_y;
3882         unsigned int temp_mask;
3883         Window temp_win;
3884
3885         if(lock_window) this->lock_window("BC_WindowBase::get_abs_cursor");
3886         XQueryPointer(top_level->display, top_level->win,
3887                 &temp_win, &temp_win, &abs_x, &abs_y, &win_x, &win_y,
3888                 &temp_mask);
3889         if(lock_window) this->unlock_window();
3890 }
3891 int BC_WindowBase::get_abs_cursor_x(int lock_window)
3892 {
3893         int abs_x, abs_y;
3894         get_abs_cursor(abs_x, abs_y, lock_window);
3895         return abs_x;
3896 }
3897 int BC_WindowBase::get_abs_cursor_y(int lock_window)
3898 {
3899         int abs_x, abs_y;
3900         get_abs_cursor(abs_x, abs_y, lock_window);
3901         return abs_y;
3902 }
3903
3904 void BC_WindowBase::get_pop_cursor(int &px, int &py, int lock_window)
3905 {
3906         int xmargin = xS(100), ymargin = yS(100);
3907         get_abs_cursor(px, py, lock_window);
3908         if( px < xmargin ) px = xmargin;
3909         if( py < ymargin ) py = ymargin;
3910         int wd = get_screen_x(lock_window,-1) + get_screen_w(lock_window,-1) - xmargin;
3911         if( px > wd ) px = wd;
3912         int ht = get_screen_y(lock_window,-1) + get_screen_h(lock_window,-1) - ymargin;
3913         if( py > ht ) py = ht;
3914 }
3915 int BC_WindowBase::get_pop_cursor_x(int lock_window)
3916 {
3917         int px, py;
3918         get_pop_cursor(px, py, lock_window);
3919         return px;
3920 }
3921 int BC_WindowBase::get_pop_cursor_y(int lock_window)
3922 {
3923         int px, py;
3924         get_pop_cursor(px, py, lock_window);
3925         return py;
3926 }
3927
3928 int BC_WindowBase::match_window(Window win)
3929 {
3930         if (this->win == win) return 1;
3931         int result = 0;
3932         for(int i = 0; i < subwindows->total; i++) {
3933                 result = subwindows->values[i]->match_window(win);
3934                 if (result) return result;
3935         }
3936         return 0;
3937
3938 }
3939
3940 int BC_WindowBase::get_cursor_over_window()
3941 {
3942         int abs_x, abs_y, win_x, win_y;
3943         unsigned int mask_return;
3944         Window root_return, child_return;
3945
3946         int ret = XQueryPointer(top_level->display, top_level->rootwin,
3947                 &root_return, &child_return, &abs_x, &abs_y,
3948                 &win_x, &win_y, &mask_return);
3949         if( ret && child_return == None ) ret = 0;
3950         if( ret && win != child_return )
3951                 ret = top_level->match_window(child_return);
3952 // query pointer can return a window manager window with this top_level as a child
3953 //  for kde this can be two levels deep
3954         unsigned int nchildren_return = 0;
3955         Window parent_return, *children_return = 0;
3956         Window top_win = top_level->win;
3957         while( !ret && top_win != top_level->rootwin && top_win != root_return &&
3958                 XQueryTree(top_level->display, top_win, &root_return,
3959                         &parent_return, &children_return, &nchildren_return) ) {
3960                 if( children_return ) XFree(children_return);
3961                 if( (top_win=parent_return) == child_return ) ret = 1;
3962         }
3963         return ret;
3964 }
3965
3966 int BC_WindowBase::cursor_above()
3967 {
3968         int rx, ry;
3969         get_relative_cursor(rx, ry);
3970         return rx < 0 || rx >= get_w() ||
3971                 ry < 0 || ry >= get_h() ? 0 : 1;
3972 }
3973
3974 int BC_WindowBase::get_drag_x()
3975 {
3976         return top_level->drag_x;
3977 }
3978
3979 int BC_WindowBase::get_drag_y()
3980 {
3981         return top_level->drag_y;
3982 }
3983
3984 int BC_WindowBase::get_cursor_x()
3985 {
3986         return top_level->cursor_x;
3987 }
3988
3989 int BC_WindowBase::get_cursor_y()
3990 {
3991         return top_level->cursor_y;
3992 }
3993
3994 int BC_WindowBase::dump_windows()
3995 {
3996         printf("\tBC_WindowBase::dump_windows window=%p win=%p '%s', %dx%d+%d+%d %s\n",
3997                 this, (void*)this->win, title, w,h,x,y, typeid(*this).name());
3998         for(int i = 0; i < subwindows->size(); i++)
3999                 subwindows->get(i)->dump_windows();
4000         for(int i = 0; i < popups.size(); i++) {
4001                 BC_WindowBase *p = popups[i];
4002                 printf("\tBC_WindowBase::dump_windows popup=%p win=%p '%s', %dx%d+%d+%d %s\n",
4003                         p, (void*)p->win, p->title, p->w,p->h,p->x,p->y, typeid(*p).name());
4004         }
4005         return 0;
4006 }
4007
4008 int BC_WindowBase::is_event_win()
4009 {
4010         return this->win == top_level->event_win;
4011 }
4012
4013 void BC_WindowBase::set_dragging(int value)
4014 {
4015         is_dragging = value;
4016 }
4017
4018 int BC_WindowBase::get_dragging()
4019 {
4020         return is_dragging;
4021 }
4022
4023 int BC_WindowBase::get_buttonpress()
4024 {
4025         return top_level->button_number;
4026 }
4027
4028 int BC_WindowBase::get_button_down()
4029 {
4030         return top_level->button_down;
4031 }
4032
4033 int BC_WindowBase::alt_down()
4034 {
4035         return top_level->alt_mask;
4036 }
4037
4038 int BC_WindowBase::shift_down()
4039 {
4040         return top_level->shift_mask;
4041 }
4042
4043 int BC_WindowBase::ctrl_down()
4044 {
4045         return top_level->ctrl_mask;
4046 }
4047
4048 wchr_t* BC_WindowBase::get_wkeystring(int *length)
4049 {
4050         if(length)
4051                 *length = top_level->wkey_string_length;
4052         return top_level->wkey_string;
4053 }
4054
4055 #ifdef X_HAVE_UTF8_STRING
4056 char* BC_WindowBase::get_keypress_utf8()
4057 {
4058         return top_level->key_pressed_utf8;
4059 }
4060 #endif
4061
4062
4063 int BC_WindowBase::get_keypress()
4064 {
4065         return top_level->key_pressed;
4066 }
4067
4068 int BC_WindowBase::get_double_click()
4069 {
4070         return top_level->double_click;
4071 }
4072
4073 int BC_WindowBase::get_triple_click()
4074 {
4075         return top_level->triple_click;
4076 }
4077
4078 int BC_WindowBase::get_bgcolor()
4079 {
4080         return bg_color;
4081 }
4082
4083 int BC_WindowBase::resize_window(int w, int h)
4084 {
4085         if(this->w == w && this->h == h) return 0;
4086
4087         if(window_type == MAIN_WINDOW && !allow_resize)
4088         {
4089                 XSizeHints size_hints;
4090                 size_hints.flags = PSize | PMinSize | PMaxSize;
4091                 size_hints.width = w;
4092                 size_hints.height = h;
4093                 size_hints.min_width = w;
4094                 size_hints.max_width = w;
4095                 size_hints.min_height = h;
4096                 size_hints.max_height = h;
4097                 if( this->x > -BC_INFINITY && this->x < BC_INFINITY ) {
4098                         size_hints.flags |= PPosition;
4099                         size_hints.x = this->x;
4100                         size_hints.y = this->y;
4101                 }
4102                 XSetNormalHints(top_level->display, win, &size_hints);
4103         }
4104         XResizeWindow(top_level->display, win, w, h);
4105
4106         this->w = w;
4107         this->h = h;
4108         delete pixmap;
4109         pixmap = new BC_Pixmap(this, w, h);
4110
4111 // Propagate to menubar
4112         for(int i = 0; i < subwindows->total; i++)
4113         {
4114                 subwindows->values[i]->dispatch_resize_event(w, h);
4115         }
4116
4117         draw_background(0, 0, w, h);
4118         if(top_level == this && get_resources()->recursive_resizing)
4119                 resize_history.append(new BC_ResizeCall(w, h));
4120         return 0;
4121 }
4122
4123 // The only way for resize events to be propagated is by updating the internal w and h
4124 int BC_WindowBase::resize_event(int w, int h)
4125 {
4126         if(window_type == MAIN_WINDOW)
4127         {
4128                 this->w = w;
4129                 this->h = h;
4130         }
4131         return 0;
4132 }
4133
4134 int BC_WindowBase::reposition_window(int x, int y)
4135 {
4136         reposition_window(x, y, -1, -1);
4137         return 0;
4138 }
4139
4140
4141 int BC_WindowBase::reposition_window(int x, int y, int w, int h)
4142 {
4143         int resize = 0;
4144
4145 // Some tools set their own dimensions before calling this, causing the
4146 // resize check to skip.
4147         this->x = x;
4148         this->y = y;
4149
4150         if(w > 0 && w != this->w)
4151         {
4152                 resize = 1;
4153                 this->w = w;
4154         }
4155
4156         if(h > 0 && h != this->h)
4157         {
4158                 resize = 1;
4159                 this->h = h;
4160         }
4161
4162 //printf("BC_WindowBase::reposition_window %d %d %d\n", translation_count, x_correction, y_correction);
4163
4164         if(this->w <= 0)
4165                 printf("BC_WindowBase::reposition_window this->w == %d\n", this->w);
4166         if(this->h <= 0)
4167                 printf("BC_WindowBase::reposition_window this->h == %d\n", this->h);
4168
4169         if(translation_count && window_type == MAIN_WINDOW)
4170         {
4171 // KDE shifts window right and down.
4172 // FVWM leaves window alone and adds border around it.
4173                 XMoveResizeWindow(top_level->display, win,
4174                         x - BC_DisplayInfo::auto_reposition_x,
4175                         y - BC_DisplayInfo::auto_reposition_y,
4176                         this->w, this->h);
4177         }
4178         else
4179         {
4180                 XMoveResizeWindow(top_level->display, win, x, y,
4181                         this->w, this->h);
4182         }
4183
4184         if(resize)
4185         {
4186                 delete pixmap;
4187                 pixmap = new BC_Pixmap(this, this->w, this->h);
4188                 clear_box(0,0, this->w, this->h);
4189 // Propagate to menubar
4190                 for(int i = 0; i < subwindows->total; i++)
4191                 {
4192                         subwindows->values[i]->dispatch_resize_event(this->w, this->h);
4193                 }
4194
4195 //              draw_background(0, 0, w, h);
4196         }
4197
4198         return 0;
4199 }
4200
4201 int BC_WindowBase::reposition_window_relative(int dx, int dy, int w, int h)
4202 {
4203         return reposition_window(get_x()+dx, get_y()+dy, w, h);
4204 }
4205
4206 int BC_WindowBase::reposition_window_relative(int dx, int dy)
4207 {
4208         return reposition_window_relative(dx, dy, -1, -1);
4209 }
4210
4211 void BC_WindowBase::set_tooltips(int v)
4212 {
4213         get_resources()->tooltips_enabled = v;
4214 }
4215
4216 void BC_WindowBase::set_force_tooltip(int v)
4217 {
4218         force_tooltip = v;
4219 }
4220
4221 int BC_WindowBase::raise_window(int do_flush)
4222 {
4223         if( hidden ) return 1;
4224         if( wait_viewable(500) ) return 1;
4225         XRaiseWindow(top_level->display, win);
4226         if(do_flush) XFlush(top_level->display);
4227         return 0;
4228 }
4229
4230 int BC_WindowBase::lower_window(int do_flush)
4231 {
4232         XLowerWindow(top_level->display, win);
4233         if(do_flush) XFlush(top_level->display);
4234         return 0;
4235 }
4236
4237 void BC_WindowBase::set_background(VFrame *bitmap)
4238 {
4239         if(bg_pixmap && !shared_bg_pixmap) delete bg_pixmap;
4240
4241         bg_pixmap = new BC_Pixmap(this, bitmap, PIXMAP_OPAQUE);
4242         shared_bg_pixmap = 0;
4243         draw_background(0, 0, w, h);
4244 }
4245
4246 void BC_WindowBase::put_title(const char *text)
4247 {
4248         char *cp = this->title, *ep = cp+sizeof(this->title)-1;
4249         for( const unsigned char *bp = (const unsigned char *)text; *bp && cp<ep; ++bp )
4250                 *cp++ = *bp >= ' ' ? *bp : ' ';
4251         *cp = 0;
4252 }
4253
4254 void BC_WindowBase::set_title(const char *text, int utf8)
4255 {
4256 // utf8>0: wm + net_wm, utf8=0: wm only,  utf<0: net_wm only
4257         put_title(text);
4258         const unsigned char *wm_title = (const unsigned char *)title;
4259         int title_len = strlen((const char *)title);
4260         if( utf8 >= 0 ) {
4261                 Atom xa_wm_name = XA_WM_NAME;
4262                 Atom xa_icon_name = XA_WM_ICON_NAME;
4263                 Atom xa_string = XA_STRING;
4264                 XChangeProperty(display, win, xa_wm_name, xa_string, 8,
4265                                 PropModeReplace, wm_title, title_len);
4266                 XChangeProperty(display, win, xa_icon_name, xa_string, 8,
4267                                 PropModeReplace, wm_title, title_len);
4268         }
4269         if( utf8 != 0 ) {
4270                 Atom xa_net_wm_name = XInternAtom(display, "_NET_WM_NAME", True);
4271                 Atom xa_net_icon_name = XInternAtom(display, "_NET_WM_ICON_NAME", True);
4272                 Atom xa_utf8_string = XInternAtom(display, "UTF8_STRING", True);
4273                 XChangeProperty(display, win, xa_net_wm_name, xa_utf8_string, 8,
4274                                         PropModeReplace, wm_title, title_len);
4275                 XChangeProperty(display, win, xa_net_icon_name, xa_utf8_string, 8,
4276                                         PropModeReplace, wm_title, title_len);
4277         }
4278         flush();
4279 }
4280
4281 // must be RGBA8888
4282 void BC_WindowBase::set_net_icon(VFrame *data)
4283 {
4284         int width = data->get_w(), height = data->get_h();
4285         int size = 2 + width * height;
4286         unsigned long *icon_data = new unsigned long[size];
4287         unsigned long *lp = icon_data;
4288         *lp++ = width;  *lp++ = height;
4289         uint8_t **rows = data->get_rows();
4290         for( int y=0; y<height; ++y ) {
4291                 unsigned *up = (unsigned *)rows[y];
4292                 for( int x=0; x<width; ++x )
4293                         *lp++ = *(unsigned *)up++;
4294         }
4295         Atom NetWMIcon = XInternAtom(display, "_NET_WM_ICON", True);
4296         XChangeProperty(top_level->display, top_level->win, NetWMIcon,
4297                 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)icon_data, size);
4298         delete [] icon_data;
4299 }
4300
4301 const char *BC_WindowBase::get_title()
4302 {
4303         return title;
4304 }
4305
4306 int BC_WindowBase::get_toggle_value()
4307 {
4308         return toggle_value;
4309 }
4310
4311 int BC_WindowBase::get_toggle_drag()
4312 {
4313         return toggle_drag;
4314 }
4315
4316 int BC_WindowBase::set_icon(VFrame *data)
4317 {
4318         if(icon_pixmap) delete icon_pixmap;
4319         icon_pixmap = new BC_Pixmap(top_level, data, PIXMAP_ALPHA, 1);
4320
4321         if(icon_window) delete icon_window;
4322         icon_window = new BC_Popup(this, 0, 0,
4323                 icon_pixmap->get_w(), icon_pixmap->get_h(),
4324                 -1, 1, // All windows are hidden initially
4325                 icon_pixmap);
4326
4327         XWMHints wm_hints;  memset(&wm_hints, 0, sizeof(wm_hints));
4328         wm_hints.flags = IconPixmapHint; // | IconMaskHint | IconWindowHint;
4329         wm_hints.icon_pixmap = icon_pixmap->get_pixmap();
4330         wm_hints.icon_mask = icon_pixmap->get_alpha();
4331         wm_hints.icon_window = icon_window->win;
4332         if( XGroupLeader ) {
4333                 wm_hints.flags |= WindowGroupHint;
4334                 wm_hints.window_group = XGroupLeader;
4335         }
4336
4337 // for(int i = 0; i < 1000; i++)
4338 // printf("02x ", icon_pixmap->get_alpha()->get_row_pointers()[0][i]);
4339 // printf("\n");
4340
4341         XSetWMHints(top_level->display, top_level->win, &wm_hints);
4342
4343         set_net_icon(data);
4344         XSync(top_level->display, 0);
4345         return 0;
4346 }
4347
4348 void BC_WindowBase::init_resources(float scale)
4349 {
4350         if( resources ) return;
4351         XInitThreads();
4352         const char *env = getenv("BC_SCALE");
4353         if( env ) scale = atof(env);
4354         float x_scale = 1, y_scale = 1;
4355         if( scale <= 0 ) {
4356                 BC_DisplayInfo info;
4357                 int wx, wy, ww, wh;
4358                 int cins = info.xinerama_big_screen();
4359                 if( !info.xinerama_geometry(cins, wx, wy, ww, wh) ) {
4360                         int sh = ww * 9 / 16;
4361                         int sw = wh * 16 / 9;
4362                         if( sw < ww ) ww = sw;
4363                         if( sh < wh ) wh = sh;
4364                         if( (x_scale = ww/1920.) < 1 ) x_scale = 1;
4365                         if( (y_scale = wh/1080.) < 1 ) y_scale = 1;
4366                 }
4367         }
4368         else
4369                 x_scale = y_scale = scale;
4370         // constructor sets BC_WindowBase::resources
4371         new BC_Resources(x_scale, y_scale);
4372 }
4373 void BC_WindowBase::finit_resources()
4374 {
4375         delete resources;  resources = 0;
4376 }
4377
4378 int BC_WindowBase::set_w(int w)
4379 {
4380         this->w = w;
4381         return 0;
4382 }
4383
4384 int BC_WindowBase::set_h(int h)
4385 {
4386         this->h = h;
4387         return 0;
4388 }
4389
4390 int BC_WindowBase::load_defaults(BC_Hash *defaults)
4391 {
4392         char string[BCTEXTLEN];
4393         int newest_id = - 1;
4394         for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++)
4395         {
4396                 sprintf(string, "FILEBOX_HISTORY_PATH%d", i);
4397                 resources->filebox_history[i].path[0] = 0;
4398                 defaults->get(string, resources->filebox_history[i].path);
4399                 sprintf(string, "FILEBOX_HISTORY_ID%d", i);
4400                 resources->filebox_history[i].id = defaults->get(string, resources->get_id());
4401                 if(resources->filebox_history[i].id > newest_id)
4402                         newest_id = resources->filebox_history[i].id;
4403         }
4404
4405         resources->filebox_id = newest_id + 1;
4406         resources->filebox_mode = defaults->get("FILEBOX_MODE", get_resources()->filebox_mode);
4407         resources->filebox_w = defaults->get("FILEBOX_W", get_resources()->filebox_w);
4408         resources->filebox_h = defaults->get("FILEBOX_H", get_resources()->filebox_h);
4409         resources->filebox_columntype[0] = defaults->get("FILEBOX_TYPE0", resources->filebox_columntype[0]);
4410         resources->filebox_columntype[1] = defaults->get("FILEBOX_TYPE1", resources->filebox_columntype[1]);
4411         resources->filebox_columntype[2] = defaults->get("FILEBOX_TYPE2", resources->filebox_columntype[2]);
4412         resources->filebox_columntype[3] = defaults->get("FILEBOX_TYPE3", resources->filebox_columntype[3]);
4413         resources->filebox_columnwidth[0] = defaults->get("FILEBOX_WIDTH0", resources->filebox_columnwidth[0]);
4414         resources->filebox_columnwidth[1] = defaults->get("FILEBOX_WIDTH1", resources->filebox_columnwidth[1]);
4415         resources->filebox_columnwidth[2] = defaults->get("FILEBOX_WIDTH2", resources->filebox_columnwidth[2]);
4416         resources->filebox_columnwidth[3] = defaults->get("FILEBOX_WIDTH3", resources->filebox_columnwidth[3]);
4417         resources->filebox_size_format = defaults->get("FILEBOX_SIZE_FORMAT", get_resources()->filebox_size_format);
4418         defaults->get("FILEBOX_FILTER", resources->filebox_filter);
4419         return 0;
4420 }
4421
4422 int BC_WindowBase::save_defaults(BC_Hash *defaults)
4423 {
4424         char string[BCTEXTLEN];
4425         for(int i = 0; i < FILEBOX_HISTORY_SIZE; i++)
4426         {
4427                 sprintf(string, "FILEBOX_HISTORY_PATH%d", i);
4428                 defaults->update(string, resources->filebox_history[i].path);
4429                 sprintf(string, "FILEBOX_HISTORY_ID%d", i);
4430                 defaults->update(string, resources->filebox_history[i].id);
4431         }
4432         defaults->update("FILEBOX_MODE", resources->filebox_mode);
4433         defaults->update("FILEBOX_W", resources->filebox_w);
4434         defaults->update("FILEBOX_H", resources->filebox_h);
4435         defaults->update("FILEBOX_TYPE0", resources->filebox_columntype[0]);
4436         defaults->update("FILEBOX_TYPE1", resources->filebox_columntype[1]);
4437         defaults->update("FILEBOX_TYPE2", resources->filebox_columntype[2]);
4438         defaults->update("FILEBOX_TYPE3", resources->filebox_columntype[3]);
4439         defaults->update("FILEBOX_WIDTH0", resources->filebox_columnwidth[0]);
4440         defaults->update("FILEBOX_WIDTH1", resources->filebox_columnwidth[1]);
4441         defaults->update("FILEBOX_WIDTH2", resources->filebox_columnwidth[2]);
4442         defaults->update("FILEBOX_WIDTH3", resources->filebox_columnwidth[3]);
4443         defaults->update("FILEBOX_FILTER", resources->filebox_filter);
4444         defaults->update("FILEBOX_SIZE_FORMAT", get_resources()->filebox_size_format);
4445         return 0;
4446 }
4447
4448
4449
4450 // For some reason XTranslateCoordinates can take a long time to return.
4451 // We work around this by only calling it when the event windows are different.
4452 void BC_WindowBase::translate_coordinates(Window src_w, Window dest_w,
4453                 int src_x, int src_y, int *dest_x_return, int *dest_y_return)
4454 {
4455         Window tempwin = 0;
4456 //Timer timer;
4457 //timer.update();
4458         if(src_w == dest_w)
4459         {
4460                 *dest_x_return = src_x;
4461                 *dest_y_return = src_y;
4462         }
4463         else
4464         {
4465                 XTranslateCoordinates(top_level->display, src_w, dest_w,
4466                         src_x, src_y, dest_x_return, dest_y_return, &tempwin);
4467 //printf("BC_WindowBase::translate_coordinates 1 %lld\n", timer.get_difference());
4468         }
4469 }
4470
4471 void BC_WindowBase::get_root_coordinates(int x, int y, int *abs_x, int *abs_y)
4472 {
4473         translate_coordinates(win, top_level->rootwin, x, y, abs_x, abs_y);
4474 }
4475
4476 void BC_WindowBase::get_win_coordinates(int abs_x, int abs_y, int *x, int *y)
4477 {
4478         translate_coordinates(top_level->rootwin, win, abs_x, abs_y, x, y);
4479 }
4480
4481
4482 #ifdef HAVE_LIBXXF86VM
4483 void BC_WindowBase::closest_vm(int *vm, int *width, int *height)
4484 {
4485         int foo,bar;
4486         *vm = 0;
4487         if(XF86VidModeQueryExtension(top_level->display,&foo,&bar)) {
4488                 int vm_count,i;
4489                 XF86VidModeModeInfo **vm_modelines;
4490                 XF86VidModeGetAllModeLines(top_level->display,
4491                         XDefaultScreen(top_level->display), &vm_count,&vm_modelines);
4492                 for( i = 0; i < vm_count; i++ ) {
4493                         if( vm_modelines[i]->hdisplay < vm_modelines[*vm]->hdisplay &&
4494                             vm_modelines[i]->hdisplay >= *width )
4495                                 *vm = i;
4496                 }
4497                 display = top_level->display;
4498                 if( vm_modelines[*vm]->hdisplay == *width )
4499                         *vm = -1;
4500                 else {
4501                         *width = vm_modelines[*vm]->hdisplay;
4502                         *height = vm_modelines[*vm]->vdisplay;
4503                 }
4504         }
4505 }
4506
4507 void BC_WindowBase::scale_vm(int vm)
4508 {
4509         int foo,bar,dotclock;
4510         if( XF86VidModeQueryExtension(top_level->display,&foo,&bar) ) {
4511                 int vm_count;
4512                 XF86VidModeModeInfo **vm_modelines;
4513                 XF86VidModeModeLine vml;
4514                 XF86VidModeGetAllModeLines(top_level->display,
4515                         XDefaultScreen(top_level->display), &vm_count,&vm_modelines);
4516                 XF86VidModeGetModeLine(top_level->display,
4517                         XDefaultScreen(top_level->display), &dotclock,&vml);
4518                 orig_modeline.dotclock = dotclock;
4519                 orig_modeline.hdisplay = vml.hdisplay;
4520                 orig_modeline.hsyncstart = vml.hsyncstart;
4521                 orig_modeline.hsyncend = vml.hsyncend;
4522                 orig_modeline.htotal = vml.htotal;
4523                 orig_modeline.vdisplay = vml.vdisplay;
4524                 orig_modeline.vsyncstart = vml.vsyncstart;
4525                 orig_modeline.vsyncend = vml.vsyncend;
4526                 orig_modeline.vtotal = vml.vtotal;
4527                 orig_modeline.flags = vml.flags;
4528                 orig_modeline.privsize = vml.privsize;
4529                 // orig_modeline.private = vml.private;
4530                 XF86VidModeSwitchToMode(top_level->display,XDefaultScreen(top_level->display),vm_modelines[vm]);
4531                 XF86VidModeSetViewPort(top_level->display,XDefaultScreen(top_level->display),0,0);
4532                 XFlush(top_level->display);
4533         }
4534 }
4535
4536 void BC_WindowBase::restore_vm()
4537 {
4538         XF86VidModeSwitchToMode(top_level->display,XDefaultScreen(top_level->display),&orig_modeline);
4539         XFlush(top_level->display);
4540 }
4541 #endif
4542
4543
4544 #ifndef SINGLE_THREAD
4545 int BC_WindowBase::get_event_count()
4546 {
4547         event_lock->lock("BC_WindowBase::get_event_count");
4548         int result = common_events.total;
4549         event_lock->unlock();
4550         return result;
4551 }
4552
4553 XEvent* BC_WindowBase::get_event()
4554 {
4555         XEvent *result = 0;
4556         while(!done && !result)
4557         {
4558                 event_condition->lock("BC_WindowBase::get_event");
4559                 event_lock->lock("BC_WindowBase::get_event");
4560
4561                 if(common_events.total && !done)
4562                 {
4563                         result = common_events.values[0];
4564                         common_events.remove_number(0);
4565                 }
4566
4567                 event_lock->unlock();
4568         }
4569         return result;
4570 }
4571
4572 void BC_WindowBase::put_event(XEvent *event)
4573 {
4574         event_lock->lock("BC_WindowBase::put_event");
4575         common_events.append(event);
4576         event_lock->unlock();
4577         event_condition->unlock();
4578 }
4579
4580 void BC_WindowBase::dequeue_events(Window win)
4581 {
4582         event_lock->lock("BC_WindowBase::dequeue_events");
4583
4584         int out = 0, total = common_events.size();
4585         for( int in=0; in<total; ++in ) {
4586                 if( common_events[in]->xany.window == win ) continue;
4587                 common_events[out++] = common_events[in];
4588         }
4589         common_events.total = out;
4590
4591         event_lock->unlock();
4592 }
4593
4594 int BC_WindowBase::resend_event(BC_WindowBase *window)
4595 {
4596         if( resend_event_window ) return 1;
4597         resend_event_window = window;
4598         return 0;
4599 }
4600
4601 #else
4602
4603 int BC_WindowBase::resend_event(BC_WindowBase *window)
4604 {
4605         return 1;
4606 }
4607
4608 #endif // SINGLE_THREAD
4609
4610 int BC_WindowBase::get_id()
4611 {
4612         return id;
4613 }
4614
4615
4616 BC_Pixmap *BC_WindowBase::create_pixmap(VFrame *vframe)
4617 {
4618         int w = vframe->get_w(), h = vframe->get_h();
4619         BC_Pixmap *icon = new BC_Pixmap(this, w, h);
4620         icon->draw_vframe(vframe, 0,0, w,h, 0,0);
4621         return icon;
4622 }
4623
4624
4625 void BC_WindowBase::flicker(int n, int ms)
4626 {
4627         int color = get_bg_color();
4628         for( int i=2*n; --i>=0; ) {
4629                 set_inverse();          set_bg_color(WHITE);
4630                 clear_box(0,0, w,h);    flash(1);
4631                 sync_display();         Timer::delay(ms);
4632         }
4633         set_bg_color(color);
4634         set_opaque();
4635 }
4636
4637 void BC_WindowBase::focus()
4638 {
4639         XWindowAttributes xwa;
4640         XGetWindowAttributes(top_level->display, top_level->win, &xwa);
4641         if( xwa.map_state == IsViewable )
4642                 XSetInputFocus(top_level->display, top_level->win, RevertToParent, CurrentTime);
4643 }
4644
4645 int BC_WindowBase::wait_viewable(int ms)
4646 {
4647         Timer timer;
4648         XWindowAttributes xwa;
4649         do {
4650                 XGetWindowAttributes(top_level->display, top_level->win, &xwa);
4651                 if( xwa.map_state == IsViewable ) return 0;
4652                 usleep(10000);
4653         } while( timer.get_difference() < ms );
4654         return 1;
4655 }
4656