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