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