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