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